other
(in-package "ACL2")
include-book
(include-book "xdoc/top" :dir :system)
include-book
(include-book "std/util/bstar" :dir :system)
other
(set-state-ok t)
other
(program)
other
(defxdoc add-io-pair :parents (add-io-pairs) :short "Speed up a function using a verified input-output pair" :long "<p>@('Add-io-pair') is just a convenient abbreviation for @(tsee add-io-pairs) in the case of a single input-output pair. See @(see add-io-pairs).</p> @({ Examples: (add-io-pair (f 3) '(3 . 3)) (add-io-pair (g 3 4) (mv 30 40)) (add-io-pair (primep (primes::bn-254-group-prime)) t :test eql :hints (("Goal" :in-theory (enable primes::primep-of-bn-254-group-prime-constant)))) General Form: (add-io-pair fn input output &key hints test debug verbose) }) <p>where @('fn') is a @(see guard)-verified function symbol whose arguments do not include @(tsee state) or any user-defined @(tsee stobj). The arguments to the macro are not evaluated.</p> <p>@('Add-io-pair') is merely a convenient way to invoke the utility @(see add-io-pairs) when there is a single input/output pair; see @(see add-io-pairs) for further documentation, including discussion of the keywords (which are optional).</p>")
other
(defxdoc add-io-pairs :parents (std/util) :short "Speed up a function using verified input-output pairs" :long "<p>For examples, see the book @('std/util/add-io-pairs-tests.lisp') in @(see community-books). Also see @(see add-io-pair) for an equivalent utility with slightly simpler syntax that can add a single input-output pair.</p> <p><b>Summary</b>. This utility provides a way to redefine a function so that it can quickly look up a function call @('(fn i1 ... ik)') to produce its output, @('val'), thus avoiding its usual computation. We call such a pair @('((fn i1 ... ik) val)') an ``I/O pair'' (for @('fn')). Each I/O pair is ``verified'': a proof obligation has been met showing that the input list is indeed mapped to the corresponding output. The (verified) I/O pairs are stored efficiently in a @(see table). See @(tsee show-io-pairs) for how to print the current I/O pairs. The present utility, @('add-io-pairs'), extends the table by adding the specified I/O pairs and also redefines the specified function to take advantage of the updated table.</p> @({ Examples (see std/util/add-io-pairs-tests.lisp): (add-io-pairs (((f 3) '(3 . 3)))) (add-io-pairs (((g 3 6) (mv (* 3 10) (* 6 10))) ((g (/ 40 10) (/ 50 10)) (mv 40 50)))) (add-io-pairs (((primep (primes::secp256k1-field-prime)) t) ((primep (primes::bn-254-group-prime)) t) ((primep (primes::baby-jubjub-subgroup-prime)) t)) :debug t :hints (("Goal" :in-theory (enable primes::primep-of-baby-jubjub-subgroup-prime-constant primes::primep-of-bn-254-group-prime-constant primes::primep-of-secp256k1-field-prime-constant)))) General Form: (add-io-pairs tuples &key hints debug test verbose) }) <p>where the arguments, which are not evaluated, are described below and the keyword arguments are optional.</p> <ul> <li>@('Tuples') is a list of I/O pairs, each of the form @('((fn i_1 ... i_k) val)') where @('fn') is a @(see guard)-verified function symbol, each @('i_n') is a term, and @('val') is a term. (The @(see term)s need not be translated.) @('Fn') must be the same in each of these I/O pairs, and must not take @(see state) or any user-defined @(see stobj) as an argument. All @('i_n') and @('val') are evaluated to produce values used as inputs and corresponding output; therefore, these terms should not contain any free variables.</li> <li>@('Hints') (optional, default @('nil')), when non-@('nil'), is used as the @(':')@(tsee hints) argument for the theorem discussed below.</li> <li>@('Test') (optional, default @('equal')) is the equality variant — @(tsee eq), @(tsee eql), or @(tsee equal) — used for testing equality of each input of @('fn') to a corresponding input of an I/O pair; or, @('test') can be a true-list of such equality variants, as described in the concluding remarks below.</li> <li>@('Debug') (optional, default @('nil')), when non-@('nil'), causes @(tsee cw) to print a message to the terminal when an I/O pair is being used during evaluation (thus avoiding the usual computation for @('fn')).</li> <li>@('Verbose') (optional, default @('nil')), when non-@('nil'), avoids suppressing output (other than what is currently globally suppressed; see @(see set-inhibit-output-lst)). This argument may be particularly useful when the proof fails for the theorem, described below, that equates @('fn') to the corresponding new function (that looks up inputs from a table of all verified I/O pairs).</li> </ul> <p>If @('tuples') is @('nil') then the call of @('add-io-pairs') is a no-op. Otherwise, as noted above, the function symbol (@('fn'), above) must be the same for each I/O pair.</p> <p>This event defines a new function, denoted @('new-fn') below (the actual name is generated automatically), to compute as follows. First, the inputs @('i_n') and corresponding output @('val') of an I/O pair @('((fn i_1 ... i_k) val)') are evaluated to produce a corresponding ``evaluated I/O pair'' @('((fn j_1 ... j_k) v)'), where the values of @('i_n') and @('val') are @('j_n') and @('v'), respectively. Then @('new-fn') is defined so that @('(fn j_1 ... j_k)') is computed by finding that evaluated I/O pair and returning @('v'), thus avoiding the usual computation for @('fn'). That is: a call of @('new-fn') considers every evaluated I/O pair @('((fn j_1 ... j_k) val)'), whether added by this call of @('add-io-pairs') or a previous such call, searching for one whose inputs @('(j_1 ... j_k)') equal that call's actual parameters, and returning the corresponding output @('v') in that case; if no such evaluated I/O pair is found, then @('new-fn') just calls @('fn'). This description is accurate if @('fn') returns a single value; otherwise, if @('fn') returns @('n') values where @('n') is greater than 1, @('val') should evaluate to multiple values @('(mv v_1 .... v_n)'), in which case the multiple values returned by @('new-fn') are @('v_1') through @('v_n'). The definition of @('new-fn') thus has roughly the following form, where: @('IO-LOOKUP') denotes a lookup utility that searches for the given inputs among evaluated I/O pairs, returning the one-element list containing the value associated with those inputs, if found; @('TEST') is the value for @(':test') discussed above (defaulting to @('equal')); and @('IO-PAIRS') is the extension of the existing evaluated I/O pairs for @('fn') by the current call of @('add-io-pairs'), as described above.</p> @({ (defun new-fn (x1 ... xk) ; same formals as for fn (declare (xargs :guard t)) ; ensure guard-verified <declare_forms> ; same declare forms as for fn (let ((pair <<IO-LOOKUP (x1 ... xk) in 'IO-PAIRS using 'TEST>>)) (if pair (car pair) (fn x)))) }) <p>The event displayed above is approximate. One can see the precise definition produced by evaluating a call of @('add-io-pairs') and using @(':')@(tsee pcb!)@(' :x') to see what has been generated. Alternatively, run @('add-io-pairs') using option @(':verbose t') and peruse the log.</p> <p>In the underlying Common Lisp, @('fn') is redefined to be @('new-fn'), but with a twist: once control passes from @('fn') to @('new-fn'), all recursive calls of @('fn') will be calls of the old version of @('fn'), without re-entering @('new-fn'). Note that when @('new-fn') is called on an input list that has an associated I/O pair, the corresponding output is returned immediately without calling @('fn') (which of course is the point of this tool); thus, in particular, side effects from @('fn') such as printing with @(tsee cw) will not take place for such an input list.</p> <p>A generated proof obligation has the following form, where @('HINTS') below is the non-@('nil') @(':hints') keyword if supplied by @('add-io-pairs'); otherwise the @(':hints') keyword below is omitted.</p> @({ (defthm <generated_name> (equal (fn x1 ... xk) (new-fn x1 ... xk)) :hints HINTS ; omitted if the given :hints is nil or omitted :rule-classes nil) }) <p>We conclude with a few remarks.</p> <p>Remark 1. When the value @(':test') is a non-@('nil') list, its length should be the number of inputs of @('fn') and each member should be @('eq'), @('eql'), or @('equal'), indicating the test used when comparing an input at that position to an input specified in an evaluated I/O pairs for @('fn').</p> <p>Remark 2. Evaluation of input and output terms in an I/O pair is performed with guard-checking set to @('nil') (see @(see set-guard-checking)) and attachments allowed (see @(see defattach)).</p> <p>Remark 3. Although @('fn') is required to be @(see guard)-verified, one may be able to avoid most of the effort of guard verification by using @(tsee ec-call). Here is a trivial example that illustrates the technique.</p> @({ ACL2 !>(defun h (x) (declare (xargs :guard t)) (ec-call (car x))) Since H is non-recursive, its admission is trivial. We could deduce no constraints on the type of H. Computing the guard conjecture for H.... The guard conjecture for H is trivial to prove. H is compliant with Common Lisp. Summary Form: ( DEFUN H ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) H ACL2 !>(add-io-pairs (((h 3) nil) ((h '(a b c)) 'a)) :debug t) H ACL2 !>(h 3) ; DEBUG: Found io-pair for input list (3). NIL ACL2 !>(h '(a b c)) ; DEBUG: Found io-pair for input list ((A B C)). A ACL2 !>(h '(e f)) E ACL2 !>(h 7) 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 7). See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. ACL2 !>(add-io-pair (h 7) nil) H ACL2 !>(h 7) NIL ACL2 !>(h '(a b c)) A ACL2 !> }) <p>Note that there is no debug printing in the final two calls. This isn't surprising for @('(h 7)'), since the call of @('add-io-pair') for @('(h 7)') did not specify keyword argument @(':debug'). It may be more surprising that debug printing no longer occurs for @('(h '(a b c))'). The reason is that each invocation of @('add-io-pair') or @('add-io-pairs') defines a new replacement function (denoted @('new-fn') in the discussions above), which is based on the updated table of evaluated I/O pairs and the @(':debug') option provided to the new invocation.</p> <p>Remark 4. A more general utility, which allows the substitution of one function for another during execution, is available with the @(':invoke') argument of @(tsee memoize). Indeed, @('add-io-pairs') actually works by invoking @('(memoize 'fn :invoke 'new-fn)'), where @('new-fn') is as above. Note that this memoization does not perform @('memoize')'s usual function of saving computational results.</p> <p>Remark 5. If you include a book with an @('add-io-pairs') form for a function symbol, @('fn'), to which you have already added I/O pairs in the current session, then by default an error will occur. The key relevant observation is that during book certification, when @('add-io-pairs') defines @('new-fn') as discussed above, that definition is saved in the book's @(see certificate). (Technical note: This is a consequence of the use of @(tsee make-event) in the implementation of @('add-io-pairs').) Without an error, ACL2 would simply use that saved definition of @('new-fn'), discarding the I/O pairs previously added in the current session.</p> <p>The error message explains how to allow the @(tsee include-book) to complete without error, merging in all I/O pairs from the current session and included books by wrapping it in a call of the macro, @(tsee merge-io-pairs), whose first argument is @('fn'). So a sequence of events might look like this.</p> @({ (defun f (x) (declare (xargs :guard t)) (cons x x)) (include-book "book1") ; has calls of add-io-pair(s) for f (merge-io-pairs f (include-book "book2") ; has calls of add-io-pair(s) for f ) }) <p>An analogous problem occurs with @(tsee encapsulate), when there are @(see local) calls of @('add-io-pairs') followed by non-local calls. Much as @('certify-book') saves the definition of @('new-fn') in the book's certificate, ACL2 saves such a definition from the first pass of the @('encapsulate') and detects missing I/O pairs (the local ones) in the second pass. We expect local calls of @('add-io-pairs') inside @('encapsulate') to be rare, so we do not discuss them further.</p> <p>Although the discussion above and the error message should suffice, you can get more understanding by looking at examples in the section ``Including a book'' in @(see community-book) @('std/util/add-io-pairs-tests.lisp'). For technical details (probably not necessary), you are also welcome to see @(see add-io-pairs-details).</p>")
other
(defxdoc add-io-pairs-details :parents (add-io-pairs) :short "Details about @(tsee add-io-pairs)" :long "<p>This rather technical topic is intended for those who have read the documentation for @(tsee add-io-pairs) and related topics but would like a more complete understanding of how @(tsee add-io-pairs) works. Our hope is that very few will have any reason to read the present topic!</p> <p>A key aspect of the implementation of @('add-io-pairs') is the use of a @(see table), @('io-pairs-table'), to store all I/O pairs. Indeed, the utilities @(tsee show-io-pairs) and @(tsee get-io-pairs) retrieve I/O pairs from this table. When @('add-io-pairs') is invoked on I/O pairs for a function symbol, @('fn'), it extends that table with those I/O pairs and then generates a @(tsee defun) event for a new function. The documentation topic @(see add-io-pairs) explains that this new function, @('new-fn'), computes by looking up the inputs in the available I/O pairs to get the result immediately if possible, otherwise calling @('fn'). Finally, it uses a special form of @(see memoization) to compute calls of @('fn') by calling @('new-fn').</p> <p>The following log fleshes out the explanation above. It shows that @('add-io-pairs') generates a call of @(tsee make-event), which we expand below to see the events created by that @('make-event') invocation. Comments have been inserted in lower case into the final output below.</p> @({ ACL2 !>(defun f (x) (declare (xargs :guard t)) (cons x x)) [[.. output elided ..]] F ACL2 !>:trans1 (add-io-pair (f 3) (cons 3 (/ 6 2))) (ADD-IO-PAIRS (((F 3) (CONS 3 (/ 6 2))))) ACL2 !>:trans1 (add-io-pairs (((f 3) (cons 3 (/ 6 2))))) (WITH-OUTPUT :OFF :ALL :ON ERROR :GAG-MODE NIL (MAKE-EVENT (B* ((TUPLES '(((F 3) (CONS 3 (/ 6 2))))) (HINTS 'NIL) (DEBUG 'NIL) (TEST 'EQUAL) (WRLD (W STATE)) ((WHEN (NULL TUPLES)) (VALUE '(VALUE-TRIPLE :EMPTY-IO-PAIRS))) (CTX 'ADD-IO-PAIRS) ((ER IO-DOUBLET-LST) (ADD-IO-PAIRS-TRANSLATE-TUPLES TUPLES CTX WRLD STATE)) (FN (CAAR (CAR TUPLES))) (EVENTS (ADD-IO-PAIRS-EVENTS FN IO-DOUBLET-LST HINTS DEBUG TEST WRLD))) (VALUE (CONS 'PROGN EVENTS))) :ON-BEHALF-OF :QUIET!)) ACL2 !>(b* ((tuples '(((f 3) (cons 3 (/ 6 2))))) (hints 'nil) (debug 'nil) (test 'equal) (wrld (w state)) ((when (null tuples)) (value '(value-triple :empty-io-pairs))) (ctx 'add-io-pairs) ((er io-doublet-lst) (add-io-pairs-translate-tuples tuples ctx wrld state)) (fn (caar (car tuples))) (events (add-io-pairs-events fn io-doublet-lst hints debug test wrld))) (value (cons 'progn events))) (PROGN ; Cause an error when including a book or running the second pass of an ; encapsulate, if the I/O pairs for F in the io-pairs table do not match ; those in the table from a previous invocation -- unless we are in the ; scope of merge-io-pairs for F. (CHECK-IO-PAIRS-LENIENCE F NIL ADD-IO-PAIRS) ; Update the I/O pairs for F in the io-pairs-table. (TABLE IO-PAIRS-TABLE 'F (LET ((OLD-ENTRY (CDR (ASSOC-EQ 'F (TABLE-ALIST 'IO-PAIRS-TABLE WORLD))))) (UPDATE-IO-LOOKUP-LST '(((3) (3 . 3))) OLD-ENTRY))) ; Define the new-fn for F. (DEFUN F29623679 (X) (DECLARE (XARGS :VERIFY-GUARDS T)) (DECLARE (XARGS :GUARD T)) (LET* ((IO-LOOKUP-VAR0 '((3 (3 . 3)))) (IO-LOOKUP-VAR0 (IO-LOOKUP IO-LOOKUP-VAR0 EQUAL X))) (IF IO-LOOKUP-VAR0 (CAR IO-LOOKUP-VAR0) (F X)))) ; Prove that F equals its new-fn, as required by the memoize call below. (DEFTHM F52318143 (EQUAL (F X) (F29623679 X)) :RULE-CLASSES NIL) ; Remove any existing memoization of F (redundant if F is not memoized). (UNMEMOIZE 'F) ; Arrange for F to call its new-fn. (MEMOIZE 'F :INVOKE 'F29623679)) ACL2 !> }) <p>It is also instructive to look at the implementation of @(tsee merge-io-pairs). We can see what is going on by using single-step macroexpansion, below: first, @('push-io-pairs-lenience') removes any @('check-io-pairs-lenience') checks discussed above for the indicated function symbols; next, the books are included; then, @(tsee install-io-pairs) merges all I/O pairs for each function (as discussed below); and finally, the @('pop-io-pairs-lenience') call undoes the effect of the @('push-io-pairs-lenience') call.</p> @({ ACL2 !>:trans1 (merge-io-pairs (f g h) (include-book "book1") (include-book "book2")) (PROGN (PUSH-IO-PAIRS-LENIENCE F G H) (INCLUDE-BOOK "book1") (INCLUDE-BOOK "book2") (INSTALL-IO-PAIRS F) (INSTALL-IO-PAIRS G) (INSTALL-IO-PAIRS H) (POP-IO-PAIRS-LENIENCE F G H)) ACL2 !> }) <p>The discussion above leads us to look at the implementation of @(tsee install-io-pairs), again using a log (below). Notice that the events are essentially the same as for @('add-io-pairs'), except that the table is not changed. In particular, there is still a call of @('check-io-pairs-lenience'), for essentially the same reason: imagine if @('(install-io-pairs f)') is in a certified book that is included after having added I/O pairs for @('f') in the current session.</p> @({ ACL2 !>:trans1 (install-io-pairs f) (WITH-OUTPUT :OFF :ALL :ON ERROR :GAG-MODE NIL (MAKE-EVENT (B* ((FN 'F) (HINTS 'NIL) (DEBUG 'NIL) (TEST 'EQUAL) (WRLD (W STATE)) (IO-DOUBLET-LST :SKIP) (EVENTS (ADD-IO-PAIRS-EVENTS FN IO-DOUBLET-LST HINTS DEBUG TEST WRLD))) (VALUE (CONS 'PROGN EVENTS))) :ON-BEHALF-OF :QUIET!)) ACL2 !>(b* ((fn 'f) (hints 'nil) (debug 'nil) (test 'equal) (wrld (w state)) (io-doublet-lst :skip) (events (add-io-pairs-events fn io-doublet-lst hints debug test wrld))) (value (cons 'progn events))) (PROGN (CHECK-IO-PAIRS-LENIENCE F NIL INSTALL-IO-PAIRS) (DEFUN F1824557376 (X) (DECLARE (XARGS :VERIFY-GUARDS T)) (DECLARE (XARGS :GUARD T)) (LET* ((IO-LOOKUP-VAR0 'NIL) (IO-LOOKUP-VAR0 (IO-LOOKUP IO-LOOKUP-VAR0 EQUAL X))) (IF IO-LOOKUP-VAR0 (CAR IO-LOOKUP-VAR0) (F X)))) (DEFTHM F1847247744 (EQUAL (F X) (F1824557376 X)) :RULE-CLASSES NIL) (UNMEMOIZE 'F) (MEMOIZE 'F :INVOKE 'F1824557376)) ACL2 !> }) <p>We conclude by noting that @(tsee remove-io-pairs) not only removes all I/O pairs for the indicated function symbols from the @('io-pairs-table'), but also unmemoizes those function symbols. By contrast, @(tsee deinstall-io-pairs) leaves the @('io-pairs-table') unchanged, merely unmemoizing the indicated function.</p>")
other
(defxdoc merge-io-pairs :parents (add-io-pairs) :short "Incorporate I/O pairs from different sources" :long "@({ General Forms: (merge-io-pairs fn event_1 ... event_n) (merge-io-pairs (fn_1 ... fn_k) event_1 ... event_n) }) <p>where @('fn') and each @('fn_i') are symbols and each @('event_i') is an @(see event) form.</p> @({ Example Forms: (merge-io-pairs f (include-book "my-book")) (merge-io-pairs (f g h) (include-book "book1") (include-book "book2")) }) <p>The second General Form above, @('(merge-io-pairs (fn_1 ... fn_k) event_1 ... event_n)'), has an effect equivalent to iterating the first general form as follows.</p> @({ (merge-io-pairs fn_1 (merge-io-pairs fn_2 ... (merge-io-pairs fn_{k-1} (merge-io-pairs fn_k event_1 ... event_n)))) }) <p>So we focus below on the first General Form.</p> <p>Normally you will use @('merge-io-pairs') when an error occurs in @('add-io-pairs') while including a book; see Remark 5 of the documentation for @(see add-io-pairs) for explanation. In short: by wrapping @('(merge-io-pairs fn ...)') around one or more @(tsee include-book) events, as illustrated in the Example Forms above, you allow all @('add-io-pairs') forms in the books to complete in a manner that merges together all the I/O pairs for @('fn').</p>")
other
(defxdoc remove-io-pairs :parents (add-io-pairs) :short "Remove input-output pairs" :long "<p>For relevant background, see @(see add-io-pairs), which modifies a function by looking up the result for specified input-output pairs. The utility @('remove-io-pairs') removes all such pairs for the specified function symbols.</p> @({ General Forms: (remove-io-pairs fn1 ... fnk) (remove-io-pairs :all) }) <p>where the arguments are not evaluated. The use of @(':all') specifies that all I/O pairs are to be removed for all function symbols; otherwise, all I/O pairs are removed only for the specified function symbols. A warning is printed for each input @('fni') that does not currently have any I/O pairs.</p> <p>Remark. As @(tsee add-io-pairs) actually memoizes functions, @('remove-io-pairs') unmemoizes the specified functions. For a utility that unmemoizes while preserving I/O pairs for possible re-installation later, see @(see deinstall-io-pairs).</p>")
other
(defxdoc get-io-pairs :parents (add-io-pairs) :short "Return a list of verified input-output pairs" :long "<p>See @(see show-io-pairs) for a more user-friendly display of the current I/O pairs. See @(see add-io-pairs) for relevant background.</p> <p>@('Get-io-pairs') returns all evaluated I/O pairs for the specified function symbols.</p> @({ General Form: (get-io-pairs :all) (get-io-pairs fn1 ... fnk) }) <p>where the arguments are not evaluated. The use of @(':all') specifies that all I/O pairs are to be returned; otherwise, all I/O pairs are returned only for the specified functions, and a warning is printed for those that do not currently have any I/O pairs.</p> <p>A single value is returned, which is a list representing all evaluated I/O pairs for the indicated function symbols. Each member of the list has the form @('((fn j_1 ... j_k) v)'), where @('v') results from the application of @('fn') to the input list @('(j_1 ... j_k)') — where in the case that @('fn') returns multiple values @('v_1'), ..., @('v_n'), then @('v') is actually the list @('(mv v_1 .. v_n)'), and otherwise @('v') is just the result.</p> <p>A warning is printed for each @('fni') that has no associated I/O pairs.</p> <p>Note that unlike @(tsee show-io-pairs), the form @('(get-io-pairs)') simply returns @('nil'), i.e., @(':all') is not implicit when no arguments are supplied. The reason is that @('get-io-pairs') is intended primarily for use in programs, where the list of function symbols might be computed.</p>")
other
(defxdoc show-io-pairs :parents (add-io-pairs) :short "Display verified input-output pairs" :long "<p>See @(see add-io-pairs) for relevant background. @('Show-io-pairs') prints I/O pairs in a pleasant format, each starting on a new line. It is evaluated only for its side effect of printing. See @(see get-io-pairs) for a related utility, which returns a list of evaluated I/O pairs.</p> <p>@('Show-io-pairs') displays all (verified) I/O pairs for the specified function symbols. Normally printing goes to the terminal, but more generally it goes to @(tsee standard-co).</p> @({ General Forms: (show-io-pairs :all) (show-io-pairs) ; same as above (show-io-pairs fn1 ... fnk) ; k > 0 }) <p>where the arguments are not evaluated. If no arguments are supplied, or equivalently there is a single argument, @(':all'), then all I/O pairs are to be printed; otherwise, all I/O pairs are printed only for the specified function symbols.</p> <p>Each I/O pair is printed in the format expected as input to @(tsee add-io-pairs), that is, the inputs and result are terms. In other words, @('add-io-pairs') prints I/O pairs, not evaluated I/O pairs (again, see @(see add-io-pairs) for relevant background). Moreover, @('show-io-pairs') displays the inputs and result as quoted terms, such as @(''abc'), even when they result from a call of @('add-io-pairs') in which the terms were not all quoted, e.g., @('(car '(abc def))').</p> <p>A warning is printed for each @('fni') that has no associated I/O pairs.</p>")
other
(defxdoc install-io-pairs :parents (add-io-pairs) :short "Install input-output pairs" :long "@({ General Form: (install-io-pairs fn &key hints debug (test 'equal) verbose) }) <p>where the keywords have the same function as for @(tsee add-io-pairs).</p> <p>For relevant background, see @(see add-io-pairs). Evaluate @('(install-io-pairs fn)') to undo the effect of @(tsee deinstall-io-pairs).</p>")
other
(defxdoc deinstall-io-pairs :parents (add-io-pairs) :short "Deinstall input-output pairs" :long " @({ General Form: (deinstall-io-pairs fn) }) <p>For relevant background, see @(see add-io-pairs). This is just an alias for @(tsee unmemoize), thus causing evaluation to use the original definition of the given function symbol, @('fn'). The I/O pairs for @('fn') are still stored, unlike @(tsee remove-io-pairs); thus, you can later evaluate @('(install-io-pairs fn)') to restore the use of I/O pairs in the evaluation of @('fn').</p>")
add-io-pairmacro
(defmacro add-io-pair (fn/input output &key hints debug test verbose) `(add-io-pairs ((,FN/INPUT ,OUTPUT)) ,@(AND HINTS `(:HINTS ,HINTS)) ,@(AND DEBUG `(:DEBUG ,DEBUG)) ,@(AND TEST `(:TEST ,TEST)) ,@(AND VERBOSE `(:VERBOSE ,VERBOSE))))
other
(table io-pairs-table nil nil :guard (function-symbolp key world))
other
(table io-pairs-lenience-table nil nil :guard (and (symbolp key) (true-listp val)))
update-io-lookup-initfunction
(defun update-io-lookup-init (args val) (declare (xargs :guard (true-listp args))) (cond ((endp args) val) (t (acons (car args) (update-io-lookup-init (cdr args) val) nil))))
update-io-lookupfunction
(defun update-io-lookup (keys val x) (cond ((endp keys) val) (t (let ((pair (assoc-equal (car keys) x))) (cond (pair (put-assoc-equal (car keys) (update-io-lookup (cdr keys) val (cdr pair)) x)) (t (acons (car keys) (update-io-lookup-init (cdr keys) val) x)))))))
update-io-lookup-lstfunction
(defun update-io-lookup-lst (lst x) (cond ((endp lst) x) (t (update-io-lookup-lst (cdr lst) (let* ((pair (car lst)) (keys (car pair)) (val (cdr pair))) (update-io-lookup keys val x))))))
io-lookup-fnfunction
(defun io-lookup-fn (var keys tests) (declare (xargs :guard (true-listp keys))) (cond ((endp keys) var) (t `(let ((,VAR (cdr (assoc ,(CAR KEYS) ,VAR :test ',(CAR TESTS))))) ,(IO-LOOKUP-FN VAR (CDR KEYS) (CDR TESTS))))))
io-lookupmacro
(defmacro io-lookup (var tests &rest keys) (declare (xargs :guard (or (member-eq tests '(equal eq eql)) (and (true-listp tests) (equal (length tests) (length keys)) (subsetp-eq tests '(equal eq eql)))))) (io-lookup-fn var keys (if (atom tests) (make-list (length keys) :initial-element tests) tests)))
io-pairs-add-inputfunction
(defun io-pairs-add-input (input lst) (cond ((endp lst) nil) (t (b* (((cons (list inputs result) rest) lst)) (cons (list (cons input inputs) result) (io-pairs-add-input input rest))))))
get-io-pairs-fn2-lstmutual-recursion
(mutual-recursion (defun get-io-pairs-fn2-lst (formals lst mvp) (cond ((endp lst) nil) (t (append (get-io-pairs-fn2 formals (car lst) mvp) (get-io-pairs-fn2-lst formals (cdr lst) mvp))))) (defun get-io-pairs-fn2 (formals x mvp) (cond ((null formals) (list (list nil (if mvp (cons 'mv x) x)))) (t (io-pairs-add-input (car x) (get-io-pairs-fn2-lst (cdr formals) (cdr x) mvp))))))
get-io-pairs-fn1function
(defun get-io-pairs-fn1 (fns tbl wrld) (cond ((endp fns) nil) (t (append (let ((fn (car fns))) (io-pairs-add-input fn (get-io-pairs-fn2-lst (formals fn wrld) (cdr (assoc-eq fn tbl)) (consp (cdr (stobjs-out fn wrld)))))) (get-io-pairs-fn1 (cdr fns) tbl wrld)))))
get-io-pairs-fnfunction
(defun get-io-pairs-fn (fns wrld warnp state) (b* ((tbl (table-alist 'io-pairs-table wrld)) (tbl-fns (strip-cars tbl)) (allp (equal fns '(:all))) (bad (and (not allp) (set-difference-eq fns tbl-fns))) (fns (cond (allp tbl-fns) (bad (intersection-eq fns tbl-fns)) (t fns))) (- (and bad warnp (warning$-cw0 'get-io-pairs nil (default-state-vars t) "There ~#0~[is no I/O pair for the symbol~/are ~ no I/O pairs for the symbols~] ~&0." bad)))) (get-io-pairs-fn1 fns tbl wrld)))
get-io-pairsmacro
(defmacro get-io-pairs (&rest fns) (declare (xargs :guard (symbol-listp fns))) (if (and (member-eq :all fns) (not (equal fns '(:all)))) '(er soft 'get-io-pairs "It is illegal to use :ALL with ~x0 except in the form ~x1." 'get-io-pairs '(get-io-pairs :all)) `(get-io-pairs-fn ',FNS (w state) t state)))
maybe-kwote-lstfunction
(defun maybe-kwote-lst (x) (declare (xargs :guard (true-listp x) :mode :logic)) (cond ((endp x) nil) (t (cons (maybe-kwote (car x)) (maybe-kwote-lst (cdr x))))))
show-io-pairs-lstfunction
(defun show-io-pairs-lst (pairs chan wrld state) (cond ((endp pairs) (newline chan state)) (t (pprogn (b* ((pair (car pairs)) (fn/inputs (car pair)) (fn (car fn/inputs)) (inputs (cdr fn/inputs)) (result (cadr pair)) (mvp (cdr (stobjs-out fn wrld))) (qinputs (maybe-kwote-lst inputs)) (qresult (if mvp (assert$ (eq (car result) 'mv) (cons 'mv (maybe-kwote-lst (cdr result)))) (maybe-kwote result))) (io-pair `((,FN ,@QINPUTS) ,QRESULT))) (fms "~x0" (list (cons #\0 io-pair)) chan state nil)) (show-io-pairs-lst (cdr pairs) chan wrld state)))))
show-io-pairs-fnfunction
(defun show-io-pairs-fn (fns state) (b* (((when (and (member-eq :all fns) (not (equal fns '(:all))))) (er soft 'show-io-pairs "It is illegal to use :ALL with ~x0 except in the form ~x1." 'show-io-pairs '(show-io-pairs :all))) (wrld (w state)) (chan (standard-co state)) (allp (equal fns '(:all))) (pairs (get-io-pairs-fn (or fns '(:all)) wrld nil state))) (pprogn (cond ((null pairs) (fms "There are no verified I/O pairs to display~#0~[~/ for the symbol ~ ~v1~/ for any of the symbols ~v1~].~|" (list (cons #\0 (zero-one-or-more (and (not allp) fns))) (cons #\1 (and (not allp) fns))) chan state nil)) (t (pprogn (fms "Verified I/O pairs ((fn arg1 .. argn) result):~|" nil chan state nil) (show-io-pairs-lst pairs chan wrld state)))) (value :invisible))))
show-io-pairsmacro
(defmacro show-io-pairs (&rest fns) (declare (xargs :guard (symbol-listp fns))) `(show-io-pairs-fn ',FNS state))
simple-trans-eval-lstfunction
(defun simple-trans-eval-lst (lst i call ctx wrld state) (b* (((when (endp lst)) (value nil)) (term (car lst)) ((er (cons ?tterm val)) (simple-translate-and-eval term nil nil (msg "The ~n0 argument of the call ~x1" (list i) call) ctx wrld state t)) ((er rest) (simple-trans-eval-lst (cdr lst) (1+ i) call ctx wrld state))) (value (cons val rest))))
add-io-pairs-translate-tuples-1function
(defun add-io-pairs-translate-tuples-1 (tuples fn arity-in stobjs-out ctx wrld state) (b* (((when (null tuples)) (value nil)) (tuple (car tuples)) ((when (not (and (consp tuple) (consp (car tuple)) (consp (cdr tuple)) (null (cddr tuple))))) (er soft ctx "An I/O tuple must be of the form ((fn x1 ... xk) result), but ~ the following is not of that form:~|~x0" tuple)) ((list (cons fn2 actuals) result) tuple) ((when (not (eq fn2 fn))) (er soft ctx "It is illegal to specify more than one function symbol in a call ~ of add-io-pairs, but both ~x0 and ~x1 were specified." fn fn2)) ((when (not (= (length actuals) arity-in))) (er soft ctx "The I/O pair ~x0 specifies ~x1 inputs, but the function symbol ~ ~x2 expects ~x3 inputs." tuple (length actuals) fn arity-in)) ((er inputs) (simple-trans-eval-lst actuals 1 (cons fn actuals) ctx wrld state)) ((er -) (b* (((mv erp ?trans bindings state) (translate1 result :stobjs-out '((:stobjs-out . :stobjs-out)) t ctx wrld state)) ((when erp) (silent-error state)) (result-stobjs-out (translate-deref :stobjs-out bindings)) ((when (not (equal stobjs-out result-stobjs-out))) (er soft ctx "The I/O pair ~x0 specifies a return of ~x1 value~#2~[~/s~] ~ but the function ~x3 returns ~x4 value~#5~[~/s~]." tuple (length result-stobjs-out) result-stobjs-out fn (length stobjs-out) stobjs-out))) (value nil))) ((er (cons ? output)) (trans-eval result ctx state t)) (io-doublet (list inputs output)) ((er io-doublet-lst) (add-io-pairs-translate-tuples-1 (cdr tuples) fn arity-in stobjs-out ctx wrld state))) (value (cons io-doublet io-doublet-lst))))
add-io-pairs-translate-tuplesfunction
(defun add-io-pairs-translate-tuples (tuples ctx wrld state) (declare (xargs :guard tuples)) (cond ((not (and (alistp tuples) (alistp (strip-cars tuples)))) (er soft ctx "The first argument of add-io-pairs must be a true list of pairs of ~ the form ((fn arg1 ... argk) result). The argument ~x0 is thus ~ illegal." tuples)) ((mbt (consp tuples)) (b* ((tuple (car tuples)) (fn (caar tuple)) ((when (not (and (symbolp fn) (function-symbolp fn wrld) (eq (symbol-class fn wrld) :common-lisp-compliant)))) (er soft ctx "Not a guard-verified function symbol: ~x0" fn)) (stobjs-in (stobjs-in fn wrld)) (arity-in (length stobjs-in)) ((when (member-eq fn *stobjs-out-invalid*)) (er soft ctx "It is illegal to add I/O pairs for the function symbol ~x0." fn)) (stobjs-out (stobjs-out fn wrld)) (stobjs (union-eq (remove-eq nil stobjs-in) (remove-eq nil stobjs-out))) ((when stobjs) (er soft ctx "It is illegal to call add-io-pairs for the function symbol ~ ~x0, because it traffics in the stobj~#1~[~/s~] ~x1." fn stobjs))) (with-guard-checking-error-triple nil (add-io-pairs-translate-tuples-1 tuples fn arity-in stobjs-out ctx wrld state)))) (t (prog2$ (er hard ctx "Implementation error: Impossible case!") (value nil)))))
add-io-pairs-dclsfunction
(defun add-io-pairs-dcls (fn wrld) (declare (xargs :mode :program)) (cons '(declare (xargs :verify-guards t)) (let* ((ev (get-event fn wrld)) (def (case (car ev) (defun (cdr ev)) (mutual-recursion (assoc-eq fn (strip-cdrs (cdr ev)))) (verify-termination-boot-strap (cdr (cltl-def-from-name fn wrld))) (otherwise `(declare (xargs :guard ,(GUARD FN NIL WRLD))))))) (butlast (cddr def) 1))))
formal-mvfunction
(defun formal-mv (n expr i vars bindings) (cond ((zp n) (list 'let* (reverse bindings) `(mv ,@(REVERSE VARS)))) (t (let* ((xi (packn (list 'x i))) (xi-binding (list xi expr)) (ai (packn (list 'a i))) (ai-binding (list ai (list 'car xi))) (next-expr (list 'cdr xi))) (formal-mv (1- n) next-expr (1+ i) (cons ai vars) (list* ai-binding xi-binding bindings))))))
push-io-pairs-lenience-fnfunction
(defun push-io-pairs-lenience-fn (fns) (cond ((endp fns) nil) (t (cons `(table io-pairs-lenience-table ',(CAR FNS) (cons nil (cdr (assoc-eq ',(CAR FNS) (table-alist 'io-pairs-lenience-table world))))) (push-io-pairs-lenience-fn (cdr fns))))))
push-io-pairs-leniencemacro
(defmacro push-io-pairs-lenience (&rest fns) (cons 'progn (push-io-pairs-lenience-fn fns)))
pop-io-pairs-lenience-fnfunction
(defun pop-io-pairs-lenience-fn (fns) (cond ((endp fns) nil) (t (cons `(table io-pairs-lenience-table ',(CAR FNS) (cdr (cdr (assoc-eq ',(CAR FNS) (table-alist 'io-pairs-lenience-table world))))) (pop-io-pairs-lenience-fn (cdr fns))))))
pop-io-pairs-leniencemacro
(defmacro pop-io-pairs-lenience (&rest fns) (cons 'progn (pop-io-pairs-lenience-fn fns)))
check-io-pairs-lenience-fnfunction
(defun check-io-pairs-lenience-fn (fn old-entry caller state) (b* ((wrld (w state)) (lenience (cdr (assoc-eq fn (table-alist 'io-pairs-lenience-table wrld)))) ((when lenience) (value '(value-triple :invisible))) (current-entry (cdr (assoc-eq fn (table-alist 'io-pairs-table wrld)))) (ctx caller) ((when (not (equal old-entry current-entry))) (b* ((book (car (global-val 'include-book-path wrld))) (str "ACL2 is encountering a call of ~x0 on function symbol ~ ~#2~[~x1 while attempting to include the book ~ ~x3.~|~/~x1. ~]But the existing list of I/O pairs for ~ ~x1 is different from ~@4. See :DOC merge-io-pairs for ~ how to avoid this ~@5.") (num2 (if book 0 1)) (str4 (if book "when that book was being certified" "what it was previously at that point (perhaps during ~ the first pass of an encapsulate event)"))) (with-output! :on error (er soft ctx str caller fn num2 book str4 "error"))))) (value '(value-triple :invisible))))
check-io-pairs-leniencemacro
(defmacro check-io-pairs-lenience (fn old-entry caller) `(make-event (check-io-pairs-lenience-fn ',FN ',OLD-ENTRY ',CALLER state) :check-expansion t :on-behalf-of :quiet!))
add-io-pairs-eventsfunction
(defun add-io-pairs-events (fn io-doublet-lst hints debug test wrld) (b* ((old-entry (cdr (assoc-eq fn (table-alist 'io-pairs-table wrld)))) (new-entry (if (eq io-doublet-lst :skip) old-entry (update-io-lookup-lst io-doublet-lst old-entry))) (sum (check-sum-obj new-entry)) (max (max-absolute-event-number wrld)) (suffix1 (check-sum-obj (list* 'defthm fn sum max))) (thm-name (add-suffix fn (coerce (explode-atom suffix1 10) 'string))) (suffix2 (check-sum-obj (list* 'defun fn sum max))) (new-fn (add-suffix fn (coerce (explode-atom suffix2 10) 'string))) (formals (formals fn wrld)) (io-lookup-var (genvar fn "IO-LOOKUP-VAR" 0 formals)) (lookup-result0 `(car ,IO-LOOKUP-VAR)) (stobjs-out (stobjs-out fn wrld)) (lookup-result (if (cdr stobjs-out) (formal-mv (length stobjs-out) lookup-result0 0 nil nil) lookup-result0))) `((check-io-pairs-lenience ,FN ,OLD-ENTRY ,(IF (EQ IO-DOUBLET-LST :SKIP) 'INSTALL-IO-PAIRS 'ADD-IO-PAIRS)) ,@(AND (NOT (EQ IO-DOUBLET-LST :SKIP)) `((TABLE IO-PAIRS-TABLE ',FN (LET ((OLD-ENTRY (CDR (ASSOC-EQ ',FN (TABLE-ALIST 'IO-PAIRS-TABLE WORLD))))) (UPDATE-IO-LOOKUP-LST ',IO-DOUBLET-LST OLD-ENTRY))))) (defun ,NEW-FN ,FORMALS ,@(ADD-IO-PAIRS-DCLS FN WRLD) (let* ((,IO-LOOKUP-VAR ',NEW-ENTRY) (,IO-LOOKUP-VAR (io-lookup ,IO-LOOKUP-VAR ,TEST ,@FORMALS))) (if ,IO-LOOKUP-VAR ,(IF DEBUG `(PROG2$ (CW "; DEBUG: Found io-pair for input list ~x0.~|" (LIST ,@FORMALS)) ,LOOKUP-RESULT) LOOKUP-RESULT) (,FN ,@FORMALS)))) (encapsulate nil (local (in-theory (enable assoc (:e assoc)))) (defthm ,THM-NAME (equal (,FN ,@FORMALS) (,NEW-FN ,@FORMALS)) ,@(AND HINTS `(:HINTS ,HINTS)) :rule-classes nil)) (unmemoize ',FN) (memoize ',FN :invoke ',NEW-FN))))
add-io-pairsmacro
(defmacro add-io-pairs (tuples &key hints debug (test 'equal) verbose) (let ((form `(b* ((tuples ',TUPLES) (hints ',HINTS) (debug ',DEBUG) (test ',TEST) (wrld (w state)) ((when (null tuples)) (value '(value-triple :empty-io-pairs))) (ctx 'add-io-pairs) ((er io-doublet-lst) (add-io-pairs-translate-tuples tuples ctx wrld state)) (fn (caar (car tuples))) (events (add-io-pairs-events fn io-doublet-lst hints debug test wrld))) (value (cons 'progn events))))) (cond (verbose `(make-event ,FORM)) (t `(with-output :off :all :on error :gag-mode nil (make-event ,FORM :on-behalf-of :quiet!))))))
remove-assoc-eq-lstfunction
(defun remove-assoc-eq-lst (lst alist) (declare (xargs :guard (if (symbol-listp lst) (alistp alist) (symbol-alistp alist)))) (if (atom lst) alist (remove-assoc-eq-lst (cdr lst) (remove-assoc-eq (car lst) alist))))
remove-io-pairs-eventfunction
(defun remove-io-pairs-event (fns ctx state) (b* (((when (null fns)) (er soft ctx "~x0 requires at least one argument. Perhaps you intended ~x1." 'remove-io-pairs '(remove-io-pairs :all))) ((when (and (member-eq :all fns) (not (equal fns '(:all))))) (er soft 'show-io-pairs "It is illegal to use :ALL with ~x0 except in the form ~x1." 'remove-io-pairs '(remove-io-pairs :all))) (wrld (w state)) (tbl (table-alist 'io-pairs-table wrld)) (tbl-fns (strip-cars tbl)) (allp (equal fns '(:all))) (bad (and (not allp) (set-difference-eq fns tbl-fns))) (fns (cond (allp tbl-fns) (bad (intersection-eq fns tbl-fns)) (t fns))) ((er -) (if bad (with-output! :stack :pop (pprogn (warning$ ctx "Add-io-pairs" "There ~#0~[is no I/O pair for the ~ symbol~/are no I/O pairs for the symbols~] ~ ~&0." bad) (value nil))) (value nil)))) (value `(progn ,@(PAIRLIS-X1 'UNMEMOIZE (PAIRLIS$ (KWOTE-LST FNS) NIL)) (table io-pairs-table nil (remove-assoc-eq-lst ',FNS (table-alist 'io-pairs-table world)) :clear)))))
remove-io-pairsmacro
(defmacro remove-io-pairs (&rest fns) (declare (xargs :guard (symbol-listp fns))) `(with-output :stack :push :off :all :on error :gag-mode nil (make-event (remove-io-pairs-event ',FNS 'remove-io-pairs state) :on-behalf-of :quiet!)))
install-io-pairsmacro
(defmacro install-io-pairs (fn &key hints debug (test 'equal) verbose) (let ((form `(b* ((fn ',FN) (hints ',HINTS) (debug ',DEBUG) (test ',TEST) (wrld (w state)) (io-doublet-lst :skip) (events (add-io-pairs-events fn io-doublet-lst hints debug test wrld))) (value (cons 'progn events))))) (cond (verbose `(make-event ,FORM)) (t `(with-output :off :all :on error :gag-mode nil (make-event ,FORM :on-behalf-of :quiet!))))))
deinstall-io-pairsmacro
(defmacro deinstall-io-pairs (fn) `(unmemoize ',FN))
merge-io-pairs-fnfunction
(defun merge-io-pairs-fn (fns events) (declare (xargs :guard (and (symbol-listp fns) (true-listp events)))) `(progn (push-io-pairs-lenience ,@FNS) ,@EVENTS ,@(PAIRLIS-X1 'INSTALL-IO-PAIRS (PAIRLIS-X2 FNS NIL)) (pop-io-pairs-lenience ,@FNS)))
merge-io-pairsmacro
(defmacro merge-io-pairs (x &rest events) (declare (xargs :guard (or (symbolp x) (and (symbol-listp x) (no-duplicatesp-eq x))))) (merge-io-pairs-fn (if (symbolp x) (list x) x) events))