Filtering...

defpm

books/misc/defpm

Included Books

top
other
(in-package "ACL2")
include-book
(include-book "xdoc/top" :dir :system)
other
(defpointer defpm def-partial-measure)
other
(defxdoc def-partial-measure
  :parents (macro-libraries)
  :short "Introduce measure and termination predicates for a partial function
 definition"
  :long "<h3>Introduction By Way of an Example</h3>

 <p>We begin with a motivating example.  Suppose we want to admit factorial
 without the need to prove termination, as follows.</p>

 @({
 (fact x)
 =
 (if (equal x 0)
     1
   (* x (fact (1- x))))
 })

 <p>Of course, this ``definition'' is non-terminating on negative number
 inputs.  But with @('def-partial-measure'), or @('defpm') for short, we can
 admit a suitable definition for this partial function as follows.  First, we
 define a function to represent the termination test and another function to
 represent the actual parameter of the recursive call.</p>

 @({
 (defun fact-test (x)
   (equal x 0))
 (defun fact-step (x)
   (1- x))
 })

 <p>Now we can execute our utility: we provide it with the names of the two
 functions above, and it generates a measure, a termination predicate, and a
 potentially helpful theory, respectively.</p>

 @({
 (defpm fact-test fact-step
   fact-measure fact-terminates fact-theory)
 })

 <p>Here are the @(see events) exported by the @('defpm') call above.</p>

 @({
 ; The first three lemmas can be useful for reasoning about the termination
 ; predicate, FACT-TERMINATES.

 (DEFTHM FACT-TERMINATES-BASE
   (IMPLIES (FACT-TEST X)
            (FACT-TERMINATES X)))

 (DEFTHM FACT-TERMINATES-STEP
   (IMPLIES (NOT (FACT-TEST X))
            (EQUAL (FACT-TERMINATES (FACT-STEP X))
                   (FACT-TERMINATES X))))

 (DEFTHMD FACT-TERMINATES-STEP-COMMUTED
   (IMPLIES (AND (SYNTAXP (SYMBOLP X))
                 (NOT (FACT-TEST X)))
            (EQUAL (FACT-TERMINATES X)
                   (FACT-TERMINATES (FACT-STEP X)))))

 (THEORY-INVARIANT (INCOMPATIBLE (:REWRITE FACT-TERMINATES-STEP)
                                 (:REWRITE FACT-TERMINATES-STEP-COMMUTED)))

 ; The next two lemmas can be useful for defining functions whose termination
 ; is ensured by the measure just introduced.

 (DEFTHM FACT-MEASURE-TYPE
   (O-P (FACT-MEASURE X)))

 (DEFTHM FACT-MEASURE-DECREASES
   (IMPLIES (AND (FACT-TERMINATES X)
                 (NOT (FACT-TEST X)))
            (O< (FACT-MEASURE (FACT-STEP X))
                (FACT-MEASURE X))))

 ; Finally, the four enabled rewrite rules above are collected into a theory.

 (DEFTHEORY FACT-THEORY
   '(FACT-TERMINATES-BASE FACT-TERMINATES-STEP
     FACT-MEASURE-TYPE FACT-MEASURE-DECREASES))
 })

 <p>With the events above, we can introduce the following definition, which in
 effect guards the body with the termination predicate.  (Perhaps at some point
 we will extend @('defpm') to create this definition automatically.)  The
 @(':')@(tsee in-theory) hint below was carefully crafted to allow the proof to
 succeed very quickly.</p>

 @({
 (defun fact (x)
   (declare (xargs :measure (fact-measure x)
                   :hints (("Goal"
                            :in-theory
                            (union-theories (theory 'fact-theory)
                                            (theory 'minimal-theory))))))
   (if (fact-terminates x)
       (if (fact-test x)
           1
         (* x (fact (fact-step x))))
     1 ; don't-care
     ))
 })

 <p>With the events above (not necessarily including the definition of
 @('fact')), we can prove that @('fact') terminates on natural number inputs.
 A second macro, @('defthm-domain'), automates much of that task:</p>

 @({
 (defthm-domain fact-terminates-holds-on-natp
   (implies (natp x)
            (fact-terminates x))
   :measure (nfix x))
 })

 <p>See @(see defthm-domain).</p>

 <h3>Detailed Documentation</h3>

 <p>General form:</p>

 @({
 (defpm ; or equivalently, def-partial-measure ; ;
   TEST STEP
   MEASURE TERMINATES THEORY
   :formals FORMALS ; default is (x)
   :verbose VERBOSE ; default is nil
   )
 })

 <p>where there is no output unless @('VERBOSE') is non-@('nil').  The
 remaining arguments are as follows.</p>

 <p>First consider the case that @('FORMALS') is the default, @('(x)').  The
 arguments @('TEST') and @('STEP') are conceptually ``inputs'': they should
 name existing functions on the indicated value for formals (which by default
 is @('(x)')).  Then @('defpm') will attempt to generate a measure and
 termination predicate on those formals, with the indicated names (@('MEASURE')
 and @('TERMINATES'), respectively).  These theorems are suitable for admitting
 a function of the following form, where capitalized names refer to those in
 the @('defpm') call above, and where additional code may appear as indicated
 with ``...''.</p>

 @({
 (defun foo (x)
   (declare (xargs :measure (MEASURE x)
                   :hints (("Goal"
                            :in-theory
                            (union-theories (theory 'THEORY)
                                            (theory 'minimal-theory))))))
   (if (TERMINATES x)
       (if (TEST x)
           ...
         (... (fact (STEP x)) ...)
     ...))
 })

 <p>The generated @('THEORY') names the four rules generated by the @('defpm')
 call, as in the example above.</p>

 @({
 (defthm TERMINATES-base
   (implies (TEST x)
            (TERMINATES x)))
 (defthm TERMINATES-step
   (implies (not (TEST x))
            (equal (TERMINATES (STEP x))
                   (TERMINATES x))))
 (defthmd TERMINATES-step-commuted
   (implies (AND (syntaxp (symbolp x))
                 (not (TEST x)))
            (equal (TERMINATES x)
                   (TERMINATES (STEP x)))))
 (theory-invariant (incompatible (:rewrite TERMINATES-step)
                                 (:rewrite TERMINATES-step-commuted)))
 (defthm MEASURE-type
   (o-p (MEASURE x)))
 (defthm MEASURE-decreases
   (implies (and (TERMINATES x)
                 (not (TEST x)))
            (o< (MEASURE (STEP x))
                (MEASURE x))))
 (deftheory THEORY
   '(TERMINATES-base TERMINATES-step MEASURE-type MEASURE-decreases))
 })

 <p>For arbitrary formals the situation is similar, except that there is one
 step function per formal, obtained by adding the formal name as a suffix to
 the specified @('STEP') separated by a hyphen.  Thus we have the following
 events when @('FORMALS') is @('(y1 ... yk)').</p>

 @({
 (defthm TERMINATES-base
   (implies (TEST y1 ... yk)
            (TERMINATES y1 ... yk)))
 (defthm TERMINATES-step
   (implies (not (TEST y1 ... yk))
            (equal (TERMINATES (STEP-y1 y1 ... yk)
                               ...
                               (STEP-yk y1 ... yk))
                   (TERMINATES y1 ... yk))))
 (defthm TERMINATES-step-commuted
   (implies (AND (syntaxp (symbolp y1)) ... (syntaxp (symbolp yk))
                 (not (TEST y1 ... yk)))
            (equal (TERMINATES (STEP-y1 y1 ... yk)
                               ...
                               (STEP-yk y1 ... yk))
                   (TERMINATES y1 ... yk))))
 (theory-invariant (incompatible (:rewrite TERMINATES-step)
                                 (:rewrite TERMINATES-step-commuted)))
 (defthm MEASURE-type
   (o-p (MEASURE y1 ... yk)))
 (defthm MEASURE-decreases
   (implies (and (TERMINATES y1 ... yk)
                 (not (TEST y1 ... yk)))
            (o< (MEASURE (STEP-y1 y1 ... yk)
                         ...
                         (STEP-yk y1 ... yk))
                (MEASURE y1 ... yk))))
 (deftheory THEORY
   '(TERMINATES-base TERMINATES-step MEASURE-type MEASURE-decreases))
 })

 <h3>Implementation</h3>

 <p>The implementation of @('defpm') (i.e., @('def-partial-measure') has been
 designed to make proofs efficient.  It should be completely unnecessary to
 know anything about the implementation in order to use @('defpm') effectively.
 If however you are interested, you can execute @(':')@(tsee trans1) on your
 @('defpm') call to see the @(see events) that it generates.</p>

 <h3>More Information</h3>

 <p>The community book @('misc/defpm.lisp') illustrates how to use @('defpm')
 and @('defthm-domain') to define ``partial'' functions.  Search for calls of
 @('my-test') in that book to see examples.</p>

 <p>Related work of Dave Greve, in particular his utility @('def::ung'), may be
 found in community books directory @('books/coi/defung/').  Our utilities
 @('def-partial-measure') and @(tsee defthm-domain) were developed
 independently using an approach that seems considerably simpler than Greve's
 development.  However, his utility is much more powerful in that it generates
 a termination test, rather than requiring the user to provide it, and also it
 handles reflexive functions &mdash; definitions with recursive calls like
 @('(mc91 (mc91 (+ n 11)))') &mdash; while ours were not designed to do
 so.</p>")
other
(defxdoc defthm-domain
  :parents (macro-libraries)
  :short "Prove termination on a given domain"
  :long "<p>This utility can be useful after executing a call of @('defpm');
 see @(see def-partial-measure).  Indeed, we assume that you have read the
 example in that @(see documentation) topic describing this call:</p>

 @({
 (defpm fact-test fact-step
   fact-measure fact-terminates fact-theory)
 })

 <h3>Introduction By Way of an Example</h3>

 <p>Consider the following form.</p>

 @({
 (defthm-domain trfact-terminates-holds-on-natp
   (implies (natp x)
            (trfact-terminates x acc))
   :test trfact-test ; optional test name: can be deduced by the tool ;
   :step trfact-step ; optional step name: can be deduced by the tool ;
   :measure (nfix x) ; required argument ;
   )
 })

 <p>This call produces a proof of the indicated formula, where the first
 argument of @('implies'), @('(natp x)'), provides a ``domain hypothesis.''
 You can use @(':')@(tsee trans1) to see the macroexpansion of this
 @('defthm-domain') call.  In short, @(see hints) are supplied to automate all
 ``boilerplate'' reasoning.  The @(':measure') is used to guide a proof by
 induction.  At this stage of development, the best way to use this macro is
 probably to submit the form in the hope that the proof will complete
 automatically; but if it doesn't, then use @(':')@(tsee trans1) to see what
 the form generates, and modify that event manually in order to fix the failed
 proofs.</p>

 <h3>Detailed Documentation</h3>

 <p>General form:</p>

 @({
 (defthm-domain NAME
   (implies DOMAIN-TERM
            (TERMINATES FORMAL-1 ... FORMAL-K))
   :test TEST
   :step STEP
   :measure MEASURE
   :verbose VERBOSE
   :root ROOT
   )
 })

 <p>where there is no output unless @('VERBOSE') is non-@('nil').  It is
 allowed to replace the @(tsee implies) call above by its second argument (the
 @('TERMINATES') call) if @('DOMAIN-TERM') is @('t').  The remaining arguments
 are as follows.</p>

 <p>The keywords @(':test') and @(':step') both have value @('nil') by default.
 So does @(':root'), unless @('TERMINATES') has a @(tsee symbol-name) of the
 form @('"root-TERMINATES"'), in which case :root is the symbol in the
 package of @('TERMINATES') whose name is that string, @('root').  If
 @(':root') has a value of @('nil'), even after taking this default into
 account, then both @(':test') and @(':step') must have a non-@('nil') value.
 The reason for this requirement is that when @(':test') and/or @(':step') is
 omitted, the value is computed from the root by adding the suffix
 @('"-TEST"') or @('"-STEP"') to the root (respectively).  The functions
 introduced for @(':test') and @(':step') are exactly as for @('defpm'); see
 @(see def-partial-measure).  Note however that the formals are those from the
 call of @('TERMINATES').</p>

 <p>See the discussion above about ``boilerplate'' reasoning for hints on how
 to deal with failures of @('defthm-domain') calls.</p>

 <h3>More Information</h3>

 <p>The community book @('misc/defpm.lisp') illustrates how to use @('defpm')
 and @('defthm-domain') to define ``partial'' functions.  Search for calls of
 @('my-test') in that book to see examples.</p>")
defpm-add-suffixfunction
(defun defpm-add-suffix
  (sym suffix)
  (declare (xargs :guard (and (symbolp sym) (or (symbolp suffix) (stringp suffix)))))
  (intern-in-package-of-symbol (concatenate 'string
      (symbol-name sym)
      "-"
      (if (symbolp suffix)
        (symbol-name suffix)
        suffix))
    sym))
defpm-add-suffix-lstfunction
(defun defpm-add-suffix-lst
  (sym lst)
  (declare (xargs :guard (and (symbolp sym)
        (or (symbol-listp lst) (string-listp lst)))))
  (cond ((endp lst) nil)
    (t (cons (defpm-add-suffix sym (car lst))
        (defpm-add-suffix-lst sym (cdr lst))))))
defpm-make-suffix-lstfunction
(defun defpm-make-suffix-lst
  (str lst)
  (declare (xargs :guard (and (stringp str) (symbol-listp lst))))
  (cond ((endp lst) nil)
    (t (cons (concatenate 'string str (symbol-name (car lst)))
        (defpm-make-suffix-lst str (cdr lst))))))
defpm-make-callsfunction
(defun defpm-make-calls
  (fns formals)
  (declare (xargs :guard (and (symbol-listp fns) (symbol-listp formals))))
  (cond ((endp fns) nil)
    (t (cons (cons (car fns) formals)
        (defpm-make-calls (cdr fns) formals)))))
syntaxp-symbolp-lstfunction
(defun syntaxp-symbolp-lst
  (formals)
  (declare (xargs :guard (true-listp formals)))
  (cond ((endp formals) nil)
    (t (cons `(syntaxp (symbolp ,(CAR FORMALS)))
        (syntaxp-symbolp-lst (cdr formals))))))
other
(make-event (pprogn (f-put-global 'defpm-arithmetic-top-book
      (extend-pathname (cbd) "arithmetic-top-theory" state)
      state)
    (value '(value-triple :defpm-arithmetic-top-book-set)))
  :check-expansion t)
defpm-formfunction
(defun defpm-form
  (test steps measure terminates theory formals)
  (declare (xargs :guard (and (symbol-listp (list measure terminates theory))
        (or (symbolp steps) (symbol-listp steps))
        (symbol-listp formals)
        (not (intersectp-eq '(defpm-m defpm-n defpm-clk) formals)))))
  (let* ((steps (if (symbolp steps)
         (if (cdr formals)
           (defpm-add-suffix-lst steps formals)
           (list steps))
         steps)) (step-calls (defpm-make-calls steps formals))
      (measure-type (defpm-add-suffix measure "TYPE"))
      (terminates-base (defpm-add-suffix terminates "BASE"))
      (terminates-step (defpm-add-suffix terminates "STEP"))
      (terminates-step-commuted (defpm-add-suffix terminates "STEP-COMMUTED"))
      (measure-decreases (defpm-add-suffix measure "DECREASES")))
    `(encapsulate ((,MEASURE ,FORMALS t) (,TERMINATES ,FORMALS t))
      (local (make-event (list 'include-book (@ defpm-arithmetic-top-book))))
      (local (in-theory (theory 'arithmetic-top-theory)))
      (local (in-theory (enable natp)))
      (local (encapsulate nil
          (defun defpm-clk-rec
            (,@FORMALS n)
            (declare (xargs :measure (nfix n)))
            (cond ((,TEST ,@FORMALS) n)
              ((zp n) nil)
              (t (defpm-clk-rec ,@STEP-CALLS (1- n)))))
          (defchoose defpm-clk-wit
            (defpm-n)
            ,FORMALS
            (and (natp defpm-n) (defpm-clk-rec ,@FORMALS defpm-n)))
          (defun defpm-clk-bound
            ,FORMALS
            (let ((defpm-n (defpm-clk-wit ,@FORMALS)))
              (and (natp defpm-n)
                (defpm-clk-rec ,@FORMALS defpm-n)
                defpm-n)))
          (defun defpm-clk
            ,FORMALS
            (let ((defpm-n (defpm-clk-bound ,@FORMALS)))
              (and defpm-n (- defpm-n (defpm-clk-rec ,@FORMALS defpm-n)))))
          (defthm defpm-clk-rec-decreases
            (implies (natp defpm-n)
              (<= (defpm-clk-rec ,@FORMALS defpm-n) defpm-n))
            :rule-classes :linear)
          (defthm defpm-clk-0-implies-test-lemma-1
            (implies (and (natp defpm-n)
                (natp defpm-m)
                (defpm-clk-rec ,@FORMALS defpm-n)
                (defpm-clk-rec ,@FORMALS defpm-m))
              (equal (- defpm-n (defpm-clk-rec ,@FORMALS defpm-n))
                (- defpm-m (defpm-clk-rec ,@FORMALS defpm-m))))
            :rule-classes nil)
          (defthm defpm-clk-suff
            (implies (and (defpm-clk-rec ,@FORMALS defpm-n) (natp defpm-n))
              (defpm-clk ,@FORMALS))
            :hints (("Goal" :use defpm-clk-wit)))
          (defthm defpm-tailrec-lemma-1
            (implies (and (defpm-clk ,@FORMALS) (not (,TEST ,@FORMALS)))
              (defpm-clk ,@STEP-CALLS))
            :hints (("Goal" :in-theory (disable defpm-clk)
               :expand ((defpm-clk ,@FORMALS) (defpm-clk-rec ,@FORMALS (defpm-clk-wit ,@FORMALS))))))
          (defthm defpm-tailrec-lemma-2
            (implies (and (posp defpm-clk)
                (defpm-clk-rec ,@FORMALS (+ -1 defpm-clk)))
              (equal (defpm-clk-rec ,@FORMALS (+ -1 defpm-clk))
                (1- (defpm-clk-rec ,@FORMALS defpm-clk)))))
          (defthm defpm-tailrec-lemma-3
            (implies (and (posp defpm-clk)
                (defpm-clk-rec ,@FORMALS (+ -1 defpm-clk)))
              (defpm-clk-rec ,@FORMALS defpm-clk)))
          (defthm defpm-tailrec-lemma-4
            (implies (and (defpm-clk ,@FORMALS) (not (,TEST ,@FORMALS)))
              (< (defpm-clk ,@STEP-CALLS) (defpm-clk ,@FORMALS)))
            :hints (("Goal" :expand ((defpm-clk ,@FORMALS) (defpm-clk-rec ,@FORMALS (defpm-clk-wit ,@FORMALS)))
               :use ((:instance defpm-clk-0-implies-test-lemma-1
                  ,@(PAIRLIS$ FORMALS (PAIRLIS$ STEP-CALLS NIL))
                  (defpm-m (defpm-clk-wit ,@FORMALS))
                  (defpm-n (defpm-clk-wit ,@STEP-CALLS))))))
            :rule-classes :linear)
          (defthm defpm-terminates-step-lemma
            (implies (not (,TEST ,@FORMALS))
              (implies (defpm-clk ,@STEP-CALLS) (defpm-clk ,@FORMALS)))
            :hints (("Goal" :in-theory (disable defpm-clk-suff)
               :use ((:instance defpm-clk-suff
                  (defpm-n (1+ (defpm-clk-wit ,@STEP-CALLS)))))))
            :rule-classes nil)
          (defun ,MEASURE ,FORMALS (or (defpm-clk ,@FORMALS) 0))
          (defun ,TERMINATES ,FORMALS (and (defpm-clk ,@FORMALS) t))))
      (defthm ,TERMINATES-BASE
        (implies (,TEST ,@FORMALS) (,TERMINATES ,@FORMALS))
        :hints (("Goal" :use ((:instance defpm-clk-wit (defpm-n 0))))))
      (defthm ,TERMINATES-STEP
        (implies (not (,TEST ,@FORMALS))
          (equal (,TERMINATES ,@STEP-CALLS) (,TERMINATES ,@FORMALS)))
        :hints (("Goal" :in-theory (disable defpm-clk)
           :use defpm-terminates-step-lemma)))
      (defthmd ,TERMINATES-STEP-COMMUTED
        (implies (and ,@(SYNTAXP-SYMBOLP-LST FORMALS)
            (not (,TEST ,@FORMALS)))
          (equal (,TERMINATES ,@FORMALS) (,TERMINATES ,@STEP-CALLS)))
        :hints (("Goal" :in-theory (disable defpm-clk)
           :use defpm-terminates-step-lemma)))
      (defthm ,MEASURE-TYPE (o-p (,MEASURE ,@FORMALS)))
      (defthm ,MEASURE-DECREASES
        (implies (and (,TERMINATES ,@FORMALS) (not (,TEST ,@FORMALS)))
          (o< (,MEASURE ,@STEP-CALLS) (,MEASURE ,@FORMALS)))
        :hints (("Goal" :in-theory (disable defpm-clk))))
      ,@(AND THEORY
       `((DEFTHEORY ,THEORY
                    '(,TERMINATES-BASE ,TERMINATES-STEP ,MEASURE-TYPE
                      ,MEASURE-DECREASES)))))))
maybe-verbose-formfunction
(defun maybe-verbose-form
  (form verbose)
  (cond (verbose form)
    (t `(with-output :off :all :on (error) :gag-mode nil ,FORM))))
def-partial-measuremacro
(defmacro def-partial-measure
  (test step
    measure
    terminates
    theory
    &key
    (formals '(x))
    (verbose 'nil))
  (maybe-verbose-form (defpm-form test step measure terminates theory formals)
    verbose))
defpmmacro
(defmacro defpm
  (test step
    measure
    terminates
    theory
    &key
    (formals '(x) formals-p)
    (verbose 'nil verbose-p))
  `(def-partial-measure ,TEST
    ,STEP
    ,MEASURE
    ,TERMINATES
    ,THEORY
    ,@(AND FORMALS-P `(:FORMALS ,FORMALS))
    ,@(AND VERBOSE-P `(:VERBOSE ,VERBOSE))))
defpm-root-from-terminatesfunction
(defun defpm-root-from-terminates
  (terminates)
  (and (symbolp terminates)
    (let* ((term-string (symbol-name terminates)) (tsuff "-TERMINATES")
        (len-suff (length tsuff))
        (len-term (length term-string)))
      (cond ((and (< len-suff len-term)
           (equal (subseq term-string (- len-term len-suff) len-term)
             tsuff)) (intern-in-package-of-symbol (subseq term-string 0 (- len-term len-suff))
            terminates))
        (t nil)))))
defthm-domain-formfunction
(defun defthm-domain-form
  (thm-name root
    test
    step
    terminates
    measure
    domain-term
    formals)
  (let* ((test (or test (defpm-add-suffix root "TEST"))) (step (or step (defpm-add-suffix root "STEP")))
      (thm-name-fn (defpm-add-suffix thm-name "FN"))
      (thm-name-fn-main (defpm-add-suffix thm-name-fn "MAIN"))
      (thm-name-induction-hint (defpm-add-suffix thm-name "INDUCTION-HINT"))
      (steps (if (symbolp step)
          (if (cdr formals)
            (defpm-add-suffix-lst step formals)
            (list step))
          step))
      (step-calls (defpm-make-calls steps formals))
      (test-call (cons test formals))
      (terminates-base (defpm-add-suffix terminates "BASE"))
      (terminates-step (defpm-add-suffix terminates "STEP"))
      (domain-implies-terminates-base (defpm-add-suffix thm-name "BASE"))
      (domain-implies-terminates-step (defpm-add-suffix thm-name "STEP"))
      (step-preserves-domain (intern-in-package-of-symbol (concatenate 'string
            (symbol-name step)
            "-PRESERVES-"
            (symbol-name terminates))
          step)))
    `(encapsulate nil
      (local (progn (defun ,THM-NAME-FN
            ,FORMALS
            (implies ,DOMAIN-TERM (,TERMINATES ,@FORMALS)))
          (defun ,THM-NAME-INDUCTION-HINT
            ,FORMALS
            (declare (xargs :measure ,MEASURE
                :hints (("Goal" :in-theory (enable ,TEST ,@STEPS)))))
            (if (or (not ,DOMAIN-TERM) ,TEST-CALL)
              (list ,@FORMALS)
              (,THM-NAME-INDUCTION-HINT ,@STEP-CALLS)))
          (defthm ,DOMAIN-IMPLIES-TERMINATES-BASE
            (implies (or (not ,DOMAIN-TERM) ,TEST-CALL)
              (,THM-NAME-FN ,@FORMALS))
            :hints (("Goal" :in-theory (union-theories '(,THM-NAME-FN ,TERMINATES-BASE)
                 (theory 'minimal-theory)))))
          (defthm ,STEP-PRESERVES-DOMAIN
            (implies (and ,DOMAIN-TERM (not ,TEST-CALL))
              (let ,(PAIRLIS$ FORMALS (PAIRLIS$ STEP-CALLS NIL))
                (declare (ignorable ,@FORMALS))
                ,DOMAIN-TERM))
            :hints (("Goal" :in-theory (enable ,TEST ,@STEPS)))
            :rule-classes nil)
          (defthm ,DOMAIN-IMPLIES-TERMINATES-STEP
            (implies (and (not ,TEST-CALL) (,THM-NAME-FN ,@STEP-CALLS))
              (,THM-NAME-FN ,@FORMALS))
            :hints (("Goal" :use ,STEP-PRESERVES-DOMAIN
               :in-theory (union-theories '(,TERMINATES-STEP ,THM-NAME-FN)
                 (theory 'minimal-theory)))))
          (defthm ,THM-NAME-FN-MAIN
            (,THM-NAME-FN ,@FORMALS)
            :hints (("Goal" :induct (,THM-NAME-INDUCTION-HINT ,@FORMALS)
               :in-theory (union-theories '(,THM-NAME-INDUCTION-HINT ,DOMAIN-IMPLIES-TERMINATES-BASE
                   ,DOMAIN-IMPLIES-TERMINATES-STEP)
                 (theory 'minimal-theory))))
            :rule-classes nil)))
      (defthm ,THM-NAME
        ,(LET ((TERM (CONS TERMINATES FORMALS)))
   (IF (EQ DOMAIN-TERM T)
       TERM
       `(IMPLIES ,DOMAIN-TERM ,TERM)))
        :hints (("Goal" :use ,THM-NAME-FN-MAIN
           :in-theory (union-theories '(,THM-NAME-FN) (theory 'minimal-theory))))))))
defthm-domainmacro
(defmacro defthm-domain
  (thm-name form &key root test step measure verbose)
  (mv-let (domain-term terminates formals)
    (case-match form
      (('implies domain-term (terminates . formals)) (mv domain-term terminates formals))
      ((terminates . formals) (mv t terminates formals))
      (& (mv (er hard!
            'defthm-domain
            "Illegal form (see :doc defthm-domain):~|~x0"
            form)
          nil
          nil)))
    (let ((root (or root (defpm-root-from-terminates terminates))))
      (prog2$ (or root
          (and test step)
          (er hard
            'defthm-domain
            "Non-nil values are required in a defthm-domain either ~
                      for keyword :root or for for keywords :test and :step, ~
                      unless the termination predicate is a symbol whose name ~
                      ends in "-TERMINATES", for example, FOO-TERMINATES.  ~
                      The following form has termination predicate ~x0 is ~
                      thus illegal:~|~x1"
            terminates
            form))
        (maybe-verbose-form (defthm-domain-form thm-name
            root
            test
            step
            terminates
            measure
            domain-term
            formals)
          verbose)))))
local
(local (defmacro my-test
    (label &rest forms)
    `(encapsulate nil (deflabel ,LABEL) (local (progn ,@FORMS)))))
local
(local (my-test test1
    (defstub defpm-test (x) t)
    (defstub defpm-step (x) t)
    (defpm defpm-test
      defpm-step
      defpm-measure
      defpm-terminates
      defpm-theory)
    (defun defpm-tailrec
      (x)
      (declare (xargs :measure (defpm-measure x)
          :hints (("Goal" :in-theory (union-theories (theory 'defpm-theory)
               (theory 'minimal-theory))))))
      (if (defpm-terminates x)
        (if (defpm-test x)
          x
          (defpm-tailrec (defpm-step x)))
        x))))
local
(local (my-test test2
    (defun trfact-test
      (x acc)
      (declare (ignore acc))
      (equal x 0))
    (defun trfact-step-x (x acc) (declare (ignore acc)) (1- x))
    (defun trfact-step-acc (x acc) (* x acc))
    (defpm trfact-test
      trfact-step
      trfact-measure
      trfact-terminates
      trfact-theory
      :formals (x acc))
    (defun trfact-simple
      (x acc)
      (declare (xargs :measure (trfact-measure x acc)
          :hints (("Goal" :in-theory (union-theories (theory 'trfact-theory)
               (theory 'minimal-theory))))))
      (if (trfact-terminates x acc)
        (if (trfact-test x acc)
          acc
          (trfact-simple (trfact-step-x x acc)
            (trfact-step-acc x acc)))
        acc))
    (defun trfact
      (x acc)
      (declare (xargs :measure (trfact-measure x acc)
          :hints (("Goal" :use (trfact-measure-decreases trfact-measure-type)
             :in-theory (union-theories '(trfact-test trfact-step-x trfact-step-acc)
               (theory 'ground-zero))))))
      (if (trfact-terminates x acc)
        (if (equal x 0)
          acc
          (trfact (1- x) (* x acc)))
        acc))
    (defthm-domain trfact-terminates-holds-on-natp
      (implies (natp x) (trfact-terminates x acc))
      :test trfact-test
      :step trfact-step
      :measure (nfix x))
    (encapsulate nil
      (set-enforce-redundancy t)
      (defthm trfact-terminates-holds-on-natp
        (implies (natp x) (trfact-terminates x acc))))))
local
(local (my-test test3
    (defund fact-test (x) (equal x 0))
    (defund fact-step (x) (1- x))
    (defpm fact-test
      fact-step
      fact-measure
      fact-terminates
      fact-theory)
    (defun fact
      (x)
      (declare (xargs :measure (fact-measure x)
          :hints (("Goal" :in-theory (union-theories (theory 'fact-theory)
               (theory 'minimal-theory))))))
      (if (fact-terminates x)
        (if (fact-test x)
          1
          (* x (fact (fact-step x))))
        1))
    (defthm-domain fact-terminates-holds-on-natp
      (implies (natp x) (fact-terminates x))
      :measure (nfix x))
    (encapsulate nil
      (set-enforce-redundancy t)
      (defthm fact-terminates-holds-on-natp
        (implies (natp x) (fact-terminates x))))
    (defthm-domain fact-terminates-holds-on-natp
      (implies (natp x) (fact-terminates x))
      :root fact
      :measure (nfix x))
    (defthm fact-def
      (implies (force (natp x))
        (equal (fact x)
          (if (fact-test x)
            1
            (* x (fact (fact-step x))))))
      :hints (("Goal" :in-theory (union-theories '(fact fact-terminates-holds-on-natp)
           (theory 'minimal-theory))))
      :rule-classes :definition)))
local
(local (my-test test4
    (defund fact2-test
      (x)
      (declare (xargs :guard t))
      (equal x 0))
    (defund fact2-step
      (x)
      (declare (xargs :guard (natp x)))
      (1- x))
    (defpm fact2-test
      fact2-step
      fact2-measure
      fact2-terminates
      fact2-theory)
    (defthm-domain fact2-terminates-holds-on-natp
      (implies (natp x) (fact2-terminates x))
      :root fact2
      :measure (nfix x))
    (defthmd natp-fact2-step
      (implies (and (not (fact2-test x)) (natp x))
        (natp (fact2-step x)))
      :hints (("Goal" :in-theory (enable fact2-test fact2-step))))
    (defun fact2
      (x)
      (declare (xargs :guard (natp x)
          :measure (fact2-measure x)
          :guard-hints (("Goal" :in-theory (union-theories '(natp-fact2-step fact2-terminates-holds-on-natp)
               (union-theories (theory 'fact2-theory)
                 (theory 'minimal-theory)))))
          :hints (("Goal" :in-theory (union-theories (theory 'fact2-theory)
               (theory 'minimal-theory))))))
      (mbe :logic (if (fact2-terminates x)
          (if (fact2-test x)
            1
            (* x (fact2 (fact2-step x))))
          1)
        :exec (if (fact2-test x)
          1
          (* x (fact2 (fact2-step x))))))))