summaryrefslogtreecommitdiff
path: root/books/tau
diff options
context:
space:
mode:
authorCamm Maguire <camm@debian.org>2017-05-08 12:58:52 -0400
committerCamm Maguire <camm@debian.org>2017-05-08 12:58:52 -0400
commit092176848cbfd27b96c323cc30c54dff4c4a6872 (patch)
tree91b91b4db76805fd2a09de0745b22080a9ebd335 /books/tau
Import acl2_7.4dfsg.orig.tar.gz
[dgit import orig acl2_7.4dfsg.orig.tar.gz]
Diffstat (limited to 'books/tau')
-rw-r--r--books/tau/bounders/elementary-bounders.acl24
-rw-r--r--books/tau/bounders/elementary-bounders.lisp3516
-rw-r--r--books/tau/bounders/find-maximal-1d.lisp96
-rw-r--r--books/tau/bounders/find-maximal-2d.lisp204
-rw-r--r--books/tau/bounders/find-minimal-1d.lisp128
-rw-r--r--books/tau/bounders/find-minimal-2d.lisp235
-rw-r--r--books/tau/bounders/tau-bounders-tests.lisp16
7 files changed, 4199 insertions, 0 deletions
diff --git a/books/tau/bounders/elementary-bounders.acl2 b/books/tau/bounders/elementary-bounders.acl2
new file mode 100644
index 0000000..1a38876
--- /dev/null
+++ b/books/tau/bounders/elementary-bounders.acl2
@@ -0,0 +1,4 @@
+#+acl2-par
+(set-waterfall-parallelism t)
+
+(certify-book "elementary-bounders")
diff --git a/books/tau/bounders/elementary-bounders.lisp b/books/tau/bounders/elementary-bounders.lisp
new file mode 100644
index 0000000..0bcbfa1
--- /dev/null
+++ b/books/tau/bounders/elementary-bounders.lisp
@@ -0,0 +1,3516 @@
+; Copyright (C) 2015, ForrestHunt, Inc.
+; Written by J Strother Moore, December, 2012
+; License: A 3-clause BSD license. See the LICENSE file distributed with ACL2.
+
+; (certify-book "elementary-bounders")
+
+; Tau Interval Functions for Basic Arithmetic
+; and Proofs of Their Correctness
+
+; J Strother Moore
+; Edinburgh, December, 2012
+
+; Acknowledgements: Since its release some additions and improvements were made
+; to this book by Dmitry Nadezhin. Thanks!
+
+; Abstract
+
+; Consider (+ x y) and suppose we know that the inputs, x and y, are contained
+; in two intervals, int1 and int2. We wish to compute from int1 and int2 an
+; interval containing (+ x y). For example, if x is known to be an INTEGERP
+; such that 1 <= x <= 16 and y is a RATIONALP such that 0 < y (with no known
+; upper bound), then (+ x y) is known to be in the RATIONALP interval such that
+; 1 < (+ x y) (with no known upper bound). I call the function that takes two
+; intervals containing x and y respectively and that returns an interval
+; containing (fn x y) a ``bounder'' for fn.
+;
+; I have developed a book that defines bounders for +, *, -, /, FLOOR, MOD,
+; LOGAND, LOGNOT, LOGIOR, LOGORC1, LOGEQV, LOGXOR, EXPT2, and ASH. The book
+; proves the correctness of each bounder. In future work I hope to extend ACL2
+; so that users can define and verify bounders to improve the tau system. The
+; present work is an exploration of the shape and content of such bounder
+; theorems. Ideally, the present book would just become part of an arithmetic
+; package and not be built into ACL2.
+
+; Our bounder definitions (and hence their correctness proofs) fall into one of
+; two different styles. The first definitional style is just straightforward
+; case analysis and arithmetic and so the proof follows the usual ACL2 style of
+; definitional expansion and use of the arithmetic-5 library. This first style
+; of proof can generate a lot of cases. For example, the proof that the
+; bounder for products is correct generates over 40,000 subgoals. This is due
+; to a variety of issues including the myriad combinations of choices for the
+; intervals containing the inputs x and y (e.g., are they open or closed,
+; finite or not, over what domains?), as well as the case analysis necessary to
+; compute the bounds. See the definition of tau-bounder-* below.
+
+; In the second style, the bounders are defined in terms of previously verified
+; bounders. For example,
+
+; (LOGIOR X Y) = (LOGNOT (LOGAND (LOGNOT X) (LOGNOT Y)))
+
+; Thus, we can bound LOGIOR by composing already verified bounders and we can
+; verify the LOGIOR bounder by exploiting the proved properties of those
+; earlier bounders. However, making this second proof style work imposes
+; additional constraints on the correctness theorems and how they are stored as
+; rules.
+
+; The book concludes with an essay On the Accuracy of the LOGAND Bounder.
+
+; Some Technical Details
+
+; In this work, an interval is a data structure, constructed by make-tau-interval,
+; containing five fields that are named the ``domain,'' the ``lower bound
+; relation,'' the ``lower bound,'' the ``upper bound relation,'' and the
+; ``upper bound.'' These fields are accessed by interval-dom, interval-lo-rel,
+; interval-lo, interval-hi-rel, and interval-hi. The actual shape of the data
+; structure is the same as for a tau-interval in the ACL2 source code:
+
+; (defrec tau-interval (domain (lo-rel . lo) . (hi-rel . hi)) t).
+
+; However, in the source code tau-intervals are constructed by the macro make
+; and accessed by the macro access, which expand to raw conses, cars, and cdrs
+; for efficiency. I chose to introduce a named constructor and accessors to
+; make it easier to hang lemmas on them during correctness proofs for bounders.
+; But the named functions work on exactly the same data structures and can be
+; applied to build and access ACL2's tau-intervals.
+
+; An interval domain must be one of INTEGERP, RATIONALP, ACL2-NUMBERP, or nil,
+; the last indicating that there is no restriction. An interval relation is a
+; Boolean, where nil means the relation is ``<='' and t means the relation is
+; ``<.'' That is, nil means ``weak inequality'' and t means ``strong
+; inequality.'' The upper and lower bounds are either nil, meaning no such
+; bound is known, or an explicit rational. Symbolic bounds are not allowed.
+; For example,
+
+; (make-tau-interval 'RATIONALP t -1/2 nil 1)
+
+; denotes the interval containing x such that
+
+; (rationalp x) & -1/2 < x <= 1.
+
+; There are two other constraints on well-formed interval. First, if the two
+; bounds, lo and hi, of an interval are non-nil, then lo <= hi. Second, if the
+; domain of an interval is INTEGERP then the relations must be weak and the
+; bounds must be integers. That is, if x is known to be an INTEGERP such that
+; 1/2 < x < 11/2 then instead of creating the (illegal) interval
+
+; (make-tau-interval 'INTEGERP T 1/2 T 11/2)
+
+; one must create
+
+; (make-tau-interval 'INTEGERP NIL 1 NIL 5)
+
+; Of course, (make-tau-interval 'RATIONALP T 1/2 T 11/2) is a perfectly legal
+; interval.
+
+; These constraints are formalized in the predicate intervalp below.
+
+; To say that x is ``in'' an interval, int, is (in-tau-intervalp x int), formalized
+; below.
+
+; The correctness theorem for the bounder function for addition, which is
+; called tau-bounder-+, is given below and suggests the shape of all bounder
+; correctness theorems here.
+
+; (implies (and (tau-intervalp int1)
+; (tau-intervalp int2)
+; (or (equal (tau-interval-dom int1) 'integerp)
+; (equal (tau-interval-dom int1) 'rationalp))
+; (or (equal (tau-interval-dom int2) 'integerp)
+; (equal (tau-interval-dom int2) 'rationalp))
+; (in-tau-intervalp x int1)
+; (in-tau-intervalp y int2))
+; (and (tau-intervalp (tau-bounder-+ int1 int2))
+; (in-tau-intervalp (+ x y) (tau-bounder-+ int1 int2))
+; ))
+
+; We envision this being used as follows: when ACL2 is asked to compute the tau
+; of (+ x y) it first computes the tau of x and the tau of y. These tau will
+; include (possibly trivially unrestrictive) intervals, say int1 and int2. An
+; invariant of the ACL2 code is that the computed int1 and int2 are well-formed
+; intervals that contain the values of x and y respectively. Thus, the first
+; two and last two hypotheses of the correctness theorem are assured. Next,
+; ACL2 should check that the domains of the two computed intervals are each
+; either INTEGERP or RATIONALP, which if true satisfies the middle two
+; hypotheses. Thus, ACL2 can then run (tau-bounder-+ int1 int2) and know that
+; it returns a well-formed interval that contains the value of (+ x y). This
+; allows it to include that newly computed interval into the tau being computed
+; for (+ x y).
+
+; In order to use such correctness theorems in the proofs of other such correctness
+; theorems, it is necessary (a) to code the theorems (mainly) as :forward-chaining
+; rules, and (b) include in the correctness theorem one additional conclusion, namely
+
+; (implies (not (equal (tau-interval-dom (tau-bounder-+ int1 int2)) 'integerp))
+; (equal (tau-interval-dom (tau-bounder-+ int1 int2)) 'rationalp)).
+
+; This additional conclusion, which is not necessary for the imagined use of
+; TAU-BOUNDER-+, allows the facts proved about TAU-BOUNDER-+ to be sufficient
+; to relieve the hypotheses of other correctness results.
+
+; The reason we code these correctness results mainly as :forward-chaining
+; rules has to do with free variables. The bounder for LOGIOR is
+; a composition of the bounders for LOGNOT and LOGAND because:
+
+; (LOGIOR X Y) = (LOGNOT (LOGAND (LOGNOT X) (LOGNOT Y)))
+
+; The correctness of the LOGIOR bounder will introduce:
+
+; (TAU-BOUNDER-LOGNOT
+; (TAU-BOUNDER-LOGAND
+; (TAU-BOUNDER-LOGNOT INT1)
+; (TAU-BOUNDER-LOGNOT INT2)))
+
+; and all we will know is that X is in INT1 and Y is INT2. By forward chaining
+; from those two facts, triggered by the presence in the goal of the trigger
+; terms (TAU-BOUNDER-LOGNOT INT2) and (TAU-BOUNDER-LOGAND ...) we can infer:
+
+; (IN-TAU-INTERVALP (LOGNOT X) (TAU-BOUNDER-LOGNOT INT1))
+
+; (IN-TAU-INTERVALP (LOGNOT Y) (TAU-BOUNDER-LOGNOT INT2))
+
+; from one round of forward chaining, and then:
+
+; (IN-TAU-INTERVALP (LOGAND (LOGNOT X) (LOGNOT Y))
+; (TAU-BOUNDER-LOGAND
+; (TAU-BOUNDER-LOGNOT INT1)
+; (TAU-BOUNDER-LOGNOT INT2)))
+
+; from a second and then
+
+; (IN-TAU-INTERVALP (LOGNOT (LOGAND (LOGNOT X) (LOGNOT Y)))
+; (TAU-BOUNDER-LOGNOT
+; (TAU-BOUNDER-LOGAND
+; (TAU-BOUNDER-LOGNOT INT1)
+; (TAU-BOUNDER-LOGNOT INT2))))
+
+; from a third. We discuss the decision to use forward-chaining further in the
+; proof of tau-bounder-floor-correct.
+
+; -----------------------------------------------------------------
+
+(in-package "ACL2")
+
+; cert_param: (pcert=1)
+
+(local (include-book "arithmetic-5/top" :dir :system))
+
+(local (deftheory jared-disables
+ '(;; Things I think are slow in arith-5
+ |(equal (if a b c) x)|
+ |(equal x (if a b c))|
+ |(+ x (if a b c))|
+
+ SIMPLIFY-PRODUCTS-GATHER-EXPONENTS-<
+ (:T EXPT-TYPE-PRESCRIPTION-INTEGERP-BASE)
+ (:T EXPT-TYPE-PRESCRIPTION-INTEGERP-BASE-A)
+ (:T EXPT-TYPE-PRESCRIPTION-NONPOSITIVE-BASE-ODD-EXPONENT)
+ (:T EXPT-TYPE-PRESCRIPTION-NONPOSITIVE-BASE-EVEN-EXPONENT)
+ (:T EXPT-TYPE-PRESCRIPTION-NEGATIVE-BASE-ODD-EXPONENT)
+ (:T EXPT-TYPE-PRESCRIPTION-NEGATIVE-BASE-EVEN-EXPONENT)
+ (:T EXPT-TYPE-PRESCRIPTION-INTEGERP-BASE-B)
+ (:T EXPT-TYPE-PRESCRIPTION-RATIONALP-BASE)
+ (:T EXPT-TYPE-PRESCRIPTION-NON-0-BASE)
+ (:T NOT-INTEGERP-3B)
+ (:T NOT-INTEGERP-1B)
+ (:T NOT-INTEGERP-2B)
+ (:T NOT-INTEGERP-4E)
+ (:T NOT-INTEGERP-4B)
+ (:T NOT-INTEGERP-4B-EXPT)
+ (:T NOT-INTEGERP-3B-EXPT)
+ (:T NOT-INTEGERP-2B-EXPT)
+ (:T NOT-INTEGERP-1B-EXPT)
+ (:T RATIONALP-EXPT-TYPE-PRESCRIPTION)
+ RATIONALP-X
+ acl2-numberp-x
+
+ not-integerp-1a
+ not-integerp-2a
+ not-integerp-3a
+ not-integerp-4a
+ not-integerp-1d
+ not-integerp-2d
+ not-integerp-3d
+ not-integerp-4d
+ not-integerp-1f
+ not-integerp-2f
+ not-integerp-3f
+ not-integerp-4f
+
+ default-times-1
+ default-times-2
+ default-less-than-1
+ default-less-than-2
+ default-car
+ default-cdr
+
+ )))
+
+(local (in-theory (disable jared-disables)))
+
+(local (SET-DEFAULT-HINTS
+ '((NONLINEARP-DEFAULT-HINT++ ID
+ STABLE-UNDER-SIMPLIFICATIONP HIST NIL)
+ (and stable-under-simplificationp
+ (not (cw "Jared-hint: re-enabling slow rules.~%"))
+ '(:in-theory (enable jared-disables))))))
+
+
+; Note: I tried the simpler nonlinear hint:
+; (set-default-hints '((nonlinearp-default-hint
+; stable-under-simplificationp
+; hist
+; pspv)))
+; but it didn't suffice for the tau-bounder-floor-correct theorem.
+
+
+; -----------------------------------------------------------------
+; Basic Abstract Properties of the Interval Functions
+
+
+(local
+ (defthm abstract-def-<?
+ (implies (implies (and x y)
+ (or (real/rationalp x)
+ (real/rationalp y)))
+ (iff (<? rel x y)
+ (if (or (null x)
+ (null y))
+ t
+ (if rel (< x y) (<= x y)))))
+ :hints
+ (("Goal"
+ :use ((:instance completion-of-< (x x) (y y))
+ (:instance completion-of-< (x y) (y x)))))
+ :rule-classes
+ ((:rewrite :corollary
+ (implies (real/rationalp x)
+ (iff (<? rel x y)
+ (if (or (null x)
+ (null y))
+ t
+ (if rel (< x y) (<= x y))))))
+ (:rewrite :corollary
+ (implies (real/rationalp y)
+ (iff (<? rel x y)
+ (if (or (null x)
+ (null y))
+ t
+ (if rel (< x y) (<= x y))))))
+ (:rewrite :corollary
+ (implies (null x)
+ (iff (<? rel x y)
+ (if (or (null x)
+ (null y))
+ t
+ (if rel (< x y) (<= x y))))))
+ (:rewrite :corollary
+ (implies (null y)
+ (iff (<? rel x y)
+ (if (or (null x)
+ (null y))
+ t
+ (if rel (< x y) (<= x y)))))))))
+
+
+(local
+ (in-theory (disable <?)))
+
+(local
+ (defthm interval-accessors
+ (and (equal (tau-interval-dom (make-tau-interval dom lo-rel lo hi-rel hi))
+ dom)
+ (equal (tau-interval-lo-rel (make-tau-interval dom lo-rel lo hi-rel hi))
+ lo-rel)
+ (equal (tau-interval-lo (make-tau-interval dom lo-rel lo hi-rel hi))
+ lo)
+ (equal (tau-interval-hi-rel (make-tau-interval dom lo-rel lo hi-rel hi))
+ hi-rel)
+ (equal (tau-interval-hi (make-tau-interval dom lo-rel lo hi-rel hi))
+ hi))))
+
+(local
+ (defthm intervalp-rules
+ (implies (tau-intervalp int)
+ (and
+ (implies (equal (tau-interval-dom int) 'integerp)
+ (and (equal (tau-interval-lo-rel int) nil)
+ (equal (tau-interval-hi-rel int) nil)
+ (equal (integerp (tau-interval-lo int))
+ (if (tau-interval-lo int) t nil))
+ (equal (integerp (tau-interval-hi int))
+ (if (tau-interval-hi int) t nil))
+ ))
+ (booleanp (tau-interval-lo-rel int))
+ (booleanp (tau-interval-hi-rel int))
+ ;;(implies (tau-interval-lo int) (rationalp (tau-interval-lo int)))
+ (equal (rationalp (tau-interval-lo int))
+ (if (tau-interval-lo int) t nil))
+ ;; (implies (tau-interval-hi int) (rationalp (tau-interval-hi int)))
+ (equal (rationalp (tau-interval-hi int))
+ (if (tau-interval-hi int) t nil))
+ (implies (and (tau-interval-lo int)
+ (tau-interval-hi int))
+ (<= (tau-interval-lo int)
+ (tau-interval-hi int)))))
+ :rule-classes
+ ((:rewrite
+ :corollary
+ (implies (tau-intervalp int)
+ (and (equal (rationalp (tau-interval-lo int))
+ (if (tau-interval-lo int) t nil))
+ (equal (rationalp (tau-interval-hi int))
+ (if (tau-interval-hi int) t nil)))))
+ (:rewrite
+ :corollary
+ (implies (and (equal (tau-interval-dom int) 'integerp)
+ (tau-intervalp int))
+ (and (equal (tau-interval-lo-rel int) nil)
+ (equal (tau-interval-hi-rel int) nil)
+ (equal (integerp (tau-interval-lo int))
+ (if (tau-interval-lo int) t nil))
+ (equal (integerp (tau-interval-hi int))
+ (if (tau-interval-hi int) t nil)))))
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int)
+ (equal (tau-interval-dom int) 'integerp)
+ (tau-interval-lo int))
+ (integerp (tau-interval-lo int))))
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int)
+ (equal (tau-interval-dom int) 'integerp)
+ (tau-interval-hi int))
+ (integerp (tau-interval-hi int))))
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int)
+ (tau-interval-lo int))
+ (rationalp (tau-interval-lo int))))
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int)
+ (tau-interval-hi int))
+ (rationalp (tau-interval-hi int))))
+
+ ;; Jared: removing these since they seem expensive and bad
+ ;; (:type-prescription
+ ;; :corollary
+ ;; (implies (tau-intervalp int) (booleanp (tau-interval-lo-rel int))))
+ ;; (:type-prescription
+ ;; :corollary
+ ;; (implies (tau-intervalp int) (booleanp (tau-interval-hi-rel int))))
+
+ (:linear
+ :corollary
+ (implies (and (tau-intervalp int)
+ (tau-interval-lo int)
+ (tau-interval-hi int))
+ (<= (tau-interval-lo int)
+ (tau-interval-hi int)))))))
+
+(local
+ (defthm in-tau-intervalp-rules
+ (and
+ (implies (and (tau-intervalp int)
+ (in-tau-intervalp x int)
+ (eq (tau-interval-dom int) 'integerp))
+ (integerp x))
+ (implies (and (tau-intervalp int)
+ (in-tau-intervalp x int)
+ (eq (tau-interval-dom int) 'rationalp))
+ (rationalp x))
+ (implies (and (tau-intervalp int)
+ (in-tau-intervalp x int)
+ (eq (tau-interval-dom int) 'acl2-numberp))
+ (acl2-numberp x))
+ (implies (and (tau-intervalp int)
+ (in-tau-intervalp x int)
+ x
+ (tau-interval-lo-rel int)
+ (tau-interval-lo int))
+ (< (tau-interval-lo int) x))
+ (implies (and (tau-intervalp int)
+ (in-tau-intervalp x int)
+ x
+ (not (tau-interval-lo-rel int))
+ (tau-interval-lo int))
+ (<= (tau-interval-lo int) x))
+ (implies (and (tau-intervalp int)
+ (in-tau-intervalp x int)
+ x
+ (tau-interval-hi-rel int)
+ (tau-interval-hi int))
+ (< x (tau-interval-hi int)))
+ (implies (and (tau-intervalp int)
+ (in-tau-intervalp x int)
+ (not (tau-interval-hi-rel int))
+ x
+ (tau-interval-hi int))
+ (<= x (tau-interval-hi int))))
+ :rule-classes
+ ((:forward-chaining
+ :corollary
+ (implies (and (in-tau-intervalp x int)
+ (tau-intervalp int)
+ (eq (tau-interval-dom int) 'integerp))
+ (integerp x)))
+ (:forward-chaining
+ :corollary
+ (implies (and (in-tau-intervalp x int)
+ (tau-intervalp int)
+ (eq (tau-interval-dom int) 'rationalp))
+ (rationalp x)))
+ (:forward-chaining
+ :corollary
+ (implies (and (in-tau-intervalp x int)
+ (tau-intervalp int)
+ (eq (tau-interval-dom int) 'acl2-numberp))
+ (acl2-numberp x)))
+ (:linear
+ :corollary
+ (implies (and (in-tau-intervalp x int)
+ x
+ (tau-intervalp int)
+ (tau-interval-lo-rel int)
+ (tau-interval-lo int))
+ (< (tau-interval-lo int) x)))
+ (:linear ; forward-chaining
+ :corollary
+ (implies (and (in-tau-intervalp x int)
+ x
+ (tau-intervalp int)
+ (tau-interval-lo int))
+ (<= (tau-interval-lo int) x)))
+ (:linear ; forward-chaining
+ :corollary
+ (implies (and (in-tau-intervalp x int)
+ x
+ (tau-intervalp int)
+ (tau-interval-hi-rel int)
+ (tau-interval-hi int))
+ (< x (tau-interval-hi int))))
+ (:linear ; forward-chaining
+ :corollary
+ (implies (and (in-tau-intervalp x int)
+ x
+ (tau-intervalp int)
+ (tau-interval-hi int))
+ (<= x (tau-interval-hi int)))))))
+
+(local
+ (defthm intervalp-make-tau-interval
+ (equal (tau-intervalp (make-tau-interval dom lo-rel lo hi-rel hi))
+ (cond ((eq dom 'integerp)
+ (and (null lo-rel)
+ (null hi-rel)
+ (if lo
+ (and (integerp lo)
+ (if hi (and (integerp hi) (<= lo hi))
+ t))
+ (if hi (integerp hi) t))))
+ (t (and (member dom '(rationalp acl2-numberp nil))
+ (booleanp lo-rel)
+ (booleanp hi-rel)
+ (if lo
+ (and (rationalp lo)
+ (if hi (and (rationalp hi) (<= lo hi))
+ t))
+ (if hi (rationalp hi) t))))))))
+
+(local
+ (defthm in-tau-intervalp-make-tau-interval
+ (equal (in-tau-intervalp x (make-tau-interval dom lo-rel lo hi-rel hi))
+ (and (tau-interval-domainp dom x)
+ (<? lo-rel lo (fix x))
+ (<? hi-rel (fix x) hi)))))
+
+(local
+ (in-theory (disable make-tau-interval
+ tau-interval-dom tau-interval-lo-rel tau-interval-lo
+ tau-interval-hi-rel tau-interval-hi)))
+
+
+
+; -----------------------------------------------------------------
+; BINARY-+
+
+(defun domain-of-binary-arithmetic-function (domx domy)
+ (declare (xargs :guard (and (symbolp domx) (symbolp domy))))
+
+; In this function, domx and domy are domain names as found in tau-bounders,
+; e.g., INTEGERP, RATIONALP, ACL2-NUMBERP, or NIL. The function returns the
+; the domain of such generic binary arithmetic functions as sum, product, or
+; mod. Note that arithmetic functions always return (at least) ACL2-NUMBERPs.
+; So we never return NIL.
+
+ (cond ((eq domx 'integerp)
+ (cond ((eq domy 'integerp) 'integerp)
+ ((eq domy 'rationalp) 'rationalp)
+ (t 'acl2-numberp)))
+ ((eq domx 'rationalp)
+ (cond ((or (eq domy 'integerp)
+ (eq domy 'rationalp))
+ 'rationalp)
+ (t 'acl2-numberp)))
+ (t 'acl2-numberp)))
+
+(defun bounds-of-sum (lo-rel1 lo1 hi-rel1 hi1 lo-rel2 lo2 hi-rel2 hi2)
+ (declare (xargs :guard (and (booleanp lo-rel1)
+ (or (null lo1) (rationalp lo1))
+ (booleanp hi-rel1)
+ (or (null hi1) (rationalp hi1))
+ (booleanp lo-rel2)
+ (or (null lo2) (rationalp lo2))
+ (booleanp hi-rel2)
+ (or (null hi2) (rationalp hi2)))))
+
+; Suppose x and y are terms and that it is known that lo1 <? x <? hi1 and lo2
+; <? y <? hi2, where the relations denoted by ``<?'' are controlled by the four
+; respective ``rel'' flags. We seek bounds (mv lo lo-rel hi hi-rel) on the sum
+; (+ x y). In the ideal case where all the intervals are bounded by finite
+; rationals, the answer would be:
+
+; (mv (or lo-rel1 lo-rel2) (+ lo1 lo2) (or hi-rel1 hi-rel2) (+ hi1 hi2)).
+
+; In the case of sum, the upper and lower bounds can be computed independently.
+; But to make this function similar to the function for computing the bounds of
+; a product -- where, for example, the lower bounds of the factors can
+; influence the upper bound of the product -- we take all eight parameters and
+; compute all four results in one function.
+
+; Complicating this function is the requirement that we must allow for
+; infinities. Thus, for example, if lo2 is nil, we have ``-infinity <= y''.
+; With no lower bound on y, there can be no lower bound on (+ x y). Finally,
+; recall our convention that when the bound is infinite, we use the weak
+; relation (rel = nil) by default.
+
+ (mv-let
+ (lo-rel lo)
+ (if (and lo1 lo2)
+ (mv (or lo-rel1 lo-rel2) (+ lo1 lo2))
+ (mv nil nil))
+ (mv-let
+ (hi-rel hi)
+ (if (and hi1 hi2)
+ (mv (or hi-rel1 hi-rel2) (+ hi1 hi2))
+ (mv nil nil))
+ (mv lo-rel lo hi-rel hi))))
+
+(defun tau-bounder-+ (int1 int2)
+ (declare (xargs :guard (and (tau-intervalp int1) (tau-intervalp int2))))
+ (let ((dom (domain-of-binary-arithmetic-function
+ (tau-interval-dom int1)
+ (tau-interval-dom int2))))
+ (mv-let (lo-rel lo hi-rel hi)
+ (bounds-of-sum (tau-interval-lo-rel int1)
+ (tau-interval-lo int1)
+ (tau-interval-hi-rel int1)
+ (tau-interval-hi int1)
+ (tau-interval-lo-rel int2)
+ (tau-interval-lo int2)
+ (tau-interval-hi-rel int2)
+ (tau-interval-hi int2))
+ (make-tau-interval dom lo-rel lo hi-rel hi))))
+
+(defthm tau-bounder-+-correct
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-+ int1 int2))
+ (in-tau-intervalp (+ x y) (tau-bounder-+ int1 int2))
+ ))
+ :rule-classes
+ ((:rewrite)
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-+ int1 int2))
+ (in-tau-intervalp (+ x y) (tau-bounder-+ int1 int2))))
+ :trigger-terms ((tau-bounder-+ int1 int2)))
+ ))
+
+; -----------------------------------------------------------------
+; BINARY-*
+
+(defund extended-rationalp (x)
+ (declare (xargs :guard t))
+ (or (equal x nil)
+ (equal x t)
+ (real/rationalp x)))
+
+(local (defthm |extended-rationalp is rationalp when not nil or t|
+ (implies
+ (and (extended-rationalp x) (not (equal x nil)) (not (equal x t)))
+ (real/rationalp x))
+ :hints (("Goal" :in-theory (enable extended-rationalp)))))
+
+(local (defthm |rationalp is extended-rationalp|
+ (implies (real/rationalp x) (extended-rationalp x))
+ :hints (("Goal" :in-theory (enable extended-rationalp)))))
+
+(local (defthm |rationalp when acl2-numberp and extended-rationalp|
+ (implies
+ (and (acl2-numberp x) (extended-rationalp x))
+ (real/rationalp x))))
+
+; I now implement ``the bounds of the product are the products of the bounds.''
+; But this is INSANELY more complicated and depends on the signs of the
+; intervals, whether they are strict or non-strict, and whether they contain
+; zero. For example, if both intervals are strictly negative (the upper bounds
+; are negative), then the product of their lower bounds is the upper bound of
+; their product, and vice versa. And if one of the intervals is negative and
+; the other is positive, the product of the positive upper bound and the
+; negative lower bound is the lower bound of the product, and vice versa.
+; After working out a few cases, we asked Robert Krug for an exhaustive case
+; analysis which he provided in the form of a theorem for products akin to that
+; shown above for sums, except instead of dealing with intervals as objects it
+; just returned the two bounding rationals and the two relations. I believe
+; Robert worked most of this out while developing the non-linear features of
+; the community books in arithmetic-5/. Thank you Robert!
+
+(defun bounds-of-product-< (x y)
+ (declare (xargs :guard (and (extended-rationalp x) (extended-rationalp y))))
+
+; X and y are either rationals or nil or t, where nil represents negative
+; infinity and t represents positive infinity. We determine whether x < y, and
+; we (rather oddly) say that x < x, when x is either infinity.
+
+ (if (null x)
+ t
+ (if (eq x t)
+ (if (eq y t)
+ t
+ nil)
+ (if (null y)
+ nil
+ (if (eq y t)
+ t
+ (< x y))))))
+
+(defun bounds-of-product-<= (x y)
+ (declare (xargs :guard (and (extended-rationalp x) (extended-rationalp y))))
+
+; X and y are either rationals or nil or t, where nil represents negative
+; infinity and t represents positive infinity. We determine whether x <= y.
+
+ (if (null x)
+ t
+ (if (eq x t)
+ (if (eq y t)
+ t
+ nil)
+ (if (null y)
+ nil
+ (if (eq y t)
+ t
+ (<= x y))))))
+
+(defun bounds-of-product-* (x y)
+ (declare (xargs :guard (and (extended-rationalp x) (extended-rationalp y))))
+
+; X and y are either rationals or nil or t, where nil represents negative
+; infinity and t represents positive infinity. We compute (* x y).
+
+ (if (or (eql x 0)
+ (eql y 0))
+ 0
+ (if (or (null x)
+ (eq x t)
+ (null y)
+ (eq y t))
+ (let ((signx (cond ((null x) nil)
+ ((eq x t) t)
+ ((< x 0) nil)
+ (t t)))
+ (signy (cond ((null y) nil)
+ ((eq y t) t)
+ ((< y 0) nil)
+ (t t))))
+ (eq signx signy))
+ (* x y))))
+
+(defun bounds-of-product1 (lo-rel1 lo1 hi-rel1 hi1 lo-rel2 lo2 hi-rel2 hi2)
+ (declare (xargs :guard (and (booleanp lo-rel1)
+ (extended-rationalp lo1)
+ (booleanp hi-rel1)
+ (extended-rationalp hi1)
+ (booleanp lo-rel2)
+ (extended-rationalp lo2)
+ (booleanp hi-rel2)
+ (extended-rationalp hi2))))
+
+; Warning: This function uses nil and t respectively to represent negative and
+; positive infinity. Except for this function and the three extended
+; arithmetic operators above, all other functions in the tau system obey the
+; convention that both infinities are represented by nil.
+
+; Given the different representations of the infinities, assume that lo1 <? x
+; <? hi1 and lo2 <? y <? hi2, where the ``<?'' relations are controlled,
+; respectively, by lo-rel1, hi-rel1, lo-rel2, and hi-rel2. We wish to
+; determine bounds on (* x y). We return (mv lo-rel lo hi-rel hi).
+
+; If you replace bounds-of-product-<, bounds-of-product-<=, and
+; bounds-of-product-* by <, <=, and * respectively below, you get a version of
+; this function that works when all the bounds are finite. Inspection shows
+; that this is still a very messy function! Trying to handle the infinities by
+; case splitting is daunting. (Abstractly, one could specialize the code below
+; for each of the 2^8 cases -- each bound is either a finite rational or is
+; infinite and each relation is either strict or not -- although one can cut it
+; in half by exploiting the commutativity of multiplication.) That's why we
+; extended the basic operations to handle infinities.
+
+; The intellectual bulk of the following definition was derived by Robert
+; Krug's, who worked out and verified an exhaustive case analysis. The proof
+; of correctness of his original involved only about 5,000 subgoals whereas the
+; proof of correctness of the full-fledged interval-based version involves
+; about 40,000. But the case analysis and bounds is due to Robert. Thank you
+; Robert!
+
+ (cond
+ ((equal lo1 hi1)
+ (cond ((bounds-of-product-< 0 lo1)
+ (mv lo-rel2
+ (bounds-of-product-* lo1 lo2)
+ hi-rel2
+ (bounds-of-product-* lo1 hi2)))
+ ((equal lo1 0)
+ (mv nil
+ 0
+ nil
+ 0))
+ (t
+ ;; (bounds-of-product-< lo1 0)
+ (mv hi-rel2
+ (bounds-of-product-* lo1 hi2)
+ lo-rel2
+ (bounds-of-product-* lo1 lo2)))))
+ ((equal lo2 hi2)
+ (cond ((bounds-of-product-< 0 lo2)
+ (mv lo-rel1
+ (bounds-of-product-* lo1 lo2)
+ hi-rel1
+ (bounds-of-product-* hi1 hi2)))
+ ((equal lo2 0)
+ (mv nil
+ 0
+ nil
+ 0))
+ (t
+ ;; (bounds-of-product-< lo2 0)
+ (mv hi-rel1
+ (bounds-of-product-* hi1 lo2)
+ lo-rel1
+ (bounds-of-product-* lo1 lo2)))))
+
+ ((and (bounds-of-product-<= 0 lo1)
+ (bounds-of-product-<= 0 lo2))
+ (mv (and (or lo-rel1 lo-rel2)
+ (or lo-rel1 (not (equal lo1 0)))
+ (or lo-rel2 (not (equal lo2 0))))
+ (bounds-of-product-* lo1 lo2)
+ (or hi-rel1 hi-rel2)
+ (bounds-of-product-* hi1 hi2)
+ ))
+ ((and (bounds-of-product-<= hi1 0)
+ (bounds-of-product-<= hi2 0))
+ (mv (and (or hi-rel1 hi-rel2)
+ (or hi-rel1 (not (equal hi1 0)))
+ (or hi-rel2 (not (equal hi2 0))))
+ (bounds-of-product-* hi1 hi2)
+ (or lo-rel1 lo-rel2)
+ (bounds-of-product-* lo1 lo2)
+ ))
+ ((and (bounds-of-product-<= 0 lo1)
+ (bounds-of-product-<= hi2 0))
+ (mv (or hi-rel1 lo-rel2)
+ (bounds-of-product-* hi1 lo2)
+ (and (or lo-rel1 hi-rel2)
+ (or lo-rel1 (not (equal lo1 0)))
+ (or hi-rel2 (not (equal hi2 0))))
+ (bounds-of-product-* lo1 hi2)
+ ))
+ ((and (bounds-of-product-<= hi1 0)
+ (bounds-of-product-<= 0 lo2))
+ (mv (or lo-rel1 hi-rel2)
+ (bounds-of-product-* lo1 hi2)
+ (and (or hi-rel1 lo-rel2)
+ (or hi-rel1 (not (equal hi1 0)))
+ (or lo-rel2 (not (equal lo2 0))))
+ (bounds-of-product-* hi1 lo2)
+ ))
+
+ ((and (bounds-of-product-< lo1 0)
+ (bounds-of-product-< 0 hi1)
+ (bounds-of-product-<= 0 lo2))
+ (mv (or lo-rel1 hi-rel2)
+ (bounds-of-product-* lo1 hi2)
+ (or hi-rel1 hi-rel2)
+ (bounds-of-product-* hi1 hi2)
+ ))
+ ((and (bounds-of-product-< lo1 0)
+ (bounds-of-product-< 0 hi1)
+ (bounds-of-product-<= hi2 0))
+ (mv (or hi-rel1 lo-rel2)
+ (bounds-of-product-* hi1 lo2)
+ (or lo-rel1 lo-rel2)
+ (bounds-of-product-* lo1 lo2)
+ ))
+
+ ((and (bounds-of-product-<= 0 lo1)
+ (bounds-of-product-< lo2 0)
+ (bounds-of-product-< 0 hi2))
+ (mv (or hi-rel1 lo-rel2)
+ (bounds-of-product-* hi1 lo2)
+ (or hi-rel1 hi-rel2)
+ (bounds-of-product-* hi1 hi2)
+ ))
+ ((and (bounds-of-product-<= hi1 0)
+ (bounds-of-product-< lo2 0)
+ (bounds-of-product-< 0 hi2))
+ (mv (or lo-rel1 hi-rel2)
+ (bounds-of-product-* lo1 hi2)
+ (or lo-rel1 lo-rel2)
+ (bounds-of-product-* lo1 lo2)
+ ))
+
+ (t
+ (cond ((and (bounds-of-product-< (bounds-of-product-* lo1 hi2)
+ (bounds-of-product-* hi1 lo2))
+ (bounds-of-product-< (bounds-of-product-* lo1 lo2)
+ (bounds-of-product-* hi1 hi2)))
+ (mv (or lo-rel1 hi-rel2)
+ (bounds-of-product-* lo1 hi2)
+ (or hi-rel1 hi-rel2)
+ (bounds-of-product-* hi1 hi2)
+ ))
+ ((and (bounds-of-product-< (bounds-of-product-* lo1 hi2)
+ (bounds-of-product-* hi1 lo2))
+ (equal (bounds-of-product-* lo1 lo2)
+ (bounds-of-product-* hi1 hi2)))
+ (mv (or lo-rel1 hi-rel2)
+ (bounds-of-product-* lo1 hi2)
+ (and (or lo-rel1 lo-rel2)
+ (or hi-rel1 hi-rel2))
+ (bounds-of-product-* lo1 lo2)
+ ))
+ ((and (bounds-of-product-< (bounds-of-product-* lo1 hi2)
+ (bounds-of-product-* hi1 lo2))
+ (bounds-of-product-< (bounds-of-product-* hi1 hi2)
+ (bounds-of-product-* lo1 lo2)))
+ (mv (or lo-rel1 hi-rel2)
+ (bounds-of-product-* lo1 hi2)
+ (or lo-rel1 lo-rel2)
+ (bounds-of-product-* lo1 lo2)
+ ))
+
+ ((and (equal (bounds-of-product-* lo1 hi2)
+ (bounds-of-product-* hi1 lo2))
+ (bounds-of-product-< (bounds-of-product-* lo1 lo2)
+ (bounds-of-product-* hi1 hi2)))
+ (mv (and (or lo-rel1 hi-rel2)
+ (or hi-rel1 lo-rel2))
+ (bounds-of-product-* lo1 hi2)
+ (or hi-rel1 hi-rel2)
+ (bounds-of-product-* hi1 hi2)
+ ))
+ ((and (equal (bounds-of-product-* lo1 hi2)
+ (bounds-of-product-* hi1 lo2))
+ (equal (bounds-of-product-* lo1 lo2)
+ (bounds-of-product-* hi1 hi2)))
+ (mv (and (or lo-rel1 hi-rel2)
+ (or hi-rel1 lo-rel2))
+ (bounds-of-product-* lo1 hi2)
+ (and (or lo-rel1 lo-rel2)
+ (or hi-rel1 hi-rel2))
+ (bounds-of-product-* lo1 lo2)
+ ))
+ ((and (equal (bounds-of-product-* lo1 hi2)
+ (bounds-of-product-* hi1 lo2))
+ (bounds-of-product-< (bounds-of-product-* hi1 hi2)
+ (bounds-of-product-* lo1 lo2)))
+ (mv (and (or lo-rel1 hi-rel2)
+ (or hi-rel1 lo-rel2))
+ (bounds-of-product-* lo1 hi2)
+ (or lo-rel1 lo-rel2)
+ (bounds-of-product-* lo1 lo2)
+ ))
+
+ ((and (bounds-of-product-< (bounds-of-product-* hi1 lo2)
+ (bounds-of-product-* lo1 hi2))
+ (bounds-of-product-< (bounds-of-product-* lo1 lo2)
+ (bounds-of-product-* hi1 hi2)))
+ (mv (or hi-rel1 lo-rel2)
+ (bounds-of-product-* hi1 lo2)
+ (or hi-rel1 hi-rel2)
+ (bounds-of-product-* hi1 hi2)
+ ))
+ ((and (bounds-of-product-< (bounds-of-product-* hi1 lo2)
+ (bounds-of-product-* lo1 hi2))
+ (equal (bounds-of-product-* lo1 lo2)
+ (bounds-of-product-* hi1 hi2)))
+ (mv (or hi-rel1 lo-rel2)
+ (bounds-of-product-* hi1 lo2)
+ (and (or lo-rel1 lo-rel2)
+ (or hi-rel1 hi-rel2))
+ (bounds-of-product-* lo1 lo2)
+ ))
+ (t
+ ;; (and (bounds-of-product-< (bounds-of-product-* hi1 lo2)
+ ;; (bounds-of-product-* lo1 hi2))
+ ;; (bounds-of-product-< (bounds-of-product-* hi1 hi2)
+ ;; (bounds-of-product-* lo1 lo2)))
+ (mv (or hi-rel1 lo-rel2)
+ (bounds-of-product-* hi1 lo2)
+ (or lo-rel1 lo-rel2)
+ (bounds-of-product-* lo1 lo2)
+ ))))))
+
+(defun bounds-of-product (lo-rel1 lo1 hi-rel1 hi1 lo-rel2 lo2 hi-rel2 hi2)
+ (declare (xargs :guard (and (booleanp lo-rel1)
+ (or (null lo1) (real/rationalp lo1))
+ (booleanp hi-rel1)
+ (or (null hi1) (real/rationalp hi1))
+ (booleanp lo-rel2)
+ (or (null lo2) (real/rationalp lo2))
+ (booleanp hi-rel2)
+ (or (null hi2) (real/rationalp hi2)))))
+
+; We coerce positive infinities to t, use the workhorse function, and then
+; coerce positive infinities back to nil. We also insure that weak
+; inequalities are used with the infinities. (The workhorse can return a
+; strong inequality with an infinity, as happens with: (bounds-of-product1 10
+; t nil nil 100 t 100 t) where we get back the lower bound of nil with the
+; relation t.)
+
+ (let ((hi1 (if (null hi1) t hi1))
+ (hi2 (if (null hi2) t hi2)))
+ (mv-let (lo-rel lo hi-rel hi)
+ (bounds-of-product1 lo-rel1 lo1 hi-rel1 hi1
+ lo-rel2 lo2 hi-rel2 hi2)
+ (mv (if (null lo) nil lo-rel)
+ lo
+ (if (eq hi t) nil hi-rel)
+ (if (eq hi t) nil hi)))))
+
+; Here is a theorem (and proof hint) establishing the correctness of the
+; bounds-of-product function. The version of this theorem in which all bounds
+; are finite is due to Robert Krug although we have changed some function names
+; and introduced our conventional rel flag (of opposite parity than Robert's
+; closedp).
+
+; This theorem is a good application for ACL2(p). If you are running ACL2(p) the
+; following two commands are helpful (and are no-ops in ordinary ACL2).
+
+; (set-waterfall-parallelism t)
+; (set-waterfall-printing :limited)
+
+; We break the usual theorem into two parts: one theorem about the domain and
+; the other about the bounds. The reason is that the bounds theorem breaks in
+; the special cases that x and/or y is INTEGERP instead of just RATIONALP.
+; This is because of some idiosyncracies of arithmetic-5's handling of
+; non-linear.
+
+(defun tau-bounder-* (int1 int2)
+ (declare (xargs :guard (and (tau-intervalp int1) (tau-intervalp int2))))
+ (let ((dom (domain-of-binary-arithmetic-function
+ (tau-interval-dom int1)
+ (tau-interval-dom int2))))
+ (mv-let (lo-rel lo hi-rel hi)
+ (bounds-of-product (tau-interval-lo-rel int1)
+ (tau-interval-lo int1)
+ (tau-interval-hi-rel int1)
+ (tau-interval-hi int1)
+ (tau-interval-lo-rel int2)
+ (tau-interval-lo int2)
+ (tau-interval-hi-rel int2)
+ (tau-interval-hi int2))
+ (make-tau-interval dom lo-rel lo hi-rel hi))))
+
+(local (defthm |tau-intervalp tau-bounder-*|
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2))
+ (tau-intervalp (tau-bounder-* int1 int2)))))
+
+(local (defthm |tau-interval-dom tau-bounder-*|
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (or (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int1) 'rationalp))
+ (or (equal (tau-interval-dom int2) 'integerp)
+ (equal (tau-interval-dom int2) 'rationalp))
+ (not (equal (tau-interval-dom (tau-bounder-* int1 int2)) 'integerp)))
+ (equal (tau-interval-dom (tau-bounder-* int1 int2)) 'rationalp))
+ :hints (("Goal" :in-theory (disable bounds-of-product)))))
+
+; Weakness: The following theorem could be strengthened by concluding, in
+; addition, that if int1 and int2 have INTEGERP domains then the tau-bounder-*
+; has an INTEGERP domain. As it stands below, we do not break out the cases
+; and concluded only that if both int1 and int2 are either INTEGERP or
+; RATIONALP then so is the product.
+
+(local (encapsulate
+ ()
+ (local (in-theory (disable
+ ;; Horrible godawful hack.
+ the-floor-below
+ the-floor-above
+ REDUCE-RATIONAL-MULTIPLICATIVE-CONSTANT-<
+ REDUCE-MULTIPLICATIVE-CONSTANT-<
+ REDUCE-ADDITIVE-CONSTANT-<
+ INTEGERP-<-CONSTANT
+ CONSTANT-<-INTEGERP
+ remove-strict-inequalities
+ remove-weak-inequalities
+ prefer-positive-addends-<
+ |(< c (/ x)) positive c --- present in goal|
+ |(< c (/ x)) positive c --- obj t or nil|
+ |(< c (/ x)) negative c --- present in goal|
+ |(< c (/ x)) negative c --- obj t or nil|
+ |(< c (- x))|
+ |(< (/ x) c) positive c --- present in goal|
+ |(< (/ x) c) positive c --- obj t or nil|
+ |(< (/ x) c) negative c --- present in goal|
+ |(< (/ x) c) negative c --- obj t or nil|
+ |(< (/ x) (/ y))|
+ |(< (- x) c)|
+ |(< (- x) (- y))|
+ intervalp-rules
+ REDUCE-RATIONALP-+
+ REDUCE-RATIONALP-*
+ RATIONALP-MINUS-X
+ META-RATIONALP-CORRECT
+ SIMPLIFY-PRODUCTS-GATHER-EXPONENTS-EQUAL
+ REDUCE-MULTIPLICATIVE-CONSTANT-EQUAL
+ REDUCE-ADDITIVE-CONSTANT-EQUAL
+ PREFER-POSITIVE-ADDENDS-EQUAL
+ EQUAL-OF-PREDICATES-REWRITE
+ |(equal c (/ x))|
+ |(equal c (- x))|
+ |(equal (/ x) c)|
+ |(equal (/ x) (/ y))|
+ |(equal (- x) c)|
+ |(equal (- x) (- y))|
+ |(< (/ x) 0)|
+ |(< (* x y) 0)|
+ (:TYPE-PRESCRIPTION IN-TAU-INTERVALP)
+ (:TYPE-PRESCRIPTION TAU-INTERVALP)
+ (:TYPE-PRESCRIPTION TAU-INTERVAL-DOMAINP)
+ NORMALIZE-FACTORS-GATHER-EXPONENTS
+ SIMPLIFY-TERMS-SUCH-AS-AX+BX-<-0-RATIONAL-REMAINDER
+ SIMPLIFY-TERMS-SUCH-AS-AX+BX-<-0-RATIONAL-COMMON
+ SIMPLIFY-SUMS-EQUAL
+ |(< 0 (/ x))|
+ |(< 0 (* x y))|
+ INTEGERP-MINUS-X
+ (:TYPE-PRESCRIPTION BOOLEANP)
+ |(< 0 (* x y)) rationalp (* x y)|
+ META-INTEGERP-CORRECT
+ default-plus-1
+ default-plus-2
+ default-divide
+ member
+ )))
+
+ (local (SET-DEFAULT-HINTS
+ '((and stable-under-simplificationp
+ '(:nonlinearp t))
+ (and stable-under-simplificationp
+ (not (cw "Jared-hint: re-enabling slow rules.~%"))
+ '(:in-theory (enable jared-disables)))
+ (and stable-under-simplificationp
+ (not (cw "Jared-hint: splitting into cases.~%"))
+ '(:cases ((and (< 0 x) (< 0 y))
+ (and (< 0 x) (equal 0 y))
+ (and (< 0 x) (< y 0))
+ (and (equal 0 x) (< 0 y))
+ (and (equal 0 x) (equal 0 y))
+ (and (equal 0 x) (< y 0))
+ (and (< x 0) (< 0 y))
+ (and (< x 0) (equal 0 y))
+ (and (< x 0) (< y 0)))))
+ (and stable-under-simplificationp
+ (NONLINEARP-DEFAULT-HINT++
+ ID STABLE-UNDER-SIMPLIFICATIONP HIST NIL)))))
+
+ (defthm |in-tau-intervalp tau-bounder-*|
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (or (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int1) 'rationalp))
+ (or (equal (tau-interval-dom int2) 'integerp)
+ (equal (tau-interval-dom int2) 'rationalp))
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (in-tau-intervalp (* x y) (tau-bounder-* int1 int2))))))
+
+(encapsulate ()
+ (local (in-theory (disable tau-bounder-*)))
+ (defthm tau-bounder-*-correct
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (or (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int1) 'rationalp))
+ (or (equal (tau-interval-dom int2) 'integerp)
+ (equal (tau-interval-dom int2) 'rationalp))
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-* int1 int2))
+ (in-tau-intervalp (* x y) (tau-bounder-* int1 int2))
+
+ (implies (not (equal (tau-interval-dom (tau-bounder-* int1 int2)) 'integerp))
+ (equal (tau-interval-dom (tau-bounder-* int1 int2)) 'rationalp))))
+
+; Robert Krug first defined the function (akin to our tau-bounder-*) that
+; computed the upper and lower bounds on a product given the bounds on the two
+; factors. He proved it correct and used the hint below, which he said was
+; necessary only for some of the cases but which he said was more convenient to
+; provide at the top. Robert also points out that tau-bounder-*-correct
+; theorem is a good example for David Rager's work on parallelism. One might
+; try it with:
+
+; (set-waterfall-parallelism t)
+; (set-waterfall-printing :limited)
+
+ :rule-classes
+ ((:rewrite)
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'integerp)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-* int1 int2))
+ (implies (not (equal (tau-interval-dom (tau-bounder-* int1 int2)) 'integerp))
+ (equal (tau-interval-dom (tau-bounder-* int1 int2)) 'rationalp))
+ (in-tau-intervalp (* x y) (tau-bounder-* int1 int2))))
+ :trigger-terms ((tau-bounder-* int1 int2)))
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'rationalp)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-* int1 int2))
+ (implies (not (equal (tau-interval-dom (tau-bounder-* int1 int2)) 'integerp))
+ (equal (tau-interval-dom (tau-bounder-* int1 int2)) 'rationalp))
+ (in-tau-intervalp (* x y) (tau-bounder-* int1 int2))))
+ :trigger-terms ((tau-bounder-* int1 int2)))
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'rationalp)
+ (equal (tau-interval-dom int2) 'integerp)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-* int1 int2))
+ (implies (not (equal (tau-interval-dom (tau-bounder-* int1 int2)) 'integerp))
+ (equal (tau-interval-dom (tau-bounder-* int1 int2)) 'rationalp))
+ (in-tau-intervalp (* x y) (tau-bounder-* int1 int2))))
+ :trigger-terms ((tau-bounder-* int1 int2)))
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'rationalp)
+ (equal (tau-interval-dom int2) 'rationalp)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-* int1 int2))
+ (implies (not (equal (tau-interval-dom (tau-bounder-* int1 int2)) 'integerp))
+ (equal (tau-interval-dom (tau-bounder-* int1 int2)) 'rationalp))
+ (in-tau-intervalp (* x y) (tau-bounder-* int1 int2))))
+ :trigger-terms ((tau-bounder-* int1 int2)))
+ )))
+
+
+; The proof above takes 15 minutes and generates a lot of subgoals.
+
+; [Jared]: I got it down to 180 seconds with stupid AP hacking. (I think this
+; could be significantly improved by not just brute forcing the proof,
+; eventually).
+
+; Subgoals: 40,661
+; Time: 920.78 seconds (prove: 917.26, print: 3.49, other: 0.02)
+; Prover steps counted: 53057929
+
+; The restrictions on the domains of int1 and int2 are necessary. The theorem
+; does not hold for complex numbers. The imaginary number i, i.e., #c(0 1), is
+; in the interval beteen 0 and 10 shown for int1 and int2 below, but (* i i) =
+; -1 is not in the product interval, 0 to 100.
+
+; (set-guard-checking :none)
+;
+; (let ((int1 (make-tau-interval 'acl2-numberp nil 0 nil 10))
+; (int2 (make-tau-interval 'acl2-numberp nil 0 nil 10))
+; (x #c(0 1))
+; (y #c(0 1)))
+; (implies (and (tau-intervalp int1)
+; (tau-intervalp int2)
+; ; (or (equal (tau-interval-dom int1) 'integerp) ; delete these hyps
+; ; (equal (tau-interval-dom int1) 'rationalp))
+; ; (or (equal (tau-interval-dom int2) 'integerp)
+; ; (equal (tau-interval-dom int2) 'rationalp))
+; (in-tau-intervalp x int1)
+; (in-tau-intervalp y int2))
+; (in-tau-intervalp (* x y) (tau-bounder-* int1 int2))))
+; NIL
+
+; -----------------------------------------------------------------
+; - - the unary minus operator
+
+; Since (- x) = (* -1 x), we can define:
+
+(defconst *tau-interval-for--1*
+ (make-tau-interval 'integerp nil -1 nil -1))
+
+(defun tau-bounder-- (int)
+ (declare (xargs :guard (tau-intervalp int)))
+ (tau-bounder-* *tau-interval-for--1* int))
+
+(defthm tau-bounder---correct
+ (implies (and (tau-intervalp int1)
+ (or (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int1) 'rationalp))
+ (in-tau-intervalp x int1))
+ (and (tau-intervalp (tau-bounder-- int1))
+ (in-tau-intervalp (unary-- x) (tau-bounder-- int1))
+ (implies (not (equal (tau-interval-dom (tau-bounder-- int1)) 'integerp))
+ (equal (tau-interval-dom (tau-bounder-- int1)) 'rationalp))))
+ :rule-classes
+ ((:rewrite)
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int1)
+ (equal (tau-interval-dom int1) 'integerp)
+ (in-tau-intervalp x int1))
+ (and (tau-intervalp (tau-bounder-- int1))
+ (in-tau-intervalp (unary-- x) (tau-bounder-- int1))
+ (equal (tau-interval-dom (tau-bounder-- int1)) 'integerp)))
+ :trigger-terms ((tau-bounder-- int1)))
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int1)
+ (equal (tau-interval-dom int1) 'rationalp)
+ (in-tau-intervalp x int1))
+ (and (tau-intervalp (tau-bounder-- int1))
+ (in-tau-intervalp (unary-- x) (tau-bounder-- int1))
+ (equal (tau-interval-dom (tau-bounder-- int1)) 'rationalp)))
+ :trigger-terms ((tau-bounder-* int1 int2)))
+ ))
+
+; -----------------------------------------------------------------
+; / -- the reciprocal operator
+
+; The reason we care about this case is that we wish to bound (floor x y). We
+; know (floor x y) <= x/y which is (* x (/ y)), and we know how to bound a
+; product. So what remains is how to bound the reciprocal of y given the
+; bounds on y.
+
+; If x occupies a finite rational interval that lies STRICTLY above or below 0,
+; e.g.,
+; 0 < lo <? x <? hi
+; or
+; lo <? x <? hi < 0
+
+; then we can bound (/ x) by inverting and switching the bounds of x:
+
+; (/ hi) <? (/ x) <? (/ lo)
+
+; However, things are more complicated if x can take on the value 0, either
+; because it is bounded weakly above or below 0 or because the interval spans
+; 0. If a rational interval includes 0 then (/ x) cannot be bounded in either
+; direction: as x approaches 0 from above, (/ x) becomes infinitely positive;
+; as x approaches 0 from below, (/ x) becomes infinitely negative. (These
+; observations depend on x being allowed to take on any rational value.) What
+; happens if one of the endpoints of the interval is 0? For example what is
+; the range of (/ x) if 0 <= x <= 7? One might think it is 1/7 <= (/ x) <=
+; infinity. But in fact it is 0 <= (/ x) <= infinity because when x = 0, (/ x)
+; is (unfortunately) 0 rather than infinity.
+
+; If x is limited to the integers, the situation is more interesting:
+; -1 <= (/ x) <= 1 for integral values of x.
+
+(defun bounds-of-reciprocal (x dom lo-rel lo hi-rel hi)
+ (declare (ignore x))
+ (declare (xargs :guard (and (symbolp dom)
+ (booleanp lo-rel)
+ (or (null lo) (real/rationalp lo))
+ (booleanp hi-rel)
+ (or (null hi) (real/rationalp hi))
+ (or (null lo) (null hi) (<= lo hi)))))
+; This function returns the bounds on (/ x) given bounds on x. It is assumed x
+; and the bounds, lo and hi, are in the domain specified by dom, which is
+; either INTEGERP or RATIONALP. Furthermore, when dom is INTEGERP, the two
+; relations are weak (nil).
+
+; The following table summarizes this definition. We use -oo and +oo for the
+; two infinities. However, when dom is INTEGERP we can't get an infinity out of
+; reciprocal. That is, the biggest thing we can get is 1, because (/ 1) = 1,
+; and the smallest is -1.
+
+; Lines [2,5,8,11] are irrelevant when dom is INTEGERP because the relations
+; are weak and those lines involve strong relations.
+
+; [1] -oo <= x <= 0 -oo <= (/ x) <= 0
+; [2] -oo <= x < 0 -oo <= (/ x) < 0
+; [3] -oo <= x <? hi < 0 (/ hi) <? (/ x) < 0
+; [4] lo <? x <? hi < 0 (/ hi) <? (/ x) <? (/ lo)
+; [5] lo <? x < 0 -oo <= (/ x) <? (/ lo)
+; [6] lo <? x <= 0 -oo <= (/ x) <= 0
+
+; [7] 0 <= x <= +oo 0 <= (/ x) <= +oo
+; [8] 0 < x <= +oo 0 < (/ x) <= +oo
+; [9] 0 < lo <? x <= +oo 0 < (/ x) <? (/ lo)
+;[10] 0 < lo <? x <? hi (/ hi) <? (/ x) <? (/ lo)
+;[11] 0 < x <? hi (/ hi) <? (/ x) <= +oo
+;[12] 0 <= x <? hi 0 <= (/ x) <= +oo
+
+; [*] all other cases -oo <= (/ x) <= +oo
+
+
+ (let ((-oo (if (eq dom 'integerp) -1 nil))
+ (+oo (if (eq dom 'integerp) +1 nil)))
+ (cond
+ ((null lo)
+ (cond ((null hi)
+ (mv nil -oo nil +oo)) ; [*]
+ ((< hi 0)
+ (mv hi-rel (/ hi) t 0)) ; [3]
+ ((equal hi 0)
+ (mv nil -oo hi-rel 0)) ; [1,2]
+ (t
+ (mv nil -oo nil +oo)))) ; [*]
+ ((< 0 lo)
+ (cond
+ ((null hi)
+ (mv t 0 lo-rel (/ lo))) ; [9]
+ (t
+ (mv hi-rel (/ hi) lo-rel (/ lo))))) ; [10]
+ ((equal 0 lo)
+ (cond ((null hi)
+ (mv lo-rel 0 nil +oo)) ; [7,8]
+ ((equal 0 hi)
+ (mv nil 0 nil 0))
+ (lo-rel
+ (mv hi-rel (/ hi) nil +oo)) ; [11]
+ (t
+ (mv nil 0 nil +oo)))) ; [12]
+ ((or (null hi)
+ (< 0 hi))
+ (mv nil -oo nil +oo)) ; [*]
+ ((equal 0 hi)
+ (cond
+ (hi-rel
+ (mv nil -oo lo-rel (/ lo))) ; [5]
+ (t
+ (mv nil -oo nil 0)))) ; [6]
+ (t
+ (mv hi-rel (/ hi) lo-rel (/ lo)))))) ; [4]
+
+(defun tau-bounder-/ (int)
+ (declare (xargs :guard (tau-intervalp int)))
+ (mv-let (lo-rel lo hi-rel hi)
+ (bounds-of-reciprocal 0
+ (tau-interval-dom int)
+ (tau-interval-lo-rel int)
+ (tau-interval-lo int)
+ (tau-interval-hi-rel int)
+ (tau-interval-hi int))
+ (make-tau-interval 'rationalp lo-rel lo hi-rel hi)))
+
+(local (defthm |tau-intervalp tau-bounder-/|
+ (implies (tau-intervalp int1)
+ (tau-intervalp (tau-bounder-/ int1)))))
+
+(local (defthm |tau-interval-dom tau-bounder-/|
+ (equal (tau-interval-dom (tau-bounder-/ int1)) 'rationalp)
+ :hints (("Goal" :in-theory (disable bounds-of-reciprocal)))))
+
+(local (defthm |in-tau-intervalp tau-bounder-/|
+ (implies (and (tau-intervalp int1)
+ (or (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int1) 'rationalp))
+ (in-tau-intervalp x int1))
+ (in-tau-intervalp (/ x) (tau-bounder-/ int1)))))
+
+(defthm tau-bounder-/-correct
+ (implies (and (tau-intervalp int1)
+ (or (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int1) 'rationalp))
+ (in-tau-intervalp x int1))
+ (and (tau-intervalp (tau-bounder-/ int1))
+ (in-tau-intervalp (/ x) (tau-bounder-/ int1))
+
+ (equal (tau-interval-dom (tau-bounder-/ int1)) 'rationalp)))
+ :hints (("Goal" :in-theory (disable tau-bounder-/)))
+ :rule-classes
+ ((:rewrite)
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int1)
+ (equal (tau-interval-dom int1) 'integerp)
+ (in-tau-intervalp x int1))
+ (and (tau-intervalp (tau-bounder-/ int1))
+ (equal (tau-interval-dom (tau-bounder-/ int1)) 'rationalp)
+ (in-tau-intervalp (/ x) (tau-bounder-/ int1))))
+ :trigger-terms ((tau-bounder-/ int1)))
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int1)
+ (equal (tau-interval-dom int1) 'rationalp)
+ (in-tau-intervalp x int1))
+ (and (tau-intervalp (tau-bounder-/ int1))
+ (equal (tau-interval-dom (tau-bounder-/ int1)) 'rationalp)
+ (in-tau-intervalp (/ x) (tau-bounder-/ int1))))
+ :trigger-terms ((tau-bounder-/ int1)))
+ ))
+
+; -----------------------------------------------------------------
+; FLOOR
+
+; Intuitively the smallest value of (floor x y) is gotten by taking the
+; smallest value of x and flooring it with the biggest value of y, and the
+; biggest value of floor is gotten by taking the biggest value of x and
+; flooring it with the smallest value of y. But what about signs, 0, etc?
+; Certainly the naive idea above is wrong: suppose 2 <= x <= 25 and -3 <= y <=
+; 7, then the actual range of (floor x y) is -25 <= (floor x y) <= +25. But
+; the naive idea says that the smallest value of (floor x y) is (floor 2 25),
+; which is 0. Wrong. Rather than think it through, we just use the fact that
+; (floor x y) is the integer under (/ x y) = (* x (/ y)) and compute the
+; integer bounds on the latter expression.
+
+(local
+ (defthm floor-tau-bounder-domain
+ (integerp (floor x y))))
+
+(defun tau-bounder-floor (int1 int2)
+ (declare (xargs :guard (and (tau-intervalp int1) (tau-intervalp int2))
+ :guard-hints (("Goal" :cases ((tau-intervalp (tau-bounder-* int1 (tau-bounder-/ int2))))
+ :in-theory (disable tau-bounder-* tau-bounder-/))
+ ("Subgoal 2" :use (
+ (:instance |tau-intervalp tau-bounder-*|
+ (int1 int1)
+ (int2 (tau-bounder-/ int2)))
+ (:instance |tau-intervalp tau-bounder-/|
+ (int1 int2))))
+ )
+ ))
+ (let ((int
+ (tau-bounder-* int1 (tau-bounder-/ int2))))
+ (make-tau-interval 'integerp
+ nil
+ (if (tau-interval-lo int)
+ (floor (tau-interval-lo int) 1)
+ nil)
+ nil
+ (if (tau-interval-hi int)
+ (floor (tau-interval-hi int) 1)
+ nil))))
+
+; This is the first of our proofs in the ``second style.'' The idea is not to
+; re-expose the (horrendous) details of tau-bounder-* or any of the other
+; pre-verified bounders. We want the proof to appeal to their correctness
+; proofs. With that in mind, we disable the functions concerned (see the e/d
+; below). We disable the executable-counterpart of make-tau-interval so that
+; constant intervals, like (make-tau-interval 'integerp nil nil nil nil) which
+; arises in one case above), don't block our use of the intervalp-make-tau-interval
+; and in-tau-intervalp-make-tau-interval rules.
+
+; The idea in these second style proofs is that the new bounder,
+; tau-bounder-floor expands to expose a composition of old bounders and/or
+; make-tau-intervals. Then the previously proved results apply to the resulting
+; intervalp, in-tau-intervalp, and interval-dom expressions which are now applied
+; to previously verified bounders.
+
+; However, there is a problem. The (in-tau-intervalp (make-tau-interval ...))
+; exposed by the expansion of tau-bounder-floor below will lead us to the
+; goal (among others):
+
+; (FLOOR X Y) <= (TAU-INTERVAL-LO (TAU-BOUNDER-* INT1 (TAU-BOUNDER-/ INT2))).
+
+; This particular goal is a bit tricky because it involves an ``extra step,'' namely
+; knowing that (floor x y) <= (* x (/ y)), which is known to arithmetic-5. So let's
+; simplify the discussion by assuming that step is taken care of. How do we prove
+; the resulting:
+
+; (* X (/ Y)) <= (TAU-INTERVAL-LO (TAU-BOUNDER-* INT1 (TAU-BOUNDER-/ INT2))).
+
+; where we know that X is in INT1 and Y is in INT2.
+
+; The relevant lemma about tau-bounder-* concludes with:
+
+; (in-tau-intervalp (* x y) (tau-bounder-* int1 int2))
+
+; From this fact we could conclude
+
+; (IN-TAU-INTERVALP (* X (/ Y)) (TAU-BOUNDER-* INT1 (TAU-BOUNDER-/ INT2))) [1]
+
+; and from there we could get an inequality relating (* X (/ Y)) to the
+; INTERVAL-LO of the TAU-BOUNDER-*. But since [1] isn't in the theorem
+; and isn't a subgoal, we have to somehow create it and the only way I can
+; think of to do that is forward-chain triggered by a term that will be in
+; the problem, namely:
+
+; (TAU-BOUNDER-* INT1 (TAU-BOUNDER-/ INT2)).
+
+; But that requires finding choices for the free variables x and y.
+
+; So the general strategy of the ``second style'' of proof is to do everything
+; by forward chaining. From the presence of (TAU-BOUNDER-/ INT2) we forward
+; chain to (IN-TAU-INTERVALP (/ Y) (TAU-BOUNDER-/ INT2)), guessing Y by the presence
+; of (IN-TAU-INTERVALP Y INT2) given in the problem. From the presence of
+; (TAU-BOUNDER-* INT1 (TAU-BOUNDER-/ INT2)) we forward chain to
+; (IN-TAU-INTERVALP (* X (/ Y)) INT1 (TAU-BOUNDER-/ INT2)), guessing X from
+; (IN-TAU-INTERVALP X INT1) given in the problem and guessing (/ Y) from the just
+; derived IN-TAU-INTERVALP.
+
+; Once these basic IN-TAU-INTERVALP conditions are derived, we can use :LINEAR lemmas
+; like that in IN-TAU-INTERVALP-RULES:
+
+; (implies (and (in-tau-intervalp x int)
+; x
+; (tau-intervalp int)
+; (tau-interval-lo int))
+; (<= (tau-interval-lo int) x))
+
+; which, note, have a free x in them. That free x is selected via the first
+; hyp above, which is present because of forward-chaining.
+
+(defthm tau-bounder-floor-correct
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (or (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int1) 'rationalp))
+ (or (equal (tau-interval-dom int2) 'integerp)
+ (equal (tau-interval-dom int2) 'rationalp))
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-floor int1 int2))
+ (in-tau-intervalp (floor x y) (tau-bounder-floor int1 int2))
+
+ (equal (tau-interval-dom (tau-bounder-floor int1 int2)) 'integerp)
+ ))
+ :hints (("Goal"
+ :in-theory
+ (disable tau-intervalp
+ in-tau-intervalp
+ tau-bounder-*
+ tau-bounder-/
+ (:executable-counterpart make-tau-interval))))
+ :rule-classes
+ ((:rewrite)
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'integerp)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-floor int1 int2))
+ (equal (tau-interval-dom (tau-bounder-floor int1 int2)) 'integerp)
+ (in-tau-intervalp (floor x y) (tau-bounder-floor int1 int2))
+ ))
+ :trigger-terms ((tau-bounder-floor int1 int2)))
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'rationalp)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-floor int1 int2))
+ (equal (tau-interval-dom (tau-bounder-floor int1 int2)) 'integerp)
+ (in-tau-intervalp (floor x y) (tau-bounder-floor int1 int2))
+ ))
+ :trigger-terms ((tau-bounder-floor int1 int2)))
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'rationalp)
+ (equal (tau-interval-dom int2) 'integerp)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-floor int1 int2))
+ (equal (tau-interval-dom (tau-bounder-floor int1 int2)) 'integerp)
+ (in-tau-intervalp (floor x y) (tau-bounder-floor int1 int2))
+ ))
+ :trigger-terms ((tau-bounder-floor int1 int2)))
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'rationalp)
+ (equal (tau-interval-dom int2) 'rationalp)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-floor int1 int2))
+ (equal (tau-interval-dom (tau-bounder-floor int1 int2)) 'integerp)
+ (in-tau-intervalp (floor x y) (tau-bounder-floor int1 int2))
+ ))
+ :trigger-terms ((tau-bounder-floor int1 int2)))
+ ))
+
+; -----------------------------------------------------------------
+; MOD
+
+; Since (mod x y) = (- x (* (floor x y) y)), the domain of MOD is given
+; by domain-of-binary-arithmetic-function:
+
+(defun lower-bound-> (a-rel a b-rel b)
+ (declare (xargs :guard (and (booleanp a-rel)
+ (or (null a) (rationalp a))
+ (booleanp b-rel)
+ (or (null b) (rationalp 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)
+ (declare (xargs :guard (and (booleanp a-rel)
+ (or (null a) (rationalp a))
+ (booleanp b-rel)
+ (or (null b) (rationalp 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 combine-intervals1 (dom1 lo-rel1 lo1 hi-rel1 hi1
+ dom2 lo-rel2 lo2 hi-rel2 hi2)
+ (declare (xargs :guard (and (symbolp dom1)
+ (booleanp lo-rel1)
+ (or (null lo1) (rationalp lo1))
+ (booleanp hi-rel1)
+ (or (null hi1) (rationalp hi1))
+ (symbolp dom2)
+ (booleanp lo-rel2)
+ (or (null lo2) (rationalp lo2))
+ (booleanp hi-rel2)
+ (or (null hi2) (rationalp hi2)))))
+ (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))
+ (make-tau-interval (domain-of-binary-arithmetic-function dom1 dom2)
+ lo-rel lo hi-rel hi))))
+
+(defun squeeze-k (upper-boundp rel k)
+ (declare (xargs :guard (and (booleanp upper-boundp)
+ (booleanp rel)
+ (or (null k) (rationalp k)))))
+
+; K is either NIL (the appopriate infinity) or a rational. Consider some
+; interval with INTEGERP domain bounded (above or below as per upper-boundp) by
+; rel and k. If k is non-nil, we squeeze the given bound as per:
+
+; Table A:
+; upper-boundp rel meaning squeezed k'
+; t t (< x k) (<= x k') (- (ceiling k 1) 1)
+; t nil (<= x k) (<= x k') (floor k 1)
+; nil t (< k x) (<= k' x) (+ (floor k 1) 1)
+; nil nil (<= k x) (<= k' x) (ceiling k 1)
+
+; Programming Note: When k is a non-integer rational,
+; (- (ceiling k 1) 1) = (floor k 1), and thus
+; (+ (floor k 1) 1) = (ceiling k 1)
+; so the table would be:
+
+; Table B: Non-Integer Rational k:
+; upper-boundp rel meaning squeezed k'
+; t t (< x k) (<= x k') (floor k 1)
+; t nil (<= x k) (<= x k') (floor k 1)
+; nil t (< k x) (<= k' x) (ceiling k 1)
+; nil nil (<= k x) (<= k' x) (ceiling k 1)
+
+; But when k is an integer, the table is:
+
+; Table C: Integer k:
+; upper-boundp rel meaning squeezed k'
+; t t (< x k) (<= x k') (- k 1)
+; t nil (<= x k) (<= x k') k
+; nil t (< k x) (<= k' x) (+ k 1)
+; nil nil (<= k x) (<= k' x) k
+
+; We actually code Tables B and C and test which to use with (integerp k),
+; because we believe it is faster than Table A because whenever k is an integer
+; we avoid calls of floor or ceiling.
+
+ (if k
+ (if (integerp k)
+ (if rel
+ (if upper-boundp (- k 1) (+ k 1))
+ k)
+ (if upper-boundp
+ (floor k 1)
+ (ceiling k 1)))
+ nil))
+
+(defun make-tau-interval-with-integerp-fix (dom lo-rel lo hi-rel hi)
+ (declare (xargs :guard (and (symbolp dom)
+ (booleanp lo-rel)
+ (or (null lo) (rationalp lo))
+ (booleanp hi-rel)
+ (or (null hi) (rationalp hi)))))
+ (cond ((eq dom 'integerp)
+ (cond ((and (null lo-rel)
+ (or (null lo) (integerp lo))
+ (null hi-rel)
+ (or (null hi) (integerp hi)))
+ (make-tau-interval 'integerp nil lo nil hi))
+ (t (let ((lo (squeeze-k nil lo-rel lo))
+ (hi (squeeze-k t hi-rel hi)))
+ (make-tau-interval 'integerp nil lo nil hi)))))
+ (t (make-tau-interval dom lo-rel lo hi-rel hi))))
+
+(defun tau-bounder-mod (int1 int2)
+ (declare (xargs :guard (and (tau-intervalp int1)
+ (tau-intervalp int2))))
+ (let ((domx (tau-interval-dom int1))
+ (lo-relx (tau-interval-lo-rel int1))
+ (lox (tau-interval-lo int1))
+ (hi-relx (tau-interval-hi-rel int1))
+ (hix (tau-interval-hi int1))
+ (domy (tau-interval-dom int2))
+ (lo-rely (tau-interval-lo-rel int2))
+ (loy (tau-interval-lo int2))
+ (hi-rely (tau-interval-hi-rel int2))
+ (hiy (tau-interval-hi int2)))
+ (cond
+ ((and (rationalp loy)
+ (if lo-rely
+ (<= 0 loy)
+ (< 0 loy)))
+
+; If y is strictly positive, then (mod x y) lies in the interval between 0 and
+; y, which means it is bound above (strictly) by hiy.
+
+ (make-tau-interval-with-integerp-fix (domain-of-binary-arithmetic-function domx domy)
+ nil 0 t hiy))
+ ((and (rationalp hiy)
+ (if hi-rely
+ (<= hiy 0)
+ (< hiy 0)))
+
+; If y is strictly negative, then (mod x y) lies in the interval
+; between y and 0 which means it is bound below (strictly) by loy.
+
+ (make-tau-interval-with-integerp-fix (domain-of-binary-arithmetic-function domx domy)
+ t loy nil 0))
+
+ (t
+
+; Otherwise, y is not strictly positive and it is not strictly negative, which
+; means that y is sometimes 0. Thus, (mod x y) might return x and hence the
+; bounds must include those for the interval containing x, and y might be
+; negative or positive, in which case (mod x y) is bound below (strictly) by
+; loy and above (strictly) by hiy. So we combine the two intervals.
+
+ (combine-intervals1 domx lo-relx lox hi-relx hix
+ domy lo-rely loy hi-rely hiy)))))
+
+; Note that (mod x y) can return a complex-rational when its arguments are
+; complex-rationals. The conclusion of the theorem would compare that
+; complex-rational to two rationals. But I don't want to think about that!
+; So, we restrict our attention to intervals over the rationals (or integers).
+; As a result of this restriction, the domain returned by the general purpose
+; bounds-of-mod is the same as that computed by domain-of-sum-or-product, as
+; noted in our theorem below.
+
+(encapsulate
+ ()
+ (local (in-theory (disable jared-disables)))
+ (local (in-theory (disable (:t not-integerp-4b)
+ (:t not-integerp-3b)
+ (:t not-integerp-2b)
+ (:t not-integerp-1b)
+ ;; integerp-mod-1
+ ;; integerp-mod-2
+ ;; rationalp-mod
+ mod-nonnegative
+ mod-nonpositive
+ mod-zero
+ mod-positive
+ mod-negative
+ MOD-X-Y-=-X
+ MOD-X-Y-=-X+Y
+ MOD-X-Y-=-X-Y
+ ;; mod-bounds-1
+ ;; mod-bounds-2
+ the-floor-below
+ the-floor-above
+ default-times-1
+ default-times-2
+ default-plus-1
+ default-plus-2
+ REDUCE-RATIONAL-MULTIPLICATIVE-CONSTANT-<
+ |(< c (/ x)) positive c --- present in goal|
+ |(< c (/ x)) positive c --- obj t or nil|
+ |(< c (/ x)) negative c --- present in goal|
+ |(< c (/ x)) negative c --- obj t or nil|
+ |(< c (- x))|
+ |(< (/ x) c) positive c --- present in goal|
+ |(< (/ x) c) positive c --- obj t or nil|
+ |(< (/ x) c) negative c --- present in goal|
+ |(< (/ x) c) negative c --- obj t or nil|
+ |(< (/ x) (/ y))|
+ |(< (- x) c)|
+ |(< (- x) (- y))|
+ rationalp-x
+ PREFER-POSITIVE-EXPONENTS-SCATTER-EXPONENTS-<-2
+ PREFER-POSITIVE-EXPONENTS-SCATTER-EXPONENTS-EQUAL
+ default-car
+ default-cdr
+ cancel-mod-+
+ meta-integerp-correct
+ meta-rationalp-correct
+ acl2-numberp-x
+
+ in-tau-intervalp-rules
+ intervalp-rules
+ )))
+
+ (local (set-default-hints
+ '((and stable-under-simplificationp
+ '(:nonlinearp t))
+ (and stable-under-simplificationp
+ (NONLINEARP-DEFAULT-HINT++
+ ID STABLE-UNDER-SIMPLIFICATIONP HIST NIL))
+ (and stable-under-simplificationp
+ (not (cw "Jared-hint: re-enabling slower rules.~%"))
+ '(:in-theory (enable jared-disables))))))
+
+ (defthm tau-bounder-mod-correct
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (or (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int1) 'rationalp))
+ (or (equal (tau-interval-dom int2) 'integerp)
+ (equal (tau-interval-dom int2) 'rationalp))
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-mod int1 int2))
+ (in-tau-intervalp (mod x y) (tau-bounder-mod int1 int2))
+
+ (implies (not (equal (tau-interval-dom (tau-bounder-mod int1 int2)) 'integerp))
+ (equal (tau-interval-dom (tau-bounder-mod int1 int2)) 'rationalp))
+ ))
+ :rule-classes
+ ((:rewrite)
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'integerp)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-mod int1 int2))
+ (implies (not (equal (tau-interval-dom (tau-bounder-mod int1 int2)) 'integerp))
+ (equal (tau-interval-dom (tau-bounder-mod int1 int2)) 'rationalp))
+ (in-tau-intervalp (mod x y) (tau-bounder-mod int1 int2))
+ ))
+ :trigger-terms ((tau-bounder-mod int1 int2)))
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'rationalp)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-mod int1 int2))
+ (implies (not (equal (tau-interval-dom (tau-bounder-mod int1 int2)) 'integerp))
+ (equal (tau-interval-dom (tau-bounder-mod int1 int2)) 'rationalp))
+ (in-tau-intervalp (mod x y) (tau-bounder-mod int1 int2))
+ ))
+ :trigger-terms ((tau-bounder-mod int1 int2)))
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'rational)
+ (equal (tau-interval-dom int2) 'integerp)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-mod int1 int2))
+ (implies (not (equal (tau-interval-dom (tau-bounder-mod int1 int2)) 'integerp))
+ (equal (tau-interval-dom (tau-bounder-mod int1 int2)) 'rationalp))
+ (in-tau-intervalp (mod x y) (tau-bounder-mod int1 int2))
+ ))
+ :trigger-terms ((tau-bounder-mod int1 int2)))
+ (:forward-chaining
+ :corollary
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'rationalp)
+ (equal (tau-interval-dom int2) 'rationalp)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-mod int1 int2))
+ (implies (not (equal (tau-interval-dom (tau-bounder-mod int1 int2)) 'integerp))
+ (equal (tau-interval-dom (tau-bounder-mod int1 int2)) 'rationalp))
+ (in-tau-intervalp (mod x y) (tau-bounder-mod int1 int2))
+ ))
+ :trigger-terms ((tau-bounder-mod int1 int2)))
+ )))
+
+
+; -----------------------------------------------------------------
+; The Arithmetic-Logical Functions
+
+; The logical functions always take and return integers. Our theorems require
+; this. We thus know that the relations, e.g., ``lo-rel'' and ``hi-rel'',
+; bounding the various quantities are always weak. Hence, our functions for
+; computing bounds and our theorems about those functions don't traffic in
+; relations. For example, ``bounds-of-logand'' takes four argument, the bounds
+; on x and y, not eight; it returns two results, not four. (However, the
+; theorems still use (<? nil lo ...), for example, rather than (<= lo ...)
+; because lo might be a nil representing negative infinity.)
+
+; To help us keep track of these reduced signatures, we add an ``i'' for
+; ``integer'' to the bounds functions and theorem names, e.g.,
+; ibounds-for-logand and logand-tau-bounder-ibounds. The functions concerned
+; are:
+
+; LOGAND
+; LOGNOT
+; LOGIOR
+; LOGORC1
+; LOGEQV
+; LOGXOR
+; EXPT2
+; ASH
+
+; If others are added, make sure they traffic only in integers!
+
+; -----------------------------------------------------------------
+; LOGAND
+
+; We first prove a variety of lemmas to handle special cases. Some of these
+; lemmas restrict the domains of x and y to INTEGERP and some don't. But our
+; main result will apply only to INTEGERP domains, INTEGERP bounds, and weak
+; relations.
+
+; By the way, it is known that INTEGERP (logand x y), no matter what x and y
+; are.
+
+; If x is in the interval 0 <= minx <= x <= maxx and
+; y is in the interval 0 <= miny <= y <= maxy, then
+; then (logand x y) is in
+; the interval 0 <= (logand x y) <= (min maxx maxy).
+
+; Note that we can't raise the lower bound because if x and y have no bits in
+; common (as in x=4 and y=8), their logand is in fact 0.
+
+(local
+ (defthm LOGAND-tau-bounder-both-nonnegative
+ (implies (and (<= 0 minx) (<= minx x) (<= x maxx)
+ (<= 0 miny) (<= miny y) (<= y maxy))
+ (and (<= 0
+ (logand x y))
+ (<= (logand x y)
+ (min maxx maxy))))
+ :rule-classes (:rewrite :linear)))
+
+; If both x and y are negative integers, then x+y < (logand x y) <= (min x y).
+
+(defun shifts-to-all-ones (x)
+ (declare (xargs :guard (integerp x)))
+
+; Assuming x < 0, how many times must we shift x to make it -1? This turns out
+; to be either (log2 (- x)) or (+ 1 (log2 (- x))), depending on whether (- x)
+; is a power of 2 -- but I haven't proved that and don't need it.
+
+ (if (and (integerp x)
+ (< x 0))
+ (if (equal x -1)
+ 0
+ (+ 1 (shifts-to-all-ones (ash x -1))))
+ 0))
+
+(local
+ (encapsulate
+ nil
+ (local
+ (defthm expt-2-shifts-to-all-ones
+ (implies (and (integerp x) (< x 0))
+ (<= (- (expt 2 (shifts-to-all-ones x)))
+ x))
+ :rule-classes :linear))
+
+ (local
+ (defthm logand-both-negative-lower-bound
+ (implies (and (integerp x)
+ (integerp y)
+ (< x 0)
+ (< y 0))
+ (<= (- (expt 2 (max (shifts-to-all-ones x)
+ (shifts-to-all-ones y))))
+ (logand x y)))
+ :hints
+ (("Goal" :in-theory (e/d (logand)(|(logand (floor x 2) (floor y 2))| ))))
+ :rule-classes nil))
+
+ (local
+ (defthm logand-both-negative
+ (implies (and (integerp x)
+ (integerp y)
+ (< x 0)
+ (< y 0))
+ (and (<= (- (expt 2 (max (shifts-to-all-ones x)
+ (shifts-to-all-ones y))))
+ (logand x y))
+ (<= (logand x y) (min x y))))
+ :hints (("Goal" :use logand-both-negative-lower-bound))
+ :rule-classes nil))
+
+
+ (local
+ (defun shifts-to-all-ones-monotonic-hint (x y)
+ (if (and (integerp x)
+ (< x 0)
+ (integerp y)
+ (< y 0))
+ (if (equal x -1)
+ 0
+ (if (equal y -1)
+ 0
+ (shifts-to-all-ones-monotonic-hint (ash x -1) (ash y -1))))
+ 0)))
+ (local
+ (defthm shifts-to-all-ones-monotic
+ (implies (and (integerp x)
+ (integerp y)
+ (< y 0)
+ (<= x y))
+ (<= (shifts-to-all-ones y)
+ (shifts-to-all-ones x)))
+ :hints (("Goal" :induct (shifts-to-all-ones-monotonic-hint x y)))
+ :rule-classes nil))
+
+ (defthm logand-tau-bounder-both-negative
+ (implies (and (integerp x)
+ (integerp y)
+ (integerp minx)
+ (integerp miny)
+ (integerp maxx)
+ (integerp maxy)
+ (<= minx x) (<= x maxx) (< maxx 0)
+ (<= miny y) (<= y maxy) (< maxy 0))
+ (and (<= (- (expt 2 (max (shifts-to-all-ones minx)
+ (shifts-to-all-ones miny))))
+ (logand x y))
+ (<= (logand x y) (min maxx maxy))))
+ :hints (("Goal" :use ((:instance logand-both-negative)
+ (:instance shifts-to-all-ones-monotic
+ (x minx)
+ (y x))
+ (:instance shifts-to-all-ones-monotic
+ (x miny)
+ (y y)))))
+ :rule-classes (:rewrite :linear))
+ ))
+
+(local
+ (defthm logand-tau-bounder-both-either-lower-bound
+ (implies (and (integerp x)
+ (integerp y)
+ (integerp minx)
+ (integerp miny)
+ (<= minx x)
+ (<= miny y))
+ (<= (min 0 (- (expt 2 (max (shifts-to-all-ones minx)
+ (shifts-to-all-ones miny)))))
+ (logand x y)))
+ :hints (("Goal"
+ :in-theory (disable max)
+ :cases ((and (< x 0)(< y 0)))))
+ :rule-classes (:rewrite :linear)))
+
+; The following bound is not very good. Intuitively it ought to be (min maxx
+; maxy). But suppose x is -1 and maxx is 2 and y is 8 and maxy is 10: Then the
+; (logand x y) = y = 8, but (min maxx maxy) = 2. Oops. The problem is that if
+; x and y are allowed to range all over, then (logand x y) might be x or y as
+; the other takes on the value -1. But if we have to describe the bounds in
+; terms of maxx and maxy, then the best we can do is be ready for either x or
+; y, e.g., (max maxx maxy).
+
+(local
+ (defthm logand-tau-bounder-both-either-upper-bound
+ (implies (and (integerp x)
+ (integerp y)
+ (<= x maxx)
+ (<= y maxy))
+ (<= (logand x y) (max maxx maxy)))
+ :hints (("Goal"
+ :cases ((and (< x 0)(< y 0)))))
+ :rule-classes (:rewrite :linear)))
+
+; Unfortunately, the analytical bounds derived above are sometimes not very
+; good. We therefore use an empirical method to compute the bounds for logand
+; over small intervals.
+
+(local (include-book "find-minimal-2d"))
+
+(defun find-minimal-logand2 (x loy hiy min)
+ (declare (xargs :measure (nfix (- (+ 1 (ifix hiy)) (ifix loy)))
+ :guard (and (integerp x)
+ (integerp loy)
+ (integerp hiy)
+ (or (null min) (integerp min)))
+ :verify-guards nil))
+ (cond
+ ((mbe :logic (not (and (integerp loy)
+ (integerp hiy)))
+ :exec nil)
+ min)
+ ((> loy hiy) min)
+ (t (find-minimal-logand2
+ x
+ (+ 1 loy) hiy
+ (if (or (null min)
+ (< (logand x loy) min))
+ (logand x loy)
+ min)))))
+
+(defun find-minimal-logand1 (lox hix loy hiy min)
+ (declare (xargs :measure (nfix (- (+ 1 (ifix hix)) (ifix lox)))
+ :guard (and (integerp lox)
+ (integerp hix)
+ (integerp loy)
+ (integerp hiy)
+ (or (null min) (integerp min)))
+ :verify-guards nil))
+ (cond
+ ((mbe :logic (not (and (integerp lox)
+ (integerp hix)))
+ :exec nil)
+ min)
+ ((> lox hix) min)
+ (t (find-minimal-logand1
+ (+ 1 lox) hix
+ loy hiy
+ (find-minimal-logand2 lox loy hiy min)))))
+
+; This is the wrapper that initializes the running minimal, min, to nil. But
+; all our lemmas except the last will be about the two recursive functions
+; above.
+
+(defun find-minimal-logand (lox hix loy hiy)
+ (declare (xargs :guard (and (integerp lox)
+ (integerp hix)
+ (integerp loy)
+ (integerp hiy))
+ :verify-guards nil))
+ (find-minimal-logand1 lox hix loy hiy nil))
+
+(local
+ (defthm find-minimal-logand-correct
+ (implies (and (integerp lox)
+ (integerp hix)
+ (integerp loy)
+ (integerp hiy)
+ (integerp x)
+ (<= lox x)
+ (<= x hix)
+ (integerp y)
+ (<= loy y)
+ (<= y hiy))
+ (and (integerp (find-minimal-logand lox hix loy hiy))
+ (<= (find-minimal-logand lox hix loy hiy)
+ (logand x y))))
+ :hints (("Goal" :use (:functional-instance
+ find-minimal-correct
+ (fmla (lambda (x y) (logand x y)))
+ (find-minimal2 find-minimal-logand2)
+ (find-minimal1 find-minimal-logand1)
+ (find-minimal find-minimal-logand))))))
+
+
+(in-theory (disable find-minimal-logand))
+
+(local (include-book "find-maximal-2d"))
+
+(defun find-maximal-logand2 (x loy hiy max)
+ (declare (xargs :measure (nfix (- (+ 1 (ifix hiy)) (ifix loy)))
+ :guard (and (integerp x)
+ (integerp loy)
+ (integerp hiy)
+ (or (null max) (integerp max)))
+ :verify-guards nil))
+ (cond
+ ((mbe :logic (not (and (integerp loy)
+ (integerp hiy)))
+ :exec nil)
+ max)
+ ((> loy hiy) max)
+ (t (find-maximal-logand2
+ x
+ (+ 1 loy) hiy
+ (if (or (null max)
+ (> (logand x loy) max))
+ (logand x loy)
+ max)))))
+
+(defun find-maximal-logand1 (lox hix loy hiy max)
+ (declare (xargs :measure (nfix (- (+ 1 (ifix hix)) (ifix lox)))
+ :guard (and (integerp lox)
+ (integerp hix)
+ (integerp loy)
+ (integerp hiy)
+ (or (null max) (integerp max)))
+ :verify-guards nil))
+ (cond
+ ((mbe :logic (not (and (integerp lox)
+ (integerp hix)))
+ :exec nil)
+ max)
+ ((> lox hix) max)
+ (t (find-maximal-logand1
+ (+ 1 lox) hix
+ loy hiy
+ (find-maximal-logand2 lox loy hiy max)))))
+
+; This is the wrapper that initializes the running maximal, max, to nil. But
+; all our lemmas except the last will be about the two recursive functions
+; above.
+
+(defun find-maximal-logand (lox hix loy hiy)
+ (declare (xargs :guard (and (integerp lox)
+ (integerp hix)
+ (integerp loy)
+ (integerp hiy))
+ :verify-guards nil))
+ (find-maximal-logand1 lox hix loy hiy nil))
+
+; A nice way to demo the effectiveness of guard verification is to admit these
+; functions with :verify-guards nil and run
+
+; (time$ (find-minimal-logand 0 1023 0 1023))
+
+; then do the verify-guards below and repeat the timing. You get about a 14x
+; speed up.
+
+(verify-guards find-minimal-logand2)
+(verify-guards find-minimal-logand1)
+(verify-guards find-minimal-logand)
+(verify-guards find-maximal-logand2)
+(verify-guards find-maximal-logand1)
+(verify-guards find-maximal-logand)
+
+(local
+ (defthm find-maximal-logand-correct
+ (implies (and (integerp lox)
+ (integerp hix)
+ (integerp loy)
+ (integerp hiy)
+ (integerp x)
+ (<= lox x)
+ (<= x hix)
+ (integerp y)
+ (<= loy y)
+ (<= y hiy))
+ (and (integerp (find-maximal-logand lox hix loy hiy))
+ (>= (find-maximal-logand lox hix loy hiy)
+ (logand x y))))
+ :hints (("Goal" :use (:functional-instance
+ find-maximal-correct
+ (fmla (lambda (x y) (logand x y)))
+ (find-maximal2 find-maximal-logand2)
+ (find-maximal1 find-maximal-logand1)
+ (find-maximal find-maximal-logand))))))
+
+(in-theory (disable find-maximal-logand))
+
+(defthm find-minimal-logand-below-find-maximal-logand
+ (implies (and (integerp lox)
+ (integerp hix)
+ (integerp loy)
+ (integerp hiy)
+ (<= lox hix)
+ (<= loy hiy))
+ (<= (find-minimal-logand lox hix loy hiy)
+ (find-maximal-logand lox hix loy hiy)))
+ :hints (("Goal"
+ :use
+ ((:instance find-minimal-logand-correct
+ (x lox)
+ (y loy))
+ (:instance find-maximal-logand-correct
+ (x lox)
+ (y loy)))
+ :in-theory (disable find-minimal-logand-correct
+ find-maximal-logand-correct)))
+ :rule-classes :linear)
+
+(defconst *logand-empirical-threshhold* (* 1024 1024))
+
+(defun worth-computingp (lox hix loy hiy)
+ (declare (xargs :guard (and (integerp lox)
+ (integerp hix)
+ (integerp loy)
+ (integerp hiy))))
+
+; This function is for heuristic purposes only. It determines whether we
+; compute the exact minimal and maximal of logand over a pair of intervals by
+; computing logand for each combination. The threshhold chosen above just
+; allows us to compute the bounds on 10-bit x 10-bit logands. It takes a total
+; of 0.066123 seconds to compute the two bounds on intervals of this size,
+; running on a 2.6 GHz Intel Core i7 Macbook Pro in CCL. Note also that the
+; way we actually compute the minimal and maximal (see bounds-of-logand below)
+; is to compute them separately in two passes over the matrix. We could
+; presumably save a little overhead by computing them in one pass.
+
+ (< (* (if (<= lox hix) (+ 1 (- hix lox)) 0)
+ (if (<= loy hiy) (+ 1 (- hiy loy)) 0))
+ *logand-empirical-threshhold*))
+
+(in-theory (disable worth-computingp))
+
+; This function assumes both domains are INTEGERp.
+
+(defun tau-bounder-logand (int1 int2)
+ (declare (xargs :guard (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'integerp))))
+ (let ((lox (tau-interval-lo int1))
+ (hix (tau-interval-hi int1))
+ (loy (tau-interval-lo int2))
+ (hiy (tau-interval-hi int2)))
+ (cond
+ ((and lox hix loy hiy (worth-computingp lox hix loy hiy))
+ (make-tau-interval
+ 'integerp
+ nil
+ (find-minimal-logand lox hix loy hiy)
+ nil
+ (find-maximal-logand lox hix loy hiy)))
+ ((and lox (<= 0 lox) ; x nonnegative
+ loy (<= 0 loy)) ; y nonnegative
+ (make-tau-interval 'integerp nil 0 nil (and hix hiy (min hix hiy))))
+ ((and hix (< hix 0) ; x negative
+ hiy (< hiy 0)) ; y negative
+ (make-tau-interval
+ 'integerp
+ nil
+ (and lox loy
+ (- (expt 2 (max (shifts-to-all-ones lox)
+ (shifts-to-all-ones loy)))))
+ nil
+ (min hix hiy)))
+ ((and hix (<= hix 0) ; x negative
+ loy (<= 0 loy)) ; y nonnegative
+ (make-tau-interval 'integerp nil 0 nil hiy))
+ ((and hiy (<= hiy 0) ; x nonnegative
+ lox (<= 0 lox)) ; y negative
+ (make-tau-interval 'integerp nil 0 nil hix))
+ ((and lox (<= 0 lox)) ; x nonnegative, y is either
+ (make-tau-interval 'integerp nil 0 nil (and hix hiy (max hix hiy))))
+ ((and loy (<= 0 loy)) ; y nonnegative, x is either
+ (make-tau-interval 'integerp nil 0 nil (and hix hiy (max hix hiy))))
+ (t
+ (make-tau-interval
+ 'integerp
+ nil
+ (and lox loy (min 0 (- (expt 2 (max (shifts-to-all-ones lox)
+ (shifts-to-all-ones loy))))))
+ nil
+ (and hix hiy (max hix hiy)))))))
+
+(local
+ (defthm min-max-properties
+ (and (<= (min x y) x)
+ (<= (min x y) y)
+ (<= x (max x y))
+ (<= y (max x y)))
+ :rule-classes :linear))
+
+(local (defthm |tau-intervalp tau-bounder-logand|
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'integerp))
+ (tau-intervalp (tau-bounder-logand int1 int2)))
+ :hints (("Goal" :in-theory (disable min max)))
+ ))
+
+(local (defthm |tau-interval-dom tau-bounder-logand|
+ (equal (tau-interval-dom (tau-bounder-logand int1 int2)) 'integerp)))
+
+(local (defthm |in-tau-intervalp tau-bounder-logand|
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'integerp)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (in-tau-intervalp (logand x y) (tau-bounder-logand int1 int2)))
+ :hints (("Goal" :in-theory (disable min max)))))
+
+(defthm tau-bounder-logand-correct
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'integerp)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-logand int1 int2))
+ (in-tau-intervalp (logand x y) (tau-bounder-logand int1 int2))
+
+ (equal (tau-interval-dom (tau-bounder-logand int1 int2)) 'integerp)
+ ))
+ :hints (("Goal" :in-theory (disable tau-bounder-logand)))
+ :rule-classes
+ ((:rewrite)
+ (:forward-chaining :trigger-terms ((tau-bounder-logand int1 int2)))
+ ))
+
+; These bounds aren't always very tight. See the essay On the Accuracy of the
+; LOGAND Bounder at the end of this book.
+
+; -----------------------------------------------------------------
+; LOGNOT
+
+(defun tau-bounder-lognot (int)
+ (declare (xargs :guard (tau-intervalp int)))
+ (let ((lo (tau-interval-lo int))
+ (hi (tau-interval-hi int)))
+ (make-tau-interval 'integerp
+ nil (if hi (- (- hi) 1) nil)
+ nil (if lo (- (- lo) 1) nil))))
+
+(local (defthm |tau-intervalp tau-bounder-lognot|
+ (implies (and (tau-intervalp int)
+ (equal (tau-interval-dom int) 'integerp))
+ (and (tau-intervalp (tau-bounder-lognot int))
+ (equal (tau-interval-dom (tau-bounder-lognot int)) 'integerp)
+ ))
+ :hints (("Goal" :in-theory (disable min max)))
+ ))
+
+(defthm tau-bounder-lognot-correct
+ (implies (and (tau-intervalp int1)
+ (equal (tau-interval-dom int1) 'integerp)
+ (in-tau-intervalp x int1))
+ (and (tau-intervalp (tau-bounder-lognot int1))
+ (in-tau-intervalp (lognot x) (tau-bounder-lognot int1))
+
+ (equal (tau-interval-dom (tau-bounder-lognot int1)) 'integerp)
+ ))
+ :hints (("Goal" :in-theory (enable lognot)))
+ :rule-classes
+ ((:rewrite)
+ (:forward-chaining :trigger-terms ((tau-bounder-lognot int1)))
+ ))
+
+; -----------------------------------------------------------------
+; LOGIOR
+
+; (Logior x y) = (lognot (logand (lognot x) (lognot y)))
+
+; Therefore, we'll first try bounding it by composing our existing bounds functions.
+; My old bound theorem for logior only worked for natp x and y. It was:
+; (MIN MINX MINY) <= (LOGIOR X Y) <= (EXPT 2 (LOG2 (MAX MAXX MAXY))).
+
+(defun tau-bounder-logior (int1 int2)
+ (declare (xargs :guard (and (tau-intervalp int1)
+ (equal (tau-interval-dom int1) 'integerp)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int2) 'integerp))
+ :guard-hints (("Goal" :use (
+ (:instance |tau-intervalp tau-bounder-lognot|
+ (int (tau-bounder-logand
+ (tau-bounder-lognot int1)
+ (tau-bounder-lognot int2))))
+ (:instance |tau-intervalp tau-bounder-logand|
+ (int1 (tau-bounder-lognot int1))
+ (int2 (tau-bounder-lognot int2)))
+ (:instance |tau-intervalp tau-bounder-lognot|
+ (int int1))
+ (:instance |tau-intervalp tau-bounder-lognot|
+ (int int2)))
+ :in-theory (disable tau-intervalp tau-bounder-lognot tau-bounder-logand)))))
+ (tau-bounder-lognot
+ (tau-bounder-logand
+ (tau-bounder-lognot int1)
+ (tau-bounder-lognot int2))))
+
+(local (defthm |tau-intervalp tau-bounder-logior|
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'integerp))
+ (and (tau-intervalp (tau-bounder-logior int1 int2))
+ (equal (tau-interval-dom (tau-bounder-logior int1 int2)) 'integerp)
+ ))
+ :hints (("Goal"
+ :in-theory
+ (e/d (logior)
+ (tau-intervalp
+ in-tau-intervalp
+ tau-bounder-logand
+ tau-bounder-lognot
+ lognot-logand ; rewrite rule in arithmetic-5 that folds logior
+ (:executable-counterpart make-tau-interval)))))
+ ))
+
+(defthm tau-bounder-logior-correct
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'integerp)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-logior int1 int2))
+ (in-tau-intervalp (logior x y) (tau-bounder-logior int1 int2))
+
+ (equal (tau-interval-dom (tau-bounder-logior int1 int2)) 'integerp)
+ ))
+ :hints
+ (("Goal"
+ :in-theory
+ (e/d (logior)
+ (tau-intervalp
+ in-tau-intervalp
+ tau-bounder-logand
+ tau-bounder-lognot
+ lognot-logand ; rewrite rule in arithmetic-5 that folds logior
+ (:executable-counterpart make-tau-interval)))))
+ :rule-classes
+ ((:rewrite)
+ (:forward-chaining :trigger-terms ((tau-bounder-logior int1 int2)))
+ ))
+
+; -----------------------------------------------------------------
+; LOGORC1
+
+; (logorc1 x y) = (logior (lognot x) y)
+
+(defun tau-bounder-logorc1 (int1 int2)
+ (declare (xargs :guard (and (tau-intervalp int1)
+ (equal (tau-interval-dom int1) 'integerp)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int2) 'integerp))
+ :guard-hints (("Goal" :use (
+ (:instance |tau-intervalp tau-bounder-logior|
+ (int1 (tau-bounder-lognot int1))
+ (int2 int2))
+ (:instance |tau-intervalp tau-bounder-lognot|
+ (int int1)))
+ :in-theory (disable tau-intervalp tau-bounder-logior tau-bounder-lognot)))))
+ (tau-bounder-logior
+ (tau-bounder-lognot int1)
+ int2))
+
+(local (defthm |tau-intervalp tau-bounder-logorc1|
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'integerp))
+ (and (tau-intervalp (tau-bounder-logorc1 int1 int2))
+ (equal (tau-interval-dom (tau-bounder-logorc1 int1 int2)) 'integerp)
+ ))
+ :hints (("Goal"
+ :in-theory
+ (e/d (logorc1)
+ (tau-intervalp
+ in-tau-intervalp
+ tau-bounder-logand
+ tau-bounder-lognot
+ |(logior y x)| ; commutative rule for logior
+ (:executable-counterpart make-tau-interval)))))
+ ))
+
+(defthm tau-bounder-logorc1-correct
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'integerp)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-logorc1 int1 int2))
+ (in-tau-intervalp (logorc1 x y) (tau-bounder-logorc1 int1 int2))
+
+ (equal (tau-interval-dom (tau-bounder-logorc1 int1 int2)) 'integerp)
+ ))
+ :hints
+ (("Goal"
+ :in-theory
+ (e/d (logorc1)
+ (tau-intervalp
+ in-tau-intervalp
+ tau-bounder-logior
+ tau-bounder-lognot
+ |(logior y x)| ; commutative rule for logior
+ (:executable-counterpart make-tau-interval)))))
+ :rule-classes
+ ((:rewrite)
+ (:forward-chaining :trigger-terms ((tau-bounder-logorc1 int1 int2)))
+ ))
+
+; -----------------------------------------------------------------
+; LOGEQV
+
+; (logeqv x y) = (logand (logorc1 x y) (logorc1 y x))
+
+(defun tau-bounder-logeqv (int1 int2)
+ (declare (xargs :guard (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'integerp))
+ :guard-hints (("Goal" :use (
+ (:instance |tau-intervalp tau-bounder-logand|
+ (int1 (tau-bounder-logorc1 int1 int2))
+ (int2 (tau-bounder-logorc1 int2 int1)))
+ (:instance |tau-intervalp tau-bounder-logorc1|
+ (int1 int1)
+ (int2 int2))
+ (:instance |tau-intervalp tau-bounder-logorc1|
+ (int1 int2)
+ (int2 int1)))
+ :in-theory (disable tau-intervalp tau-bounder-logand tau-bounder-logorc1)))))
+ (tau-bounder-logand
+ (tau-bounder-logorc1 int1 int2)
+ (tau-bounder-logorc1 int2 int1)))
+
+(local (defthm |tau-intervalp tau-bounder-logeqv|
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'integerp))
+ (and (tau-intervalp (tau-bounder-logeqv int1 int2))
+ (equal (tau-interval-dom (tau-bounder-logeqv int1 int2)) 'integerp)
+ ))
+ :hints (("Goal"
+ :in-theory
+ (e/d (logeqv)
+ (tau-intervalp
+ in-tau-intervalp
+ tau-bounder-logand
+ tau-bounder-logorc1
+ (:executable-counterpart make-tau-interval)))))
+ ))
+
+(defthm tau-bounder-logeqv-correct
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'integerp)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-logeqv int1 int2))
+ (in-tau-intervalp (logeqv x y) (tau-bounder-logeqv int1 int2))
+
+ (equal (tau-interval-dom (tau-bounder-logeqv int1 int2)) 'integerp)
+ ))
+ :hints
+ (("Goal"
+ :in-theory
+ (e/d (logeqv)
+ (tau-intervalp
+ in-tau-intervalp
+ logorc1
+ tau-bounder-logand
+ tau-bounder-logorc1
+ (:executable-counterpart make-tau-interval)))))
+ :rule-classes
+ ((:rewrite)
+ (:forward-chaining :trigger-terms ((tau-bounder-logeqv int1 int2)))
+ ))
+
+; -----------------------------------------------------------------
+; LOGXOR
+
+; (logxor x y) = (lognot (logeqv x y))
+
+(defun tau-bounder-logxor (int1 int2)
+ (declare (xargs :guard (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'integerp))
+ :guard-hints (("Goal" :use (
+ (:instance |tau-intervalp tau-bounder-lognot|
+ (int (tau-bounder-logeqv int1 int2)))
+ (:instance |tau-intervalp tau-bounder-logeqv|
+ (int1 int1)
+ (int2 int2)))
+ :in-theory (disable tau-intervalp tau-bounder-lognot tau-bounder-logeqv)))))
+ (tau-bounder-lognot
+ (tau-bounder-logeqv int1 int2)))
+
+(defthm tau-bounder-logxor-correct
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'integerp)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-logxor int1 int2))
+ (in-tau-intervalp (logxor x y) (tau-bounder-logxor int1 int2))
+
+ (equal (tau-interval-dom (tau-bounder-logxor int1 int2)) 'integerp)
+ ))
+ :hints
+ (("Goal"
+ :in-theory
+ (e/d (logxor)
+ (tau-intervalp
+ in-tau-intervalp
+ logeqv
+ tau-bounder-lognot
+ tau-bounder-logeqv
+ (:executable-counterpart make-tau-interval)))))
+ :rule-classes
+ ((:rewrite)
+ (:forward-chaining :trigger-terms ((tau-bounder-logxor int1 int2)))
+ ))
+
+; -----------------------------------------------------------------
+; (EXPT 2 y)
+
+; While we could bound (EXPT x y), we choose for the moment to focus on just
+; what we need for ASH, which is (EXPT 2 y). The situations for x < 0 and (not
+; (integerp x)) are messy. We assume y is an INTEGERP.
+
+; Note that this ``i''-bounds function uncharacteristically returns the domain
+; pair of (expt 2 y) as well. The trouble is that while y must be an integer
+; and (expt 2 y) is an integer for non-negative y, (expt 2 y) is a rational for
+; negative y.
+
+(defun tau-bounder-expt2 (int2)
+ (declare (xargs :guard (and (tau-intervalp int2)
+ (equal (tau-interval-dom int2) 'integerp))))
+ (let ((loy (tau-interval-lo int2))
+ (hiy (tau-interval-hi int2)))
+ (if loy
+ (if (<= 0 loy)
+ (if hiy
+ (make-tau-interval 'integerp nil (expt 2 loy) nil (expt 2 hiy))
+ (make-tau-interval 'integerp nil (expt 2 loy) nil nil))
+ (if hiy
+ (make-tau-interval 'rationalp nil (expt 2 loy) nil (expt 2 hiy))
+ (make-tau-interval 'rationalp nil (expt 2 loy) nil nil)))
+ (if hiy
+ (make-tau-interval 'rationalp t 0 nil (expt 2 hiy))
+ (make-tau-interval 'rationalp t 0 nil nil)))))
+
+; Warning: We only deal with (EXPT 2 y), for integerp y!
+
+(defthm tau-bounder-expt2-correct
+ (implies (and (tau-intervalp int2)
+ (equal (tau-interval-dom int2) 'integerp)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-expt2 int2))
+ (in-tau-intervalp (expt 2 y) (tau-bounder-expt2 int2))
+
+ (implies (not (equal (tau-interval-dom (tau-bounder-expt2 int2)) 'integerp))
+ (equal (tau-interval-dom (tau-bounder-expt2 int2)) 'rationalp))
+ ))
+ :rule-classes
+ ((:rewrite)
+ (:forward-chaining :trigger-terms ((tau-bounder-expt2 int2)))
+ ))
+
+; -----------------------------------------------------------------
+; (EXPT r i)
+
+; Both parameters of this tau-bounder are intervals. So tau-system can accept
+; it. It recognizes specific case r=2 and delegates it to the previous bounder
+; and returns the entire rational set in the general case.
+
+(defun tau-bounder-expt (intr inti)
+ (declare (xargs :guard (and (tau-intervalp intr)
+ (tau-intervalp inti)
+ (equal (tau-interval-dom intr) 'integerp)
+ (equal (tau-interval-dom inti) 'integerp))))
+ (if
+ (and
+ (equal (tau-interval-lo intr) 2)
+ (equal (tau-interval-hi intr) 2))
+ (tau-bounder-expt2 inti)
+ (make-tau-interval 'rationalp nil nil nil nil)))
+
+; Warning: We only deal with (EXPT 2 i), for integerp i!
+
+(defthm tau-bounder-expt-correct
+ (implies (and (tau-intervalp intr)
+ (equal (tau-interval-dom intr) 'integerp)
+ (in-tau-intervalp r intr)
+ (tau-intervalp inti)
+ (equal (tau-interval-dom inti) 'integerp)
+ (in-tau-intervalp i inti))
+ (and (tau-intervalp (tau-bounder-expt intr inti))
+ (in-tau-intervalp (expt r i) (tau-bounder-expt intr inti))
+
+ (implies (not (equal (tau-interval-dom (tau-bounder-expt intr inti)) 'integerp))
+ (equal (tau-interval-dom (tau-bounder-expt intr inti)) 'rationalp))
+ ))
+ :rule-classes
+ ((:rewrite)
+ (:forward-chaining :trigger-terms ((tau-bounder-expt intr inti)))
+ ))
+
+; -----------------------------------------------------------------
+; ASH
+
+; (ash x y) = (floor (* (ifix x) (expt 2 y)) 1)
+
+(defun tau-bounder-ash (int1 int2)
+ (declare (xargs :guard (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'integerp))))
+ (tau-bounder-floor
+ (tau-bounder-* int1
+ (tau-bounder-expt2 int2))
+ (make-tau-interval 'integerp nil 1 nil 1)))
+
+; Weakness: See the little essay immediately below on why I need the :use hint!
+; It opens a raft of difficulties.
+
+(defthm tau-bounder-ash-correct
+ (implies (and (tau-intervalp int1)
+ (tau-intervalp int2)
+ (equal (tau-interval-dom int1) 'integerp)
+ (equal (tau-interval-dom int2) 'integerp)
+ (in-tau-intervalp x int1)
+ (in-tau-intervalp y int2))
+ (and (tau-intervalp (tau-bounder-ash int1 int2))
+ (in-tau-intervalp (ash x y) (tau-bounder-ash int1 int2))
+
+ (equal (tau-interval-dom (tau-bounder-ash int1 int2)) 'integerp)
+ ))
+ :hints
+ (("Goal"
+ :use ((:instance tau-bounder-floor-correct
+ (x (* x (expt 2 y)))
+ (int1 (TAU-BOUNDER-* INT1 (TAU-BOUNDER-EXPT2 INT2)))
+ (y 1)
+ (int2 (MAKE-TAU-INTERVAL 'INTEGERP NIL 1 NIL 1))))
+ :in-theory
+ (e/d (ash)
+ (tau-intervalp
+ in-tau-intervalp
+ tau-bounder-floor
+ tau-bounder-*
+ tau-bounder-expt2
+ tau-bounder-floor-correct ; <--- since I :use it above
+ (:executable-counterpart make-tau-interval)))))
+ :rule-classes
+ ((:rewrite)
+ (:forward-chaining :trigger-terms ((tau-bounder-ash int1 int2)))
+ ))
+
+; On Why We Need the :USE Hint in the Proof of that the ASH Bounder is Correct
+
+; Consider just the proof of the first concluding conjunct above,
+
+; (TAU-INTERVALP (TAU-BOUNDER-ASH INT1 INT2)).
+
+; After opening up, this is:
+
+; (TAU-INTERVALP (TAU-BOUNDER-FLOOR
+; (TAU-BOUNDER-* INT1
+; (TAU-BOUNDER-EXPT2 INT2))
+; (MAKE-TAU-INTERVAL 'INTEGERP NIL 1 NIL 1)))
+
+; We mean to prove this by :forward-chaining from the three correctness lemmas
+; for FLOOR, *, and EXPT2. To use the TAU-BOUNDER-FLOOR-CORRECT theorem, we
+; need to have derived:
+
+; [1] (tau-intervalp (TAU-BOUNDER-* INT1 (TAU-BOUNDER-EXPT2 INT2)))
+
+; [2] (tau-intervalp (MAKE-TAU-INTERVAL 'INTEGERP NIL 1 NIL 1))
+
+; We could get [2] in one of several ways but we don't pursue those
+; improvements because we'd still be blocked from our ultimate ASH goal by the
+; problems raised by [1]. But to elaborate slightly on fixing [2], one
+; solution would be to add the :forward-chaining rule:
+
+; (defthm make-tau-interval-x-x
+; (implies (integerp x)
+; (and (equal (tau-interval-dom (make-tau-interval 'integerp nil x nil x)) 'integerp)
+; (tau-intervalp (make-tau-interval 'integerp nil x nil x))
+; (in-tau-intervalp x (make-tau-interval 'integerp nil x nil x))))
+; :rule-classes ((:forward-chaining :trigger-terms ((make-tau-interval 'integerp nil x nil x)))))
+
+; Alternatively we could rely on the fact that forward-chaining evaluates
+; ground terms and [2] is a ground term, but we have (:executable-counterpart
+; make-tau-interval) disabled in these ``second style'' proofs so as to simplify
+; the application of our basic rules for tearing apart IN-TAU-INTERVALP and
+; INTERVALP of MAKE-TAU-INTERVAL expressions. (If ground MAKE-TAU-INTERVALs are
+; evaluated, we'd have to recode some of those rules so that, for example
+; (in-tau-intervalp x (cons dom (cons (cons lo-rel lo) (cons hi-rel hi)))) is
+; expanded appropriately.)
+
+; But as noted, we don't try to fix [2] because we're still blocked by [1].
+
+; What does it take to get [1]? To apply tau-bounder-*-correct we need to
+; know that its second argument, (TAU-BOUNDER-EXPT2 INT2), is an intervalp
+; [which we can get via tau-bounder-expt2-correct] and we need to know that
+; the domain of (TAU-BOUNDER-EXPT2 INT2) is either 'INTEGERP or 'RATIONALP.
+; Now in fact the domain of (TAU-BOUNDER-EXPT2 INT2) is one of those two, but
+; we don't know which. (If Y is non-negative, the domain is INTEGERP and if Y
+; is negative it is RATIONALP -- but this fact, like the fact that
+; tau-bounder-* is weak because it doesn't tell us when the product is an
+; INTEGERP -- is not proved here.) But even we had proved this fact, it
+; wouldn't help us because we don't know the sign of Y, forward-chaining
+; doesn't do case analysis, and forward-chaining cannot propagage disjunctions.
+; The only way to win here is to :USE some rule that puts the necessary
+; conditions into the theorem and let the simplifier consider the cases.
+
+; This perhaps calls into question the whole ``second style'' of proof, based
+; on forward-chaining from previously proved correctness theorems. However,
+; revisit the discussion on the role of forward-chaining in the comment
+; preceding tau-bounder-floor-correct.
+
+; -----------------------------------------------------------------
+; There are no events beyond this point
+; -----------------------------------------------------------------
+
+; On the Accuracy of the LOGAND Bounder
+;
+; [This essay was written to be somewhat self-contained and so repeats some basic
+; facts before getting to the gist of the matter.]
+;
+; Tau is capable of tracking finite intervals. For example, suppose x is known
+; to be an integer that lies between two known integer constants, e.g., 0 <= x <= 15.
+; This information is treated by tau as a ``type'' in the sense that it can
+; be propagated by signatures and other rules available to the system.
+;
+; Suppose the lower and upper bounds of the interval on x are given by the
+; integers lox (``low x'') and hix (``high x''), and the bounds on y are
+; loy and hiy. Obviously then, the bounds on (+ x y) are lox+loy and hix+hiy.
+; Note that the bounds on (+ x y) are constants, computed by summing the constant
+; bounds on the inputs x and y.
+;
+; In the same fashion, we wish to bound (logand x y).
+;
+; A little bit of analysis can provide some rough bounds. For example, if both
+; input intervals are non-negative (i.e., 0 <= lox and 0 <= loy), then
+; (logand x y) can be no smaller than 0 and no larger than the minimum of hix and
+; hiy. If both input intervals are strictly negative, (logand x y) is negative
+; and can be no smaller than
+;
+; (- (expt 2 (max (shifts-to-all-ones lox)
+; (shifts-to-all-ones loy))))
+;
+; and no greater than the minimum of hix and hiy. Here, shifts-to-all-ones is
+; the function that determines how many times its argument must be right
+; shifted to produce -1 (and is thus ceiling of the log base 2 of the negative
+; of its argument; e.g., (shifts-to-all-ones -9) = 4).
+;
+; Using this analysis, if -7 <= x <= -1 and -6 <= y <= -2, then
+; -8 <= (logand x y) <= -2.
+;
+; One can similarly work out analytical bounds for when one of the inputs is
+; strictly negative and the other is non-negative, and for when both can range
+; over both the negatives and non-negatives.
+;
+; I have worked out such analytical bounds and proved that they are sufficient
+; to actually contain all values of the relevant logand expressions. However,
+; my analytical bounds of logand can be quite inaccurate. [Footnote: I say
+; ``my analytical bounds'' because one could probably work out more accurate
+; ones and, indeed, I illustrate the approach below but don't pursue it.]
+;
+; To give an example of the inaccuracy of my bounds, consider a simple case
+; where both x and y range over non-negative values. Analytically we say that
+; (logand x y) in this cases ranges from 0 to the minimal of the upper bounds of
+; x and y. This is obvious since logand will only turn some bits off.
+;
+; But now consider this example:
+;
+; lox = (+ (expt 2 31) 8)
+; hix = (+ (expt 2 31) 8 7)
+; loy = (+ (expt 2 30) 8)
+; hiy = (+ (expt 2 30) 8 7) = 1073741839
+;
+; The analytical answer says 0 <= (logand x y) <= 1073741839. But actually, we
+; can see that 8 <= (logand x y) <= 15. Thus both our lower analytical bound and
+; our upper analytical bound can be wildly inaccurate.
+;
+; Part of the project of computing bounds for logand was to address the
+; inaccuracy of this analysis. Notice that despite the magnitude of the bounds
+; above, there are only 8 possible values of x and 8 possible values of y.
+; Hence, there are only 64 combinations and it is straightforward to evaluate
+; logand on those 64 combinations to compute the actual bounds. I call this the
+; ``empirical'' method instead of the (my) ``analytical'' method.
+;
+; Of course, the empirical method cannot be applied to problems beyond a certain
+; small size (as measured by the product of the widths of the two input
+; intervals). This size, called the ``empirical threshhold,'' is set currently
+; at 1024^2 or 1,048,576. This allows us to bound 10-bit by 10-bit logands
+; perfectly. The time it takes to compute both the upper and lower bounds for an
+; interval of this size is 0.066123 seconds on a Macbook Pro running CCL on an
+; 2.6 GHz Intel Core i7. Of course, we might wish to lower the threshhold to
+; avoid the empirical computation if we decide that it is just too expensive in
+; actual proofs. My gut feeling is that since this only happens when we
+; encounter LOGANDs, we can probably afford the 10-bit by 10-bit test.
+;
+; The approach we adopted in tau-bounder-logand is to first determine the size
+; of the problem. If it is small enough, we use the empirical method.
+; Otherwise, we use the analytic method. We call this combined scheme the
+; ``heuristic method.''
+;
+; In tau-bounder-logand-correct, above, I prove that the heuristic bounds are
+; logically sufficient: they are always wide enough to include the right answer.
+;
+; Two ill-formed questions swirl in my mind. It would be nice if someone with a
+; more analytical mindset (or a sense of having more time!) made the two
+; questions more precise so they could actually be answered. One question is:
+; if we didn't use the empirical method to make some of our bounds perfect,
+; how bad would it be? Of course, the example above shows that the error can be
+; about as bad as you can imagine. Indeed, if the two inputs have exactly one
+; bit in common, that bit determines both the upper and lower bounds of the interval
+; (they're equal) and all the other bits in the two inputs contribute only to the
+; size of the error! But really, how often do these bad cases happen over random
+; input intervals? This clearly has a precise, abstract answer but I haven't
+; pursued it.
+;
+; The second question is: how much more accurate do it get if we use the
+; empirical method on the small problems? This could help us decide whether the
+; empirical method is worth its cost.
+;
+; Of course, the more meaningful cost/benefit question is: how many more
+; theorems (that we're likely to run into) would we prove due to the use of the
+; empirical method on small problems? In the absence of an answer to that more
+; meaningful question, an accuracy analysis of tau-bounder-logand is sort of
+; irrelevant or, at least to me, of secondary importance. That's why I'm
+; satisfied with a proof of correctness and a half-baked experimental
+; determination that the empirical methods sort of seems to probably improve
+; accuracy by a meaningful amount.
+;
+; With these apologies, let me now present my experimental setup and results.
+;
+; Let k be some natural number. In the tests below, I use k=5, 10, 15, ..., 50.
+; I call k the ``radius'' of a test. In a test of radius k, I consider all
+; combinations of two intervals whose upper and lower bounds are between -k and
+; k. I call this the test set. More precisely, the test set for radius k is the
+;
+; {(make-tau-interval 'integerp nil lo nil hi) : (-k <= lo <= k) & (lo <= hi <= k)}
+;
+; Note: hi must be at least lo for the interval to be legal.
+;
+; The test for radius k then chooses every possible combination of intervals from
+; the test set and assesses the accuracy of the logand bounder, with and without
+; the empirical method.
+;
+; But how do I ``assess accuracy?'' The only way I know is to use the empirical
+; method to determine the true bounds of each combination.
+;
+; Since the number of combinations in a test set increases exponentially with k,
+; this experiment gets computationally expensive. I therefore limit my tests to
+; radii below 50. However, if every interval in every test set is small, then
+; they'd all fall below the threshhold for using the empirical method and every
+; single result of tau-bounder-logand would be perfect.
+;
+; To deal with this, I set the thresshold artificially low: 100. That is,
+; tau-bounder-logand uses the empirical method only when product of the size of
+; the two intervals is less than 10 * 10. (To run tau-bounder-logand without it
+; using the empirical method at all, I set the threshhold to 0.)
+;
+; My hypothesis is that even with a relatively low threshhold (so that the number
+; of combinations handled correctly ``by definition'' becomes increasiningly
+; small compared to the number of combinations), the empirical method improves
+; accuracy significantly. Here are my results. See the Appendix A for the code
+; and resulting raw data from which this table is extracted.
+;
+; k A H (E coverage)
+;
+; 5 65 100 (99)
+; 10 60 87 (80)
+; 15 61 87 (58)
+; 20 57 77 (43)
+; 25 58 75 (32)
+; 30 60 73 (25)
+; 35 57 68 (20)
+; 40 56 65 (17)
+; 45 56 65 (14)
+; 50 57 65 (12)
+;
+; In column A I show the percentage of the combinations bounded perfectly by the
+; analytical method (i.e., with the threshhold set to 0 so that the empirical
+; method is not employed). Note that it seems to hold steady at about 57% as k
+; increases.
+;
+; In column H I show the percentage of the combinations bounded perfectly by the
+; heuristic method (which augments the analytical method with the empirical
+; method for combinations under the size threshhold of 100).
+;
+; In parenthesis I show the percentage of the cases covered by the empirical
+; method. For example, when k=50, the heuristic method is perfect 65 percent of
+; the time but only 12% of the cases were handled by the empirical method.
+;
+; What this shows is that the empirical method increases the accuracy even as it
+; addresses a smaller percentage of the cases. Since the empirical method is
+; limited to small cases, I suspect this means that the small cases are handled
+; inaccurately by the analytical method a disproportionate number of times.
+;
+; -----------------------------------------------------------------
+; Appendix A.
+;
+; In this section I show the raw Lisp code and raw data behind the table above.
+;
+; ; These bounds aren't always very tight. Let k be some natural number.
+; ; Consider every combination of:
+;
+; ; -k <= lox <= hix <= k and -k <= loy <= hiy <= k
+;
+; ; and then compute the predicted bounds of logand. Then, for every x and y in
+; ; the given two intervals, compute the actual lo and the actual hi of (logand x
+; ; y). Let dlo be the distances between the actual lo and the predicted lo, and
+; ; let dhi be the comparable hi distance. Sum dlo and dhi to get a measure how
+; ; accurate the interval is. Sum all the measures across all combinations of
+; ; input intervals.
+;
+; (include-book "elementary-bounders")
+;
+; (value :q)
+;
+; ; This test, which should return T, confirms that we can rebind the threshhold
+; ; to control whether the empirical method is used by tau-bounder-logand:
+;
+; (not (equal (worth-computingp 0 100 0 100)
+; (let ((*logand-empirical-threshhold* 100))
+; (worth-computingp 0 100 0 100))))
+;
+; (defun score-logand-prediction (k)
+; ; Given a max radius of k, compute the
+; (let ((combinations 0)
+; (perfect 0)
+; (perfect-by-def 0)
+; (worst-miss-on-lo nil)
+; (worst-miss-on-hi nil)
+; (score nil))
+; ; When non-nil, the two ``worst-miss'' values are:
+; ; (miss-magnitude (lox hix loy hiy) (predicted-lo predicted-hi) (alo ahi))
+; (setq score
+; (loop for lox from (- k) to k
+; sum
+; (loop for hix from lox to k
+; sum
+; (let ((int1 (make-tau-interval 'integerp nil lox nil hix)))
+; (loop for loy from (- k) to k
+; sum
+; (loop for hiy from loy to k
+; sum
+; (let* ((int2 (make-tau-interval 'integerp nil loy nil hiy))
+; (int (tau-bounder-logand int1 int2))
+; (lo (tau-interval-lo int))
+; (hi (tau-interval-hi int))
+;
+; ; Warning: Even though we construct the input intervals int1 and int2, we still
+; ; exploit the fact that the variables lox, hix, loy, and hiy are bound to the
+; ; respective bounds of those intervals.
+;
+; (alo (find-minimal-logand lox hix loy hiy))
+; (ahi (find-maximal-logand lox hix loy hiy)))
+; (setq combinations (+ 1 combinations))
+; (if (worth-computingp lox hix loy hiy)
+; (setq perfect-by-def (+ 1 perfect-by-def)))
+; (if (and (equal lo alo)
+; (equal hi ahi))
+; (setq perfect (+ 1 perfect)))
+; (if (or (null worst-miss-on-lo)
+; (< (car worst-miss-on-lo)
+; (abs (- lo alo))))
+; (setq worst-miss-on-lo
+; `(,(abs (- lo alo))
+; (,lox ,hix ,loy ,hiy)
+; (,lo ,hi)
+; (,alo ,ahi))))
+; (if (or (null worst-miss-on-hi)
+; (< (car worst-miss-on-hi)
+; (abs (- hi ahi))))
+; (setq worst-miss-on-hi
+; `(,(abs (- hi ahi))
+; (,lox ,hix ,loy ,hiy)
+; (,lo ,hi)
+; (,alo ,ahi))))
+; (+ (abs (- lo alo))
+; (abs (- hi ahi))))))))))
+; `((combinations ,combinations)
+; (perfect ,perfect)
+; (perfect-percentage ,(floor (* 100 (/ perfect combinations)) 1) %)
+; (perfect-by-def ,perfect-by-def)
+; (perfect-by-def-percentage ,(floor (* 100 (/ perfect-by-def combinations)) 1) %)
+; (perfect-otherwise ,(- perfect perfect-by-def))
+; (perfect-otherwise-percentage ,(floor (* 100 (/ (- perfect perfect-by-def) combinations)) 1) %)
+; (score ,score)
+; (average-error ,(float (/ score combinations)))
+; (worst-miss-on-lo ,worst-miss-on-lo)
+; (worst-miss-on-hi ,worst-miss-on-hi))))
+;
+; ; The test:
+;
+; (let ((*logand-empirical-threshhold* *logand-empirical-threshhold*))
+; (progn
+; (print (setq *logand-empirical-threshhold* 100))
+; (print (list '(score-logand-prediction 5) (score-logand-prediction 5)))
+; (print (list '(score-logand-prediction 10) (score-logand-prediction 10)))
+; (print (list '(score-logand-prediction 15) (score-logand-prediction 15)))
+; (print (list '(score-logand-prediction 20) (score-logand-prediction 20)))
+; (print (list '(score-logand-prediction 25) (score-logand-prediction 25)))
+; (print (list '(score-logand-prediction 30) (score-logand-prediction 30)))
+; (print (list '(score-logand-prediction 35) (score-logand-prediction 35)))
+; (print (list '(score-logand-prediction 40) (score-logand-prediction 40)))
+; (print (list '(score-logand-prediction 45) (score-logand-prediction 45)))
+; (print (list '(score-logand-prediction 50) (score-logand-prediction 50)))
+; (print (setq *logand-empirical-threshhold* 0))
+; (print (list '(score-logand-prediction 5) (score-logand-prediction 5)))
+; (print (list '(score-logand-prediction 10) (score-logand-prediction 10)))
+; (print (list '(score-logand-prediction 15) (score-logand-prediction 15)))
+; (print (list '(score-logand-prediction 20) (score-logand-prediction 20)))
+; (print (list '(score-logand-prediction 25) (score-logand-prediction 25)))
+; (print (list '(score-logand-prediction 30) (score-logand-prediction 30)))
+; (print (list '(score-logand-prediction 35) (score-logand-prediction 35)))
+; (print (list '(score-logand-prediction 40) (score-logand-prediction 40)))
+; (print (list '(score-logand-prediction 45) (score-logand-prediction 45)))
+; (print (list '(score-logand-prediction 50) (score-logand-prediction 50)))
+; nil
+; ))
+;
+; ; Output:
+;
+; 100
+; ((SCORE-LOGAND-PREDICTION 5)
+; ((COMBINATIONS 4356) (PERFECT 4356) (PERFECT-PERCENTAGE 100 %)
+; (PERFECT-BY-DEF 4347) (PERFECT-BY-DEF-PERCENTAGE 99 %) (PERFECT-OTHERWISE 9)
+; (PERFECT-OTHERWISE-PERCENTAGE 0 %) (SCORE 0) (AVERAGE-ERROR 0.0)
+; (WORST-MISS-ON-LO (0 (-5 -5 -5 -5) (-5 -5) (-5 -5)))
+; (WORST-MISS-ON-HI (0 (-5 -5 -5 -5) (-5 -5) (-5 -5)))))
+; ((SCORE-LOGAND-PREDICTION 10)
+; ((COMBINATIONS 53361) (PERFECT 49621) (PERFECT-PERCENTAGE 92 %)
+; (PERFECT-BY-DEF 43165) (PERFECT-BY-DEF-PERCENTAGE 80 %) (PERFECT-OTHERWISE 6456)
+; (PERFECT-OTHERWISE-PERCENTAGE 12 %) (SCORE 11498) (AVERAGE-ERROR 0.21547572)
+; (WORST-MISS-ON-LO (7 (-9 -1 -1 10) (-16 10) (-9 10)))
+; (WORST-MISS-ON-HI (6 (-10 10 0 4) (0 10) (0 4)))))
+; ((SCORE-LOGAND-PREDICTION 15)
+; ((COMBINATIONS 246016) (PERFECT 214784) (PERFECT-PERCENTAGE 87 %)
+; (PERFECT-BY-DEF 143557) (PERFECT-BY-DEF-PERCENTAGE 58 %)
+; (PERFECT-OTHERWISE 71227) (PERFECT-OTHERWISE-PERCENTAGE 28 %) (SCORE 105010)
+; (AVERAGE-ERROR 0.42684215) (WORST-MISS-ON-LO (7 (-9 -4 -1 15) (-16 15) (-9 12)))
+; (WORST-MISS-ON-HI (12 (-15 15 0 3) (0 15) (0 3)))))
+; ((SCORE-LOGAND-PREDICTION 20)
+; ((COMBINATIONS 741321) (PERFECT 574847) (PERFECT-PERCENTAGE 77 %)
+; (PERFECT-BY-DEF 319249) (PERFECT-BY-DEF-PERCENTAGE 43 %)
+; (PERFECT-OTHERWISE 255598) (PERFECT-OTHERWISE-PERCENTAGE 34 %) (SCORE 967170)
+; (AVERAGE-ERROR 1.3046575)
+; (WORST-MISS-ON-LO (15 (-17 -13 -1 18) (-32 18) (-17 18)))
+; (WORST-MISS-ON-HI (18 (-20 20 0 2) (0 20) (0 2)))))
+; ((SCORE-LOGAND-PREDICTION 25)
+; ((COMBINATIONS 1758276) (PERFECT 1321323) (PERFECT-PERCENTAGE 75 %)
+; (PERFECT-BY-DEF 579499) (PERFECT-BY-DEF-PERCENTAGE 32 %)
+; (PERFECT-OTHERWISE 741824) (PERFECT-OTHERWISE-PERCENTAGE 42 %) (SCORE 2743968)
+; (AVERAGE-ERROR 1.5606014) (WORST-MISS-ON-LO (16 (-16 -7 16 25) (0 25) (16 25)))
+; (WORST-MISS-ON-HI (24 (-25 25 0 1) (0 25) (0 1)))))
+; ((SCORE-LOGAND-PREDICTION 30)
+; ((COMBINATIONS 3575881) (PERFECT 2634466) (PERFECT-PERCENTAGE 73 %)
+; (PERFECT-BY-DEF 927789) (PERFECT-BY-DEF-PERCENTAGE 25 %)
+; (PERFECT-OTHERWISE 1706677) (PERFECT-OTHERWISE-PERCENTAGE 47 %) (SCORE 6587188)
+; (AVERAGE-ERROR 1.8421161) (WORST-MISS-ON-LO (16 (-16 -10 16 30) (0 30) (16 22)))
+; (WORST-MISS-ON-HI (29 (-30 30 0 1) (0 30) (0 1)))))
+; ((SCORE-LOGAND-PREDICTION 35)
+; ((COMBINATIONS 6533136) (PERFECT 4502512) (PERFECT-PERCENTAGE 68 %)
+; (PERFECT-BY-DEF 1367879) (PERFECT-BY-DEF-PERCENTAGE 20 %)
+; (PERFECT-OTHERWISE 3134633) (PERFECT-OTHERWISE-PERCENTAGE 47 %) (SCORE 19708780)
+; (AVERAGE-ERROR 3.016741) (WORST-MISS-ON-LO (32 (-32 -8 32 35) (0 35) (32 35)))
+; (WORST-MISS-ON-HI (34 (-35 35 0 1) (0 35) (0 1)))))
+; ((SCORE-LOGAND-PREDICTION 40)
+; ((COMBINATIONS 11029041) (PERFECT 7251983) (PERFECT-PERCENTAGE 65 %)
+; (PERFECT-BY-DEF 1905769) (PERFECT-BY-DEF-PERCENTAGE 17 %)
+; (PERFECT-OTHERWISE 5346214) (PERFECT-OTHERWISE-PERCENTAGE 48 %) (SCORE 42591220)
+; (AVERAGE-ERROR 3.8617337) (WORST-MISS-ON-LO (32 (-32 -21 32 40) (0 40) (32 40)))
+; (WORST-MISS-ON-HI (39 (-40 40 0 1) (0 40) (0 1)))))
+; ((SCORE-LOGAND-PREDICTION 45)
+; ((COMBINATIONS 17522596) (PERFECT 11407801) (PERFECT-PERCENTAGE 65 %)
+; (PERFECT-BY-DEF 2547459) (PERFECT-BY-DEF-PERCENTAGE 14 %)
+; (PERFECT-OTHERWISE 8860342) (PERFECT-OTHERWISE-PERCENTAGE 50 %) (SCORE 72472530)
+; (AVERAGE-ERROR 4.135947) (WORST-MISS-ON-LO (32 (-32 -25 32 44) (0 44) (32 39)))
+; (WORST-MISS-ON-HI (44 (-45 45 0 1) (0 45) (0 1)))))
+; ((SCORE-LOGAND-PREDICTION 50)
+; ((COMBINATIONS 26532801) (PERFECT 17294676) (PERFECT-PERCENTAGE 65 %)
+; (PERFECT-BY-DEF 3298343) (PERFECT-BY-DEF-PERCENTAGE 12 %)
+; (PERFECT-OTHERWISE 13996333) (PERFECT-OTHERWISE-PERCENTAGE 52 %)
+; (SCORE 114203530) (AVERAGE-ERROR 4.3042397)
+; (WORST-MISS-ON-LO (32 (-32 -27 32 48) (0 48) (32 37)))
+; (WORST-MISS-ON-HI (50 (-50 50 0 0) (0 50) (0 0)))))
+; 0
+; ((SCORE-LOGAND-PREDICTION 5)
+; ((COMBINATIONS 4356) (PERFECT 2873) (PERFECT-PERCENTAGE 65 %) (PERFECT-BY-DEF 0)
+; (PERFECT-BY-DEF-PERCENTAGE 0 %) (PERFECT-OTHERWISE 2873)
+; (PERFECT-OTHERWISE-PERCENTAGE 65 %) (SCORE 2845) (AVERAGE-ERROR 0.6531221)
+; (WORST-MISS-ON-LO (5 (-3 -3 5 5) (0 5) (5 5)))
+; (WORST-MISS-ON-HI (5 (-5 5 0 0) (0 5) (0 0)))))
+; ((SCORE-LOGAND-PREDICTION 10)
+; ((COMBINATIONS 53361) (PERFECT 32499) (PERFECT-PERCENTAGE 60 %) (PERFECT-BY-DEF 0)
+; (PERFECT-BY-DEF-PERCENTAGE 0 %) (PERFECT-OTHERWISE 32499)
+; (PERFECT-OTHERWISE-PERCENTAGE 60 %) (SCORE 68793) (AVERAGE-ERROR 1.2892)
+; (WORST-MISS-ON-LO (10 (-6 -6 10 10) (0 10) (10 10)))
+; (WORST-MISS-ON-HI (10 (-10 10 0 0) (0 10) (0 0)))))
+; ((SCORE-LOGAND-PREDICTION 15)
+; ((COMBINATIONS 246016) (PERFECT 150727) (PERFECT-PERCENTAGE 61 %)
+; (PERFECT-BY-DEF 0) (PERFECT-BY-DEF-PERCENTAGE 0 %) (PERFECT-OTHERWISE 150727)
+; (PERFECT-OTHERWISE-PERCENTAGE 61 %) (SCORE 405365) (AVERAGE-ERROR 1.6477181)
+; (WORST-MISS-ON-LO (15 (-1 -1 15 15) (0 15) (15 15)))
+; (WORST-MISS-ON-HI (15 (-15 15 0 0) (0 15) (0 0)))))
+; ((SCORE-LOGAND-PREDICTION 20)
+; ((COMBINATIONS 741321) (PERFECT 429599) (PERFECT-PERCENTAGE 57 %)
+; (PERFECT-BY-DEF 0) (PERFECT-BY-DEF-PERCENTAGE 0 %) (PERFECT-OTHERWISE 429599)
+; (PERFECT-OTHERWISE-PERCENTAGE 57 %) (SCORE 1889899) (AVERAGE-ERROR 2.5493667)
+; (WORST-MISS-ON-LO (20 (-12 -12 20 20) (0 20) (20 20)))
+; (WORST-MISS-ON-HI (20 (-20 20 0 0) (0 20) (0 0)))))
+; ((SCORE-LOGAND-PREDICTION 25)
+; ((COMBINATIONS 1758276) (PERFECT 1034743) (PERFECT-PERCENTAGE 58 %)
+; (PERFECT-BY-DEF 0) (PERFECT-BY-DEF-PERCENTAGE 0 %) (PERFECT-OTHERWISE 1034743)
+; (PERFECT-OTHERWISE-PERCENTAGE 58 %) (SCORE 4887045) (AVERAGE-ERROR 2.7794528)
+; (WORST-MISS-ON-LO (25 (-7 -7 25 25) (0 25) (25 25)))
+; (WORST-MISS-ON-HI (25 (-25 25 0 0) (0 25) (0 0)))))
+; ((SCORE-LOGAND-PREDICTION 30)
+; ((COMBINATIONS 3575881) (PERFECT 2146211) (PERFECT-PERCENTAGE 60 %)
+; (PERFECT-BY-DEF 0) (PERFECT-BY-DEF-PERCENTAGE 0 %) (PERFECT-OTHERWISE 2146211)
+; (PERFECT-OTHERWISE-PERCENTAGE 60 %) (SCORE 11087983) (AVERAGE-ERROR 3.1007695)
+; (WORST-MISS-ON-LO (30 (-2 -2 30 30) (0 30) (30 30)))
+; (WORST-MISS-ON-HI (30 (-30 30 0 0) (0 30) (0 0)))))
+; ((SCORE-LOGAND-PREDICTION 35)
+; ((COMBINATIONS 6533136) (PERFECT 3786285) (PERFECT-PERCENTAGE 57 %)
+; (PERFECT-BY-DEF 0) (PERFECT-BY-DEF-PERCENTAGE 0 %) (PERFECT-OTHERWISE 3786285)
+; (PERFECT-OTHERWISE-PERCENTAGE 57 %) (SCORE 27677205) (AVERAGE-ERROR 4.236435)
+; (WORST-MISS-ON-LO (35 (-29 -29 35 35) (0 35) (35 35)))
+; (WORST-MISS-ON-HI (35 (-35 35 0 0) (0 35) (0 0)))))
+; ((SCORE-LOGAND-PREDICTION 40)
+; ((COMBINATIONS 11029041) (PERFECT 6215779) (PERFECT-PERCENTAGE 56 %)
+; (PERFECT-BY-DEF 0) (PERFECT-BY-DEF-PERCENTAGE 0 %) (PERFECT-OTHERWISE 6215779)
+; (PERFECT-OTHERWISE-PERCENTAGE 56 %) (SCORE 55769581) (AVERAGE-ERROR 5.056612)
+; (WORST-MISS-ON-LO (40 (-24 -24 40 40) (0 40) (40 40)))
+; (WORST-MISS-ON-HI (40 (-40 40 0 0) (0 40) (0 0)))))
+; ((SCORE-LOGAND-PREDICTION 45)
+; ((COMBINATIONS 17522596) (PERFECT 9959663) (PERFECT-PERCENTAGE 56 %)
+; (PERFECT-BY-DEF 0) (PERFECT-BY-DEF-PERCENTAGE 0 %) (PERFECT-OTHERWISE 9959663)
+; (PERFECT-OTHERWISE-PERCENTAGE 56 %) (SCORE 92758101) (AVERAGE-ERROR 5.2936277)
+; (WORST-MISS-ON-LO (45 (-19 -19 45 45) (0 45) (45 45)))
+; (WORST-MISS-ON-HI (45 (-45 45 0 0) (0 45) (0 0)))))
+; ((SCORE-LOGAND-PREDICTION 50)
+; ((COMBINATIONS 26532801) (PERFECT 15351489) (PERFECT-PERCENTAGE 57 %)
+; (PERFECT-BY-DEF 0) (PERFECT-BY-DEF-PERCENTAGE 0 %) (PERFECT-OTHERWISE 15351489)
+; (PERFECT-OTHERWISE-PERCENTAGE 57 %) (SCORE 144236691) (AVERAGE-ERROR 5.4361653)
+; (WORST-MISS-ON-LO (50 (-14 -14 50 50) (0 50) (50 50)))
+; (WORST-MISS-ON-HI (50 (-50 50 0 0) (0 50) (0 0)))))
+;
+;
+; -----------------------------------------------------------------
+; Appendix B.
+;
+; I now discuss briefly the prospects of refining my analysis so that it is
+; inherently more accurate. I am not optimistic but record my thoughts for
+; posterity in case somebody wants to pursue it.
+;
+; Here is an example of a bad case. The x interval contains only one element.
+; The y interval contains 64 elements, so it is easy to compute the empirical
+; answer. The analytical range is 0 to 3392, but the actual range is 3072 to
+; 3104. Furthermore, there are only two points in the actual range: 3072 and
+; 3104. Here is some Common Lisp code to confirm these statements:
+;
+; (let* ((lox #b10110000100000)
+; (loy #b00110100000000)
+; (hiy #b00110101000000)
+; (hix lox))
+; (mv-let (rel1 lo rel2 hi)
+; (ibounds-of-logand nil lox nil hix nil loy nil hiy)
+; `((lox ,lox)
+; (hix ,hix)
+; (loy ,loy)
+; (hiy ,hiy)
+; (,lo --- ,hi)
+; (,(find-minimal-logand lox hix loy hiy) ---
+; ,(find-maximal-logand lox hix loy hiy))
+; (all ,(let ((ans nil))
+; (loop for x from lox to hix
+; do (loop for y from loy to hiy
+; do (or (member-equal (logand x y) ans)
+; (setq ans (cons (logand x y) ans)))))
+; ans)))))
+; This produces:
+;
+; ((LOX 11296)
+; (HIX 11296)
+; (LOY 3328)
+; (HIY 3392)
+; (0 --- 3392) ; analytical interval
+; (3072 --- 3104) ; actual interval
+; (ALL (3104 3072))) ; all possible actual values of (logand x y)
+;
+; Let's blow up this example to see why the prediction is so hard. I discuss
+; the example in the text below and, in the ``footnotes'', discuss the
+; generalization of the example to whole classes of problems.
+;
+; bits a b c d e f g h i j k l m n
+; pwr 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+;
+; lox = hix = #b 1 0 1 1 0 0 0 0 1 0 0 0 0 0
+; loy = #b 0 0 1 1 0 1 0 0 0 0 0 0 0 0
+; hiy = #b 0 0 1 1 0 1 0 1 0 0 0 0 0 0
+;
+; Note that as y ranges from loy to hiy, we will generate all possible 6-bit
+; patterns in bits i through n, stopping when we finally set bit h. [Footnote:
+; In general we won't generate all h-bit patterns, since we start with the
+; pattern given by the low h bits of loy and end with the pattern given by the
+; low h+1 bits of hiy, but since in this example, loy and hiy both have all 0s
+; below bit h, we will generate all patterns.] Thus, all the y's in the range
+; share the same bits above bit h. Thus, every logand of x and y will be
+; non-zero, because x and every y will always share whatever bits all three
+; share above bit h. In this case that is bits c and d, which is 2^11 + 2^10 =
+; 3072. [Footnote: Clearly we could have an arbitrary number of shared bits
+; contributing to this lower bound.] Furthermore, since there is only one x
+; and it has only one bit set below bit h, as y varies it the low order h bits
+; of (logand x y) will will either be all 0 or all 0 except for 1 in bit i.
+; This will mean that the low order h bits of the logand either contribute 0 or
+; 2^5 to the value. This is how the empirical hi is achieved: lo + 2^5 = 3104.
+; This accounts for all (both) of the values of the logand. [Footnote: By
+; choosing an x with multiple bits set below bit h we can arrange for multiple
+; values to be generated by the logand. Precisely which values can only be
+; determined, I think, by generating each of the h-bit numbers between the low
+; h bits of loy and the low h bits of hiy and loganding those patterns with the
+; low h bits of x.]
+;
+; One could over estimate the maximal amount by which lo will be incremented by
+; supposing that there exists a combination in y that will select all of the
+; lower h bits of x.
+;
+; But I've decided that trying to get more accurate analytical bounds is not
+; worth the effort. Instead, I'll just go with the empirical approach. We'll
+; see if this is helpful enough.
diff --git a/books/tau/bounders/find-maximal-1d.lisp b/books/tau/bounders/find-maximal-1d.lisp
new file mode 100644
index 0000000..bd2a20c
--- /dev/null
+++ b/books/tau/bounders/find-maximal-1d.lisp
@@ -0,0 +1,96 @@
+; Copyright (C) 2013, ForrestHunt, Inc.
+; Written by J Strother Moore, December 19, 2012
+; License: A 3-clause BSD license. See the LICENSE file distributed with ACL2.
+
+; Toy: Finding the Maximal Value of a Function over an Integer Interval
+
+; To recertify:
+; (certify-book "find-maximal-1d")
+
+; Abstract: Suppose you have an integer-valued function, FMLA, of one integer
+; argument, x, and you wish to find the maximal value of (FMLA x) as x ranges
+; over a given interval lo <= x <= hi. See find-minimal-1d.lisp for the
+; original development.
+
+(in-package "ACL2")
+
+(encapsulate ((fmla (x) t))
+ (local (defun fmla (x) (* x x)))
+ (defthm integerp-fmla
+ (implies (integerp x)
+ (integerp (fmla x)))
+ :rule-classes :type-prescription))
+
+(defun find-maximal1 (lo hi max)
+ (declare (xargs :measure (nfix (- (+ 1 (ifix hi)) (ifix lo)))))
+ (cond
+ ((not (and (integerp lo)
+ (integerp hi)))
+ max)
+ ((> lo hi) max)
+ (t (find-maximal1
+ (+ 1 lo) hi
+ (if (or (null max)
+ (> (fmla lo) max))
+ (fmla lo)
+ max)))))
+
+(defun find-maximal (lo hi)
+ (find-maximal1 lo hi nil))
+
+(defthm find-maximal1-increases
+ (implies max
+ (>= (find-maximal1 lo hi max) max))
+ :rule-classes :linear)
+
+(defthm integerp-find-maximal1
+ (implies (and (integerp lo)
+ (integerp hi)
+ (or (null max) (integerp max)))
+ (equal (integerp (find-maximal1 lo hi max))
+ (if (null max)
+ (<= lo hi)
+ t)))
+ :hints (("Goal" :induct (find-maximal1 lo hi max))))
+
+(defun above-all (max lo hi)
+ (declare (xargs :measure (nfix (- (+ 1 (ifix hi)) (ifix lo)))))
+ (cond
+ ((not (and (integerp lo)
+ (integerp hi)))
+ t)
+ ((> lo hi) t)
+ ((>= max (fmla lo))
+ (above-all max (+ 1 lo) hi))
+ (t nil)))
+
+(defthm above-all-find-maximal1
+ (implies (and (integerp lo)
+ (integerp hi)
+ (or (null xxx) (integerp xxx)))
+ (above-all (find-maximal1 lo hi xxx) lo hi)))
+
+(defthm above-all-is-a-universal-quantifier
+ (implies (and (integerp lo)
+ (integerp hi)
+ (integerp x)
+ (<= lo x)
+ (<= x hi)
+ (above-all max lo hi))
+ (>= max (fmla x))))
+
+(defthm find-maximal-correct
+ (implies (and (integerp lo)
+ (integerp hi)
+ (integerp x)
+ (<= lo x)
+ (<= x hi))
+ (and (integerp (find-maximal lo hi))
+ (>= (find-maximal lo hi) (fmla x))))
+ :rule-classes nil)
+
+
+
+
+
+
diff --git a/books/tau/bounders/find-maximal-2d.lisp b/books/tau/bounders/find-maximal-2d.lisp
new file mode 100644
index 0000000..d0eb863
--- /dev/null
+++ b/books/tau/bounders/find-maximal-2d.lisp
@@ -0,0 +1,204 @@
+; Copyright (C) 2013, ForrestHunt, Inc.
+; Written by J Strother Moore, December 20, 2012
+; License: A 3-clause BSD license. See the LICENSE file distributed with ACL2.
+
+; Toy: Finding the Maximal Value of a Function over a Pair of Integer Intervals
+
+; To recertify:
+; (certify-book "find-maximal-2d")
+
+; Abstract: Given
+
+; FMLA : integerp x integerp --> integerp.
+
+; maximize (fmla x y), where lox <= x <= hix and loy <= y <= hiy. See
+; find-minimal-2d.lisp for the original development.
+
+(in-package "ACL2")
+
+(encapsulate ((fmla (x y) t))
+ (local (defun fmla (x y) (* x y)))
+ (defthm integerp-fmla
+ (implies (and (integerp x)
+ (integerp y))
+ (integerp (fmla x y)))
+ :rule-classes :type-prescription))
+
+(defun find-maximal2 (x loy hiy max)
+ (declare (xargs :measure (nfix (- (+ 1 (ifix hiy)) (ifix loy)))))
+ (cond
+ ((not (and (integerp loy)
+ (integerp hiy)))
+ max)
+ ((> loy hiy) max)
+ (t (find-maximal2
+ x
+ (+ 1 loy) hiy
+ (if (or (null max)
+ (> (fmla x loy) max))
+ (fmla x loy)
+ max)))))
+
+(defun find-maximal1 (lox hix loy hiy max)
+ (declare (xargs :measure (nfix (- (+ 1 (ifix hix)) (ifix lox)))))
+ (cond
+ ((not (and (integerp lox)
+ (integerp hix)))
+ max)
+ ((> lox hix) max)
+ (t (find-maximal1
+ (+ 1 lox) hix
+ loy hiy
+ (find-maximal2 lox loy hiy max)))))
+
+(defun find-maximal (lox hix loy hiy)
+ (find-maximal1 lox hix loy hiy nil))
+
+(defthm find-maximal2-increases
+ (implies (and max
+ (integerp x))
+ (>= (find-maximal2 x loy hiy max) max))
+ :rule-classes :linear)
+
+(defthm integerp-find-maximal2
+ (implies (and (integerp x)
+ (integerp loy)
+ (integerp hiy)
+ (or (null max) (integerp max)))
+ (equal (integerp (find-maximal2 x loy hiy max))
+ (if (null max)
+ (<= loy hiy)
+ t))))
+
+(defthm non-nil-find-maximal2
+ (implies (and (integerp x)
+ (integerp loy)
+ (integerp hiy))
+ (iff (find-maximal2 x loy hiy max)
+ (or max (<= loy hiy)))))
+
+(defthm find-maximal1-increases
+ (implies (and max
+ (integerp lox)
+ (integerp hix)
+ (integerp loy)
+ (integerp hiy))
+ (>= (find-maximal1 lox hix loy hiy max) max))
+ :rule-classes :linear)
+
+(defthm integerp-find-maximal1
+ (implies (and (integerp lox)
+ (integerp hix)
+ (integerp loy)
+ (integerp hiy)
+ (or (null max) (integerp max)))
+ (equal (integerp (find-maximal1 lox hix loy hiy max))
+ (if (null max)
+ (and (<= lox hix)
+ (<= loy hiy))
+ t))))
+
+(defun above-all2 (max x loy hiy)
+ (declare (xargs :measure (nfix (- (+ 1 (ifix hiy)) (ifix loy)))))
+ (cond
+ ((not (and (integerp loy)
+ (integerp hiy)))
+ t)
+ ((> loy hiy) t)
+ ((>= max (fmla x loy))
+ (above-all2 max x (+ 1 loy) hiy))
+ (t nil)))
+
+(defun above-all1 (max lox hix loy hiy)
+ (declare (xargs :measure (nfix (- (+ 1 (ifix hix)) (ifix lox)))))
+ (cond
+ ((not (and (integerp lox)
+ (integerp hix)))
+ t)
+ ((> lox hix) t)
+ ((above-all2 max lox loy hiy)
+ (above-all1 max (+ 1 lox) hix loy hiy))
+ (t nil)))
+
+(defthm above-all2-find-maximal2
+ (implies (and (integerp x)
+ (integerp loy)
+ (integerp hiy)
+ (or (null xxx) (integerp xxx)))
+ (above-all2 (find-maximal2 x loy hiy xxx) x loy hiy)))
+
+; This is a lemma not found in the 1D case...
+
+(defthm above-all2-trans
+ (implies (and (>= max1 max2)
+ (above-all2 max2 x loy hiy))
+ (above-all2 max1 x loy hiy))
+ :rule-classes nil)
+
+(defthm above-all1-find-maximal1
+ (implies (and (integerp lox)
+ (integerp hix)
+ (integerp loy)
+ (integerp hiy)
+ (or (null xxx) (integerp xxx)))
+ (above-all1 (find-maximal1 lox hix loy hiy xxx) lox hix loy hiy))
+ :hints (("Goal" :induct (find-maximal1 lox hix loy hiy xxx))
+ ("Subgoal *1/2"
+ :use
+ ((:instance above-all2-trans
+ (x LOX)
+ (max1 (FIND-MAXIMAL1 (+ 1 LOX)
+ HIX
+ LOY HIY
+ (FIND-MAXIMAL2 LOX LOY HIY XXX)))
+ (max2 (FIND-MAXIMAL2 LOX LOY HIY XXX)))))))
+
+(defthm above-all2-is-a-universal-quantifier
+ (implies (and (above-all2 max x loy hiy)
+ (integerp x)
+ (integerp loy)
+ (integerp hiy)
+ (integerp y)
+ (<= loy y)
+ (<= y hiy))
+ (>= max (fmla x y)))
+ :rule-classes :linear)
+
+(defthm above-all1-is-a-universal-quantifier
+ (implies (and (above-all1 max lox hix loy hiy)
+ (integerp lox)
+ (integerp hix)
+ (integerp loy)
+ (integerp hiy)
+ (integerp x)
+ (<= lox x)
+ (<= x hix)
+ (integerp y)
+ (<= loy y)
+ (<= y hiy))
+ (>= max (fmla x y)))
+ :rule-classes nil)
+
+(defthm find-maximal-correct
+ (implies (and (integerp lox)
+ (integerp hix)
+ (integerp loy)
+ (integerp hiy)
+ (integerp x)
+ (<= lox x)
+ (<= x hix)
+ (integerp y)
+ (<= loy y)
+ (<= y hiy))
+ (and (integerp (find-maximal lox hix loy hiy))
+ (>= (find-maximal lox hix loy hiy)
+ (fmla x y))))
+ :hints (("Goal" :use (:instance above-all1-is-a-universal-quantifier
+ (max (find-maximal lox hix loy hiy)))))
+ :rule-classes nil)
+
+
+
+
+
+
diff --git a/books/tau/bounders/find-minimal-1d.lisp b/books/tau/bounders/find-minimal-1d.lisp
new file mode 100644
index 0000000..19b9221
--- /dev/null
+++ b/books/tau/bounders/find-minimal-1d.lisp
@@ -0,0 +1,128 @@
+; Copyright (C) 2013, ForrestHunt, Inc.
+; Written by J Strother Moore, December 19, 2012
+; License: A 3-clause BSD license. See the LICENSE file distributed with ACL2.
+
+; Toy: Finding the Minimal Value of a Function over an Integer Interval
+
+; To recertify:
+; (certify-book "find-minimal-1d")
+
+; Abstract: Suppose you have an integer-valued function, FMLA, of one integer
+; argument, x, and you wish to find the minimal value of (FMLA x) as x ranges
+; over a given interval lo <= x <= hi. Below is a function that does it
+; and a proof that it does it.
+
+; Because of the context in which I wish to apply a result like this one, the
+; function that finds the minimal value is tail-recursive. That means it
+; maintains an accumulator with the least value seen so far. That gives rise
+; to a generalization issue.
+
+; None of this is deep mathematics! The whole point of this exercise is just
+; to do a toy example of this type of problem to guide me in the more
+; complicated situation of finding a minimal of a dyadic function over the
+; cross-product of two integer intervals. Furthermore, this is a toy I must
+; have worked half a dozen times before. (See the examples of theorems called
+; ``...-is-a-universal-quantifier'' in the Nqthm proveall from the 70s and
+; 80s.)
+
+(in-package "ACL2")
+
+; Here is fmla : integerp --> integerp.
+
+(encapsulate ((fmla (x) t))
+ (local (defun fmla (x) (* x x)))
+ (defthm integerp-fmla
+ (implies (integerp x)
+ (integerp (fmla x)))
+ :rule-classes :type-prescription))
+
+; Scan the interval from lo to hi, accumulating into min the minimal value
+; seen. Min starts at nil, meaning ``nothing seen yet.''
+
+(defun find-minimal1 (lo hi min)
+ (declare (xargs :measure (nfix (- (+ 1 (ifix hi)) (ifix lo)))))
+ (cond
+ ((not (and (integerp lo)
+ (integerp hi)))
+ min)
+ ((> lo hi) min)
+ (t (find-minimal1
+ (+ 1 lo) hi
+ (if (or (null min)
+ (< (fmla lo) min))
+ (fmla lo)
+ min)))))
+
+; Here is the wrapper that initializes the running minimal to nil. We deal
+; exclusively with the workhorse, find-minimal1, in our lemmas below and don't
+; see find-minimal again until our final theorem.
+
+(defun find-minimal (lo hi)
+ (find-minimal1 lo hi nil))
+
+(defthm find-minimal1-decreases
+ (implies min
+ (<= (find-minimal1 lo hi min) min))
+ :rule-classes :linear)
+
+(defthm integerp-find-minimal1
+ (implies (and (integerp lo)
+ (integerp hi)
+ (or (null min) (integerp min)))
+ (equal (integerp (find-minimal1 lo hi min))
+ (if (null min)
+ (<= lo hi)
+ t)))
+ :hints (("Goal" :induct (find-minimal1 lo hi min))))
+
+; The hint above is necessary because the proof splits into two cases: min=nil
+; and (integerp min). The second case is immediate by type reasoning. But the
+; system won't open up (find-minimal1 lo hi nil) unless told to.
+
+; The ``trick'' to this proof is to introduce the notion that min is a lower
+; bound on (fmla x) over lo <= x <= hi:
+
+(defun below-all (min lo hi)
+ (declare (xargs :measure (nfix (- (+ 1 (ifix hi)) (ifix lo)))))
+ (cond
+ ((not (and (integerp lo)
+ (integerp hi)))
+ t)
+ ((> lo hi) t)
+ ((<= min (fmla lo))
+ (below-all min (+ 1 lo) hi))
+ (t nil)))
+
+(defthm below-all-find-minimal1
+ (implies (and (integerp lo)
+ (integerp hi)
+ (or (null xxx) (integerp xxx)))
+ (below-all (find-minimal1 lo hi xxx) lo hi)))
+
+; Now prove that below-all is a universal quantifier: If (below-all min lo hi)
+; holds and lo <= x <= hi, min <= (fmla x).
+
+(defthm below-all-is-a-universal-quantifier
+ (implies (and (integerp lo)
+ (integerp hi)
+ (integerp x)
+ (<= lo x)
+ (<= x hi)
+ (below-all min lo hi))
+ (<= min (fmla x))))
+
+(defthm find-minimal-correct
+ (implies (and (integerp lo)
+ (integerp hi)
+ (integerp x)
+ (<= lo x)
+ (<= x hi))
+ (and (integerp (find-minimal lo hi))
+ (<= (find-minimal lo hi) (fmla x))))
+ :rule-classes nil)
+
+
+
+
+
+
diff --git a/books/tau/bounders/find-minimal-2d.lisp b/books/tau/bounders/find-minimal-2d.lisp
new file mode 100644
index 0000000..3332ed1
--- /dev/null
+++ b/books/tau/bounders/find-minimal-2d.lisp
@@ -0,0 +1,235 @@
+; Copyright (C) 2013, ForrestHunt, Inc.
+; Written by J Strother Moore, December 20, 2012
+; License: A 3-clause BSD license. See the LICENSE file distributed with ACL2.
+
+; Toy: Finding the Minimal Value of a Function over a Pair of Integer Intervals
+
+; To recertify:
+; (certify-book "find-minimal-2d")
+
+; Abstract: This is like find-minimal-1d.lisp except it deals with a
+; dyadic function over pairs of integers,
+
+; FMLA : integerp x integerp --> integerp.
+
+; We wish to minimize (fmla x y), where lox <= x <= hix and loy <= y <= hiy.
+
+(in-package "ACL2")
+
+(encapsulate ((fmla (x y) t))
+ (local (defun fmla (x y) (* x y)))
+ (defthm integerp-fmla
+ (implies (and (integerp x)
+ (integerp y))
+ (integerp (fmla x y)))
+ :rule-classes :type-prescription))
+
+; Find-minimal initializes the accumulator to nil and calls find-minimal1.
+; Find-minimal1 calls find-minimal2 for each x between lox and hix,
+; accumulating the results. Find-minimal2 takes a given x and maps y from loy
+; to hiy and for each y accumulates (minimizes wrt min) the values of (fmla x
+; y).
+
+(defun find-minimal2 (x loy hiy min)
+ (declare (xargs :measure (nfix (- (+ 1 (ifix hiy)) (ifix loy)))))
+ (cond
+ ((not (and (integerp loy)
+ (integerp hiy)))
+ min)
+ ((> loy hiy) min)
+ (t (find-minimal2
+ x
+ (+ 1 loy) hiy
+ (if (or (null min)
+ (< (fmla x loy) min))
+ (fmla x loy)
+ min)))))
+
+(defun find-minimal1 (lox hix loy hiy min)
+ (declare (xargs :measure (nfix (- (+ 1 (ifix hix)) (ifix lox)))))
+ (cond
+ ((not (and (integerp lox)
+ (integerp hix)))
+ min)
+ ((> lox hix) min)
+ (t (find-minimal1
+ (+ 1 lox) hix
+ loy hiy
+ (find-minimal2 lox loy hiy min)))))
+
+; This is the wrapper that initializes the running minimal, min, to nil. But
+; all our lemmas except the last will be about the two recursive functions
+; above.
+
+(defun find-minimal (lox hix loy hiy)
+ (find-minimal1 lox hix loy hiy nil))
+
+(defthm find-minimal2-decreases
+ (implies (and min
+ (integerp x))
+ (<= (find-minimal2 x loy hiy min) min))
+ :rule-classes :linear)
+
+(defthm integerp-find-minimal2
+ (implies (and (integerp x)
+ (integerp loy)
+ (integerp hiy)
+ (or (null min) (integerp min)))
+ (equal (integerp (find-minimal2 x loy hiy min))
+ (if (null min)
+ (<= loy hiy)
+ t))))
+
+; Unlike the 1d case, in the 2d case the accumulator can take on the value not
+; just nil or the integer value of the formula but the value of find-minimal2.
+; So we must show it is generally non-nil too.
+
+(defthm non-nil-find-minimal2
+ (implies (and (integerp x)
+ (integerp loy)
+ (integerp hiy))
+ (iff (find-minimal2 x loy hiy min)
+ (or min (<= loy hiy)))))
+
+(defthm find-minimal1-decreases
+ (implies (and min
+ (integerp lox)
+ (integerp hix)
+ (integerp loy)
+ (integerp hiy))
+ (<= (find-minimal1 lox hix loy hiy min) min))
+ :rule-classes :linear)
+
+(defthm integerp-find-minimal1
+ (implies (and (integerp lox)
+ (integerp hix)
+ (integerp loy)
+ (integerp hiy)
+ (or (null min) (integerp min)))
+ (equal (integerp (find-minimal1 lox hix loy hiy min))
+ (if (null min)
+ (and (<= lox hix)
+ (<= loy hiy))
+ t))))
+
+(defun below-all2 (min x loy hiy)
+ (declare (xargs :measure (nfix (- (+ 1 (ifix hiy)) (ifix loy)))))
+ (cond
+ ((not (and (integerp loy)
+ (integerp hiy)))
+ t)
+ ((> loy hiy) t)
+ ((<= min (fmla x loy))
+ (below-all2 min x (+ 1 loy) hiy))
+ (t nil)))
+
+(defun below-all1 (min lox hix loy hiy)
+ (declare (xargs :measure (nfix (- (+ 1 (ifix hix)) (ifix lox)))))
+ (cond
+ ((not (and (integerp lox)
+ (integerp hix)))
+ t)
+ ((> lox hix) t)
+ ((below-all2 min lox loy hiy)
+ (below-all1 min (+ 1 lox) hix loy hiy))
+ (t nil)))
+
+(defthm below-all2-find-minimal2
+ (implies (and (integerp x)
+ (integerp loy)
+ (integerp hiy)
+ (or (null xxx) (integerp xxx)))
+ (below-all2 (find-minimal2 x loy hiy xxx) x loy hiy)))
+
+; This is a lemma not found in the 1D case...
+
+(defthm below-all2-trans
+ (implies (and (<= min1 min2)
+ (below-all2 min2 x loy hiy))
+ (below-all2 min1 x loy hiy))
+ :rule-classes nil)
+
+; The lemma above contains the free variable min2 and there is no good way to
+; choose it, that is, instances of neither of the hypotheses above appear in
+; the subgoal below to which the lemma applies. The lemma must be applied in
+; Subgoal *1/2 of the theorem below. The lemma's first hypothesis will be
+; established the theorem find-minimal1-decreases above. The lemma's second
+; hypothesis will be established by below-all2-find-minimal2. Since both
+; hypotheses are established by lemmas and contain the free variable, I could
+; see no more elegant way to proceed than to use an explicit hint, below, to
+; use the desired instance of below-all2-trans.
+
+(defthm below-all1-find-minimal1
+ (implies (and (integerp lox)
+ (integerp hix)
+ (integerp loy)
+ (integerp hiy)
+ (or (null xxx) (integerp xxx)))
+ (below-all1 (find-minimal1 lox hix loy hiy xxx) lox hix loy hiy))
+ :hints (("Goal" :induct (find-minimal1 lox hix loy hiy xxx))
+ ("Subgoal *1/2"
+ :use
+ ((:instance below-all2-trans
+ (x LOX)
+ (min1 (FIND-MINIMAL1 (+ 1 LOX)
+ HIX
+ LOY HIY
+ (FIND-MINIMAL2 LOX LOY HIY XXX)))
+ (min2 (FIND-MINIMAL2 LOX LOY HIY XXX)))))))
+
+(defthm below-all2-is-a-universal-quantifier
+ (implies (and (below-all2 min x loy hiy)
+ (integerp x)
+ (integerp loy)
+ (integerp hiy)
+ (integerp y)
+ (<= loy y)
+ (<= y hiy))
+ (<= min (fmla x y)))
+ :rule-classes :linear)
+
+(defthm below-all1-is-a-universal-quantifier
+ (implies (and (below-all1 min lox hix loy hiy)
+ (integerp lox)
+ (integerp hix)
+ (integerp loy)
+ (integerp hiy)
+ (integerp x)
+ (<= lox x)
+ (<= x hix)
+ (integerp y)
+ (<= loy y)
+ (<= y hiy))
+ (<= min (fmla x y)))
+ :rule-classes nil)
+
+; Again, note that we have free-variables above, and the hypothesis containing
+; them will be established by a lemma when the theorem above is applied.
+; Therefore, I make the theorem above rule-classes nil and :use it explicitly
+; in the proof below.
+
+(defthm find-minimal-correct
+ (implies (and (integerp lox)
+ (integerp hix)
+ (integerp loy)
+ (integerp hiy)
+ (integerp x)
+ (<= lox x)
+ (<= x hix)
+ (integerp y)
+ (<= loy y)
+ (<= y hiy))
+ (and
+ (integerp (find-minimal lox hix loy hiy))
+ (<= (find-minimal lox hix loy hiy)
+ (fmla x y))))
+
+ :hints (("Goal" :use (:instance below-all1-is-a-universal-quantifier
+ (min (find-minimal lox hix loy hiy)))))
+ :rule-classes nil)
+
+
+
+
+
+
diff --git a/books/tau/bounders/tau-bounders-tests.lisp b/books/tau/bounders/tau-bounders-tests.lisp
new file mode 100644
index 0000000..f292128
--- /dev/null
+++ b/books/tau/bounders/tau-bounders-tests.lisp
@@ -0,0 +1,16 @@
+(in-package "ACL2")
+(include-book "ihs/basic-definitions" :dir :system)
+
+; Original "elementary-bounders" does not prove test-expt and test-expt2
+; Modified "elementary-bounders" does prove.
+(include-book "elementary-bounders")
+
+(defthm tau-bounders-test-expt2
+ (implies
+ (and (natp w) (> w 2))
+ (>= (expt2 (* w w)) 512)))
+
+(defthm tau-bounders-test-expt
+ (implies
+ (and (integerp w) (< w 2))
+ (>= (/ (+ 1 (expt 2 w))) 1/3)))