other
(in-package "BITOPS")
other
(include-book "logbitp-mismatch")
other
(include-book "clause-processors/witness-cp" :dir :system)
other
(include-book "std/util/wizard" :dir :system)
other
(include-book "std/basic/arith-equiv-defs" :dir :system)
other
(local (include-book "ihsext-basics"))
other
(local (include-book "integer-length"))
other
(local (include-book "arithmetic/top-with-meta" :dir :system))
other
(defsection bitops/equal-by-logbitp :parents (bitops) :short "Introduces @('equal-by-logbitp'), a strategy for proving that @('a = b') by showing that their bits are equal, and computed hints that can automate the application of this strategy.")
other
(local (defthm equal-of-logcdrs-when-equal-of-logcars (implies (and (integerp i) (integerp j) (equal (logcar i) (logcar j))) (equal (equal (logcdr i) (logcdr j)) (equal i j)))))
other
(local (defthm logbitp-of-increment (implies (and (natp i) (integerp j)) (equal (logbitp (+ 1 i) j) (logbitp i (logcdr j)))) :hints (("Goal" :in-theory (enable logbitp**)))))
other
(local (defthm logcar-possibilities (or (equal (logcar a) 0) (equal (logcar a) 1)) :rule-classes ((:forward-chaining :trigger-terms ((logcar a))))))
other
(defsection equal-by-logbitp :parents (bitops/equal-by-logbitp logbitp) :short "Show @('a = b') by showing their bits are equal." :long "<p>@('Equal-by-logbitp') may be functionally instantiated to prove @('(equal a b)') by showing that:</p> @({ (equal (logbitp bit a) (logbitp bit b)) }) <p>for any arbitrary @('bit') less than the maximum @(see integer-length) of @('a') or @('b'), where @('a') and @('b') are known to be integers.</p> <p>This unusual (but occasionally useful) proof strategy is similar to the <i>pick-a-point</i> proofs found in the ordered sets or <see topic="@(url acl2::ubdds)">ubdd</see> libraries.</p> <p>There are a couple of ways to invoke the hint. First, you might manually appeal to the theorem using a hint such as:</p> @({ :use ((:functional-instance equal-by-logbitp (logbitp-hyp (lambda () my-hyps)) (logbitp-lhs (lambda () my-lhs)) (logbitp-rhs (lambda () my-rhs)))) }) <p>But this can be irritating if your particular hyps, lhs, and rhs are large or complex terms. See the @(see equal-by-logbitp-hint) computed hint, which can generate the appropriate @(':functional-instance') automatically.</p>" (encapsulate (((logbitp-hyp) => *) ((logbitp-lhs) => *) ((logbitp-rhs) => *)) (local (defun logbitp-hyp nil t)) (local (defun logbitp-lhs nil 0)) (local (defun logbitp-rhs nil 0)) (defthm logbitp-constraint (implies (and (logbitp-hyp) (natp bit) (<= bit (max (integer-length (logbitp-lhs)) (integer-length (logbitp-rhs))))) (equal (logbitp bit (logbitp-lhs)) (logbitp bit (logbitp-rhs)))))) (defthmd equal-by-logbitp (implies (logbitp-hyp) (equal (ifix (logbitp-lhs)) (ifix (logbitp-rhs)))) :hints (("Goal" :in-theory (e/d (logbitp-mismatch-under-iff) (logbitp-mismatch-upper-bound logbitp-constraint)) :use ((:instance logbitp-constraint (bit (logbitp-mismatch (logbitp-lhs) (logbitp-rhs)))) (:instance logbitp-mismatch-upper-bound (a (logbitp-lhs)) (b (logbitp-rhs))))))))
other
(defthm logbitp-of-negative-const (implies (and (syntaxp (quotep x)) (< x 0)) (equal (logbitp bit x) (not (logbitp bit (lognot x))))))
other
(defsection logbitp-of-const (local (in-theory (enable* arith-equiv-forwarding))) (local (defthm logbitp-of-lognot-free (implies (equal n (lognot m)) (equal (logbitp x n) (not (logbitp x m)))))) (local (defthm logbitp-of-1 (equal (logbitp x 1) (zp x)) :hints (("Goal" :in-theory (enable logbitp** zp nfix))))) (local (defthm logbitp-of-minus-2 (equal (logbitp x -2) (not (zp x))) :hints (("Goal" :in-theory (enable logbitp**))))) (defthm logbitp-of-const (implies (and (syntaxp (quotep n)) (let ((n (ifix n))) (or (= n 0) (= n -1) (if (< n 0) (= n (lognot (ash 1 (1- (integer-length n))))) (= n (ash 1 (1- (integer-length n)))))))) (equal (logbitp x n) (let ((n (ifix n))) (cond ((= n 0) nil) ((= n -1) t) ((< n 0) (not (equal (nfix x) (1- (integer-length n))))) (t (equal (nfix x) (1- (integer-length n)))))))) :hints (("Goal" :in-theory (enable logbitp-of-ash-split nfix ifix) :cases ((> (integer-length n) 0))))) (defthmd logbitp-of-bitmask (implies (bitmaskp n) (equal (logbitp x n) (< (nfix x) (integer-length n)))) :hints (("Goal" :in-theory (enable* ihsext-recursive-redefs ihsext-inductions) :induct t) '(:cases ((logbitp x n))))) (local (in-theory (enable logbitp-of-bitmask))) (defthm integer-length-of-lognot (equal (integer-length (lognot x)) (integer-length x)) :hints (("Goal" :in-theory (enable* ihsext-recursive-redefs ihsext-inductions)))) (defthm logbitp-of-mask (implies (and (syntaxp (quotep n)) (let* ((n (ifix n)) (len (integer-length n))) (and (< 0 len) (if (< n 0) (equal n (lognot (1- (ash 1 (1- (integer-length n)))))) (equal n (1- (ash 1 (1- (integer-length n))))))))) (equal (logbitp x n) (let ((n (ifix n))) (if (< n 0) (<= (integer-length n) (nfix x)) (< (nfix x) (integer-length n)))))) :hints (("goal" :cases ((logbitp x n)) :in-theory (enable ifix nfix)))))
other
(defsection logbitp-of-const-split (local (in-theory (enable* arith-equiv-forwarding))) (defund scan-for-bit (start val n) (declare (xargs :guard (and (natp start) (booleanp val) (integerp n)))) (if (zp start) nil (let ((start (1- start))) (if (eq (logbitp start n) (mbe :logic (and val t) :exec val)) start (scan-for-bit start val n))))) (local (progn (in-theory (enable scan-for-bit)) (defcong nat-equiv equal (scan-for-bit start val n) 1) (defcong iff equal (scan-for-bit start val n) 2) (defcong int-equiv equal (scan-for-bit start val n) 3) (defthm logbitp-of-scan-for-bit (implies (scan-for-bit start val n) (equal (logbitp (scan-for-bit start val n) n) (and val t)))) (defthm logbitp-of-scan-for-bit-free (implies (and (equal x (scan-for-bit start val n)) (scan-for-bit start val n)) (equal (logbitp x n) (and val t)))) (defthm scan-for-bit-bound (implies (scan-for-bit start val n) (< (scan-for-bit start val n) (nfix start))) :rule-classes :linear) (defthm scan-for-bit-between (implies (and (< (scan-for-bit start val n) bit) (natp bit) (equal val (not (logbitp start n))) (<= bit (nfix start))) (equal (logbitp bit n) (not val))) :hints (("Goal" :in-theory (enable nfix) :induct (scan-for-bit start val n)) '(:cases ((= bit (nfix start)))))) (defthm logbitp-when-not-scan-for-bit (implies (and (not (scan-for-bit start val n)) (syntaxp (not (equal start b))) (equal val (not (logbitp start n))) (<= (nfix b) (nfix start))) (equal (logbitp b n) (not val)))) (in-theory (disable scan-for-bit)))) (defun bit-scan-rec (bit start curr-val n) (declare (xargs :guard (and (natp bit) (natp start) (booleanp curr-val) (integerp n)) :measure (nfix start))) (let* ((nbit (mbe :logic (nfix bit) :exec bit)) (start (mbe :logic (nfix start) :exec start)) (curr-val (mbe :logic (and curr-val t) :exec curr-val)) (n (mbe :logic (ifix n) :exec n)) (next (scan-for-bit start (not curr-val) n))) (cond ((not next) (and curr-val (if (= start 0) (zp bit) (or (not (natp bit)) (<= bit start))))) ((not curr-val) (bit-scan-rec nbit next t n)) ((= next (1- (nfix start))) (or (= bit start) (bit-scan-rec nbit next nil n))) (t (or (and (<= nbit start) (> nbit next)) (bit-scan-rec nbit next nil n)))))) (local (progn (defcong nat-equiv equal (bit-scan-rec bit start curr-val n) 1 :hints (("goal" :expand ((:free (bit) (bit-scan-rec bit start curr-val n))) :in-theory (enable nfix)))) (defcong nat-equiv equal (bit-scan-rec bit start curr-val n) 2 :hints (("goal" :expand ((:free (start) (bit-scan-rec bit start curr-val n)))))) (defcong iff equal (bit-scan-rec bit start curr-val n) 3 :hints (("goal" :expand ((:free (curr-val) (bit-scan-rec bit start curr-val n)))))) (defcong int-equiv equal (bit-scan-rec bit start curr-val n) 4 :hints (("goal" :expand ((:free (n) (bit-scan-rec bit start curr-val n)))))) (defthm <-0-forward-to-nat-equiv-0 (implies (< x 0) (nat-equiv x 0)) :rule-classes :forward-chaining) (defthmd equal-of-booleans (implies (and (booleanp a) (booleanp b)) (equal (equal a b) (iff a b)))) (defthm bit-scan-rec-correct (implies (and (natp start) (equal curr-val (logbitp start n))) (equal (bit-scan-rec bit start curr-val n) (and (<= (nfix bit) start) (logbitp bit n)))) :hints (("goal" :induct t :in-theory (e/d (nfix ifix) (not (:definition bit-scan-rec))) :expand ((:free (bit val) (bit-scan-rec bit start val n)) (:free (bit val) (bit-scan-rec bit 0 val n)) (:free (val) (bit-scan-rec bit bit val n)))) '(:cases ((logbitp bit n))) (and stable-under-simplificationp '(:in-theory (enable equal-of-booleans))))) (defthm logbitp-when-gte-integer-length (implies (<= (integer-length n) (nfix i)) (equal (logbitp i n) (< (ifix n) 0))) :hints (("goal" :in-theory (enable* ihsext-inductions ihsext-recursive-redefs) :induct (logbitp i n)))) (defthm logbitp-of-integer-length-minus-1 (implies (not (equal 0 (integer-length n))) (equal (logbitp (+ -1 (integer-length n)) n) (<= 0 (ifix n)))) :hints (("goal" :in-theory (enable* ihsext-inductions ihsext-recursive-redefs) :induct (integer-length n)))))) (defthm open-bit-scan-rec (implies (syntaxp (and (quotep start) (quotep curr-val) (quotep n))) (equal (bit-scan-rec bit start curr-val n) (let* ((nbit (mbe :logic (nfix bit) :exec bit)) (start (mbe :logic (nfix start) :exec start)) (curr-val (mbe :logic (and curr-val t) :exec curr-val)) (n (mbe :logic (ifix n) :exec n)) (next (scan-for-bit start (not curr-val) n))) (cond ((not next) (and curr-val (if (= start 0) (zp bit) (or (not (natp bit)) (<= bit start))))) ((not curr-val) (bit-scan-rec nbit next t n)) ((= next (1- (nfix start))) (or (= bit start) (bit-scan-rec nbit next nil n))) (t (or (and (<= nbit start) (> nbit next)) (bit-scan-rec nbit next nil n))))))) :hints (("goal" :in-theory nil :expand ((bit-scan-rec bit start curr-val n))))) (defthmd logbitp-of-const-split (implies (syntaxp (quotep n)) (equal (logbitp bit n) (let ((n (ifix n))) (cond ((= n 0) nil) ((= n -1) t) ((< n 0) (or (>= (nfix bit) (integer-length n)) (not (bit-scan-rec bit (1- (integer-length n)) t (lognot n))))) (t (bit-scan-rec bit (1- (integer-length n)) t n)))))) :hints (("Goal" :in-theory (e/d nil (bit-scan-rec))) (and stable-under-simplificationp (if (member-equal '(negp n) clause) '(:cases ((< bit (integer-length n)))) '(:use ((:instance bit-scan-rec-correct (start (+ -1 (integer-length n))) (curr-val t) (n (lognot n)))) :in-theory (e/d nil (bit-scan-rec bit-scan-rec-correct))))))) (add-wizard-advice :pattern (logbitp x const) :restrict (and (quotep const) (not (let* ((n (ifix (unquote const))) (len (integer-length n))) (or (= len 0) (if (< n 0) (= n (lognot (ash 1 (1- (integer-length n))))) (= n (ash 1 (1- (integer-length n))))) (if (< n 0) (equal n (lognot (1- (ash 1 (1- (integer-length n)))))) (equal n (1- (ash 1 (1- (integer-length n)))))))))) :msg "Possibly enable ~x0 to match ~x1; this may cause ~ some case splitting." :args (list 'logbitp-of-const-split `(logbitp ,BITOPS::X ,BITOPS::CONST))))
other
(defsection equal-by-logbitp-hint :parents (bitops/equal-by-logbitp) :short "Basic automation for @(see equal-by-logbitp)." :long "<p>The @('equal-by-logbitp-hint') computed hint looks for goals of the form:</p> @({ (implies (and hyp1 hyp2 ...) (equal lhs rhs)) }) <p>And automatically generates an appropriate @(':functional-instance') of the @(see equal-by-logbitp) theorem. A typical use of this hint might be:</p> @({ :hints(("Goal" :in-theory (enable foo bar)) (and stable-under-simplificationp (equal-by-logbitp-hint))) }) <p>Note that this hint is very aggressive. For instance, it doesn't try to determine whether @('lhs') and @('rhs') are numbers; it will try to use @(see equal-by-logbitp) anyway. Because of this, you would never want to add this to the @(see default-hints).</p>" (defun equal-by-logbitp-hint-fn (clause) (declare (xargs :mode :program)) (b* ((concl (car (last clause))) ((unless (and (consp concl) (eq (car concl) 'equal))) nil) (lhs (cadr concl)) (rhs (caddr concl)) (hyp (cons 'and (dumb-negate-lit-lst (butlast clause 1))))) `(:use ((:functional-instance equal-by-logbitp (logbitp-hyp (lambda nil ,BITOPS::HYP)) (logbitp-lhs (lambda nil ,BITOPS::LHS)) (logbitp-rhs (lambda nil ,BITOPS::RHS))))))) (defmacro equal-by-logbitp-hint nil `(equal-by-logbitp-hint-fn clause)))
other
(defsection open-logbitp-of-const-meta :parents (bitops/equal-by-logbitp logbitp) :short "Rewrite terms like @('(logbitp foo 7)') to @('(or (not (natp foo)) (member-equal foo '(0 1 2)))')." :long "<p>This meta rule targets terms of the form</p> @({ (logbitp term const) }) <p>where @('const') is a quoted constant, typically a number. We know that such a term can only be true when @('term') happens to be one of the bit positions that has a @('1') bit set in @('const'), so we can split into cases based on which bits of @('const') are set.</p> <p>Note that this rule basically is going to split into @('n') cases, where @('n') is the number of @('1') bits in @('const')! Because of this we keep it disabled. But if you see a @('logbitp') term applied to a constant, you might want to consider enabling this rule.</p>" (defevaluator lbpc-ev lbpc-ev-lst ((logbitp a b) (not a) (if a b c) (equal a b) (natp a) (zp a) (member-equal a x))) (defund bits-between (n m x) (declare (xargs :guard (and (natp n) (natp m) (<= n m) (integerp x)) :hints (("Goal" :in-theory (enable nfix))) :measure (nfix (- (nfix m) (nfix n))))) (let* ((n (lnfix n)) (m (lnfix m))) (cond ((mbe :logic (zp (- m n)) :exec (= m n)) nil) ((logbitp n x) (cons n (bits-between (+ n 1) m x))) (t (bits-between (+ n 1) m x))))) (local (defthm member-equal-of-bits-between (iff (member-equal a (bits-between n m x)) (and (natp a) (<= (nfix n) a) (< a (nfix m)) (logbitp a x))) :hints (("Goal" :in-theory (enable bits-between) :induct (bits-between n m x))))) (defund enumerate-logbitps (x) (declare (xargs :guard (integerp x))) (bits-between 0 (integer-length x) x)) (local (defun ind (n x) (if (zp n) (list n x) (ind (- n 1) (logcdr x))))) (local (defthm crock (implies (and (natp x) (<= (integer-length x) (nfix n))) (not (logbitp n x))) :hints (("Goal" :induct (ind n x) :in-theory (enable* logbitp** integer-length**))))) (local (defthm member-equal-of-enumerate-logbitps-for-naturals (implies (and (natp x) (natp a)) (iff (member-equal a (enumerate-logbitps x)) (logbitp a x))) :hints (("Goal" :in-theory (enable enumerate-logbitps))))) (defund open-logbitp-of-const (term) (declare (xargs :guard (pseudo-termp term))) (case-match term (('logbitp x ('quote const . &) . &) (cond ((or (not (integerp const)) (= const 0)) ''nil) ((= const -1) ''t) ((natp const) `(if (natp ,BITOPS::X) (if (member-equal ,BITOPS::X ',(BITOPS::ENUMERATE-LOGBITPS BITOPS::CONST)) 't 'nil) ',(LOGBITP 0 BITOPS::CONST))) (t `(if (natp ,BITOPS::X) (not (member-equal ,BITOPS::X ',(BITOPS::ENUMERATE-LOGBITPS (LOGNOT BITOPS::CONST)))) ',(LOGBITP 0 BITOPS::CONST))))) (& term))) (defthmd open-logbitp-of-const-meta (equal (lbpc-ev x a) (lbpc-ev (open-logbitp-of-const x) a)) :rule-classes ((:meta :trigger-fns (logbitp))) :hints (("Goal" :in-theory (enable* open-logbitp-of-const arith-equiv-forwarding)))) (local (defthm logbitp-of-ash-free-lemma (implies (equal x (ash 1 y)) (equal (logbitp n x) (equal (nfix n) (ifix y)))) :hints (("Goal" :in-theory (enable logbitp-of-ash-split))))) (local (in-theory (enable* arith-equiv-forwarding))) (local (defthm logbitp-of-ash-free-lemma2 (implies (and (equal (lognot x) (ash 1 y)) (natp y)) (equal (logbitp n x) (not (equal (nfix n) (ifix y))))) :hints (("Goal" :in-theory (e/d nil (logbitp-of-ash-free-lemma)) :use ((:instance logbitp-of-ash-free-lemma (x (lognot x)))))))) (local (defthm logbitp-of-ash-free-lemma3 (equal (logbitp n -2) (not (zp n))) :hints (("Goal" :in-theory (e/d* (logbitp**)))))) (defund open-logbitp-of-const-lite (term) (declare (xargs :guard (pseudo-termp term))) (case-match term (('logbitp x ('quote const . &) . &) (cond ((or (not (integerp const)) (= const 0)) ''nil) ((= const -1) ''t) ((natp const) (let ((len (1- (integer-length const)))) (if (equal const (ash 1 len)) (if (= len 0) `(zp ,BITOPS::X) `(equal ,BITOPS::X ',BITOPS::LEN)) term))) (t (let* ((const (lognot const)) (len (1- (integer-length const)))) (if (equal const (ash 1 len)) (if (= len 0) `(not (zp ,BITOPS::X)) `(not (equal ,BITOPS::X ',BITOPS::LEN))) term))))) (& term))) (defthm open-logbitp-of-const-lite-meta (equal (lbpc-ev x a) (lbpc-ev (open-logbitp-of-const-lite x) a)) :rule-classes ((:meta :trigger-fns (logbitp))) :hints (("Goal" :in-theory (e/d* (open-logbitp-of-const-lite arith-equiv-forwarding nfix ifix))))))
other
(defsection equal-by-logbitp-hammer :parents (bitops/equal-by-logbitp) :short "Drastic automation for @(see equal-by-logbitp)." :long "<p>This is an enhanced version of @(see equal-by-logbitp-hint) that also enables a bunch of case-splitting rules that allow @(see logbitp) to go through functions like @(see ash), @(see loghead), @(see install-bit), etc.</p>" (def-ruleset! logbitp-case-splits '(logbitp-of-ash-split logbitp-of-loghead-split logbitp-of-logapp-split logbitp-of-logsquash-split logbitp-of-logrev-split)) (def-ruleset! logbitp-case-splits+ '(logbitp-case-splits logbitp-when-bit b-not b-and b-ior b-xor b-eqv b-nand b-nor b-andc1 b-andc2 b-orc1 b-orc2 nfix ifix bfix)) (defmacro equal-by-logbitp-hammer nil '(and stable-under-simplificationp '(:computed-hint-replacement ((and stable-under-simplificationp '(:in-theory (e/d* (logbitp-of-const-split)))) (and stable-under-simplificationp '(:in-theory (e/d* (logbitp-case-splits logbitp-when-bit logbitp-of-const-split)))) (and stable-under-simplificationp (equal-by-logbitp-hint)) (and stable-under-simplificationp '(:in-theory (e/d* (logbitp-case-splits logbitp-when-bit logbitp-of-const-split b-xor b-ior b-and))))) :no-thanks t))))
other
(define logbitp-mismatch? ((x integerp) (y integerp)) (nfix (logbitp-mismatch x y)) /// (local (in-theory (e/d (logbitp-mismatch) (int-equiv)))) (defcong int-equiv equal (logbitp-mismatch? x y) 1) (defcong int-equiv equal (logbitp-mismatch? x y) 2) (defthm logbitp-mismatch?-correct (implies (not (equal (ifix x) (ifix y))) (let ((i (logbitp-mismatch? x y))) (not (equal (logbitp i x) (logbitp i y)))))))
other
(defsection equal-by-logbitp-witnessing-rules
(definstantiate equal-by-logbitp-instancing
:predicate (equal x y)
:vars (bit)
:expr (equal (logbitp bit x) (logbitp bit y))
:hints ('(:in-theory nil)))
(defwitness unequal-by-logbitp-witnessing
:predicate (not (equal x y))
:expr (or (let ((bit (hide (logbitp-mismatch? x y))))
(and (natp bit)
(not (equal (logbitp bit x) (logbitp bit y)))))
(and (zip x)
(zip y)
(not (and (integerp x) (integerp y)))))
:generalize (((hide (logbitp-mismatch? x y)) . wbit))
:hints ('(:use logbitp-mismatch?-correct
:in-theory (e/d (ifix)
(logbitp-mismatch?-correct))
:expand ((:free (x) (hide x)))))))
other
(defines eqbylbp-collect-terms :verify-guards nil :prepwork ((local (defthm pseudo-term-listp-of-union (implies (and (pseudo-term-listp a) (pseudo-term-listp b)) (pseudo-term-listp (union-equal a b)))))) (define eqbylbp-collect-terms ((x pseudo-termp)) :returns (terms pseudo-term-list-listp :hyp :guard) (b* (((when (or (atom x) (eq (car x) 'quote))) nil) (rest (eqbylbp-collect-terms-list (cdr x)))) (if (eq (car x) 'logbitp) (cons (cdr x) rest) rest))) (define eqbylbp-collect-terms-list ((x pseudo-term-listp)) :returns (terms pseudo-term-list-listp :hyp :guard) (if (atom x) nil (union-equal (eqbylbp-collect-terms (car x)) (eqbylbp-collect-terms-list (cdr x))))) /// (local (defthm pseudo-term-listp-implies-true-listp (implies (pseudo-term-list-listp x) (true-listp x)))) (verify-guards eqbylbp-collect-terms))
other
(defaggregate eqbylbp-config ((restriction pseudo-termp) (witness-rule wcp-witness-rule-p) (instance-rule (and (wcp-instance-rule-p instance-rule) (equal (len (wcp-instance-rule->vars instance-rule)) 1))) (prune-examples) (passes posp) (simp-hint) (add-hints) (verbosep)))
other
(define eqbylbp-check-witnesses
((x pseudo-term-listp) (config eqbylbp-config-p)
state)
:returns (mv (apply-witness-list boolean-listp)
(new-terms pseudo-term-listp))
(b* (((when (atom x)) (mv nil nil)) ((eqbylbp-config config) config)
(rule config.witness-rule)
((wcp-witness-rule rule) rule)
(restriction-term config.restriction)
((mv rest-apply rest-terms) (eqbylbp-check-witnesses (cdr x)
config
state))
((unless (mbt (and (pseudo-termp (car x))
(wcp-witness-rule-p rule)))) (mv (cons nil rest-apply)
rest-terms))
((mv unify-ok alist) (simple-one-way-unify rule.term (car x) nil))
((unless unify-ok) (mv (cons nil rest-apply)
rest-terms))
((mv er val) (if (equal restriction-term ''t)
(mv nil t)
(witness-eval-restriction restriction-term
alist
state)))
(- (and er
(raise "Restriction term evaluation failed! ~x0"
er)))
((when (or er (not val))) (mv (cons nil rest-apply)
rest-terms))
(new-term (substitute-into-term rule.expr alist)))
(mv (cons t rest-apply)
(cons new-term rest-terms)))
///
(defthm eqbylbp-check-witnesses-len-of-apply-list
(equal (len (mv-nth 0
(eqbylbp-check-witnesses x
config
state)))
(len x))))
other
(define eqbylbp-simplify ((x pseudo-termp) (config eqbylbp-config-p) (equiv symbolp) state) :mode :program (b* (((eqbylbp-config config) config) ((mv erp rw state) (easy-simplify-term1-fn x nil config.simp-hint equiv t t 1000 1000 state)) ((when erp) (raise "Logbitp-reasoning: error simplifying ~x0: ~x1" x erp) (mv x state))) (mv rw state)))
other
(define eqbylbp-simplify-each
((x pseudo-term-listp) (config eqbylbp-config-p)
(equiv symbolp)
state)
:mode :program (b* (((when (atom x)) (mv nil state)) ((mv first state) (eqbylbp-simplify (car x)
config
equiv
state))
((mv rest state) (eqbylbp-simplify-each (cdr x)
config
equiv
state)))
(mv (cons first rest) state)))
other
(define eqbylbp-is-var ((x pseudo-termp) (var symbolp)) (or (eq x var) (and (consp x) (eq (car x) 'nfix) (eq (cadr x) var))))
other
(encapsulate (((eqbylbp-solve-for-var * * *) => (mv * *) :formals (x var target) :guard (and (pseudo-termp x) (symbolp var) (pseudo-termp target)))) (local (defun eqbylbp-solve-for-var (x var target) (declare (ignore var target)) (mv t x))) (defthm pseudo-termp-of-eqbbylbp-solve-for-var (implies (and (pseudo-termp x) (pseudo-termp target)) (pseudo-termp (mv-nth 1 (eqbylbp-solve-for-var x var target))))))
other
(define eqbylbp-solve-for-var-default ((x pseudo-termp) (var symbolp) (target pseudo-termp)) :returns (mv ok (res pseudo-termp :hyp (and (pseudo-termp x) (pseudo-termp target)))) (b* (((when (eqbylbp-is-var x var)) (mv t target)) ((when (atom x)) (mv nil nil)) ((when (eq (car x) 'unary--)) (eqbylbp-solve-for-var-default (cadr x) var `(unary-- ,BITOPS::TARGET))) ((unless (eq (car x) 'binary-+)) (mv nil nil)) ((when (eqbylbp-is-var (cadr x) var)) (mv t `(binary-+ ,BITOPS::TARGET (unary-- ,(CADDR BITOPS::X)))))) (eqbylbp-solve-for-var-default (caddr x) var `(binary-+ ,BITOPS::TARGET (unary-- ,(CADR BITOPS::X))))))
other
(defattach eqbylbp-solve-for-var eqbylbp-solve-for-var-default)
other
(define eqbylbp-collect-examples-for-target ((avail-logbitp-args pseudo-term-list-listp) (var symbolp) (target-logbitp-args pseudo-term-listp) (examples pseudo-term-listp) (config eqbylbp-config-p) state) :mode :program (b* (((when (atom avail-logbitp-args)) (mv examples state)) ((unless (equal (cadr (car avail-logbitp-args)) (cadr target-logbitp-args))) (eqbylbp-collect-examples-for-target (cdr avail-logbitp-args) var target-logbitp-args examples config state)) ((mv ok res) (eqbylbp-solve-for-var (car (car avail-logbitp-args)) var (car target-logbitp-args))) ((unless ok) (eqbylbp-collect-examples-for-target (cdr avail-logbitp-args) var target-logbitp-args examples config state)) (simp res) ((mv rest state) (eqbylbp-collect-examples-for-target (cdr avail-logbitp-args) var target-logbitp-args examples config state))) (mv (if (member-equal simp rest) rest (cons simp rest)) state)))
other
(define eqbylbp-collect-examples-targets ((avail-logbitp-args pseudo-term-list-listp) (var symbolp) (target-logbitp-args pseudo-term-list-listp) (examples pseudo-term-listp) (config eqbylbp-config-p) state) :mode :program (b* (((when (atom target-logbitp-args)) (mv examples state)) ((mv examples state) (eqbylbp-collect-examples-for-target avail-logbitp-args var (car target-logbitp-args) examples config state))) (eqbylbp-collect-examples-targets avail-logbitp-args var (cdr target-logbitp-args) examples config state)))
other
(define eqbylbp-eval-example ((alist pseudo-term-substp) (example pseudo-termp) (config eqbylbp-config-p) state) :mode :program (b* (((eqbylbp-config config) config) (rule config.instance-rule) ((wcp-instance-rule rule) rule) (alist1 (append (pairlis$ rule.vars (list example)) alist)) (newterm (wcp-beta-reduce-term (substitute-into-term rule.expr alist1))) ((mv newterm-rw state) (eqbylbp-simplify newterm config 'iff state)) (includep (equal newterm-rw ''t))) (mv includep (eqbylbp-collect-terms newterm-rw) state)))
other
(define eqbylbp-try-example ((alist pseudo-term-substp) (example pseudo-termp) (target-logbitp-args pseudo-term-list-listp) (config eqbylbp-config-p) state) :mode :program (b* (((eqbylbp-config config) config) ((mv includep new-logbitp-args state) (eqbylbp-eval-example alist example config state)) (intersection (intersection-equal new-logbitp-args target-logbitp-args)) ((unless (or includep intersection (not config.prune-examples))) (and config.verbosep (cw "Rejected: ~x0 (produced: ~x1)~%" example new-logbitp-args)) (mv nil target-logbitp-args state)) (new-targets (set-difference-equal new-logbitp-args intersection))) (mv (list (make-wcp-example-app :instrule config.instance-rule :bindings (list example))) (append new-targets target-logbitp-args) state)))
other
(define eqbylbp-try-examples
((alist pseudo-term-substp) (examples pseudo-term-listp)
(target-logbitp-args pseudo-term-list-listp)
(config eqbylbp-config-p)
state)
:mode :program (b* (((when (atom examples)) (mv nil target-logbitp-args state)) ((mv first-examples
target-logbitp-args
state) (eqbylbp-try-example alist
(car examples)
target-logbitp-args
config
state))
((mv rest-examples
target-logbitp-args
state) (eqbylbp-try-examples alist
(cdr examples)
target-logbitp-args
config
state)))
(mv (append first-examples rest-examples)
target-logbitp-args
state)))
other
(define eqbylbp-decide-examples-lit ((lit pseudo-termp) (var symbolp) (target-logbitp-args pseudo-term-list-listp) (config eqbylbp-config-p) state) :mode :program (b* (((unless (mbt (and (pseudo-termp lit) (eqbylbp-config-p config)))) (mv nil target-logbitp-args state)) ((eqbylbp-config config) config) (rule config.instance-rule) ((wcp-instance-rule rule) rule) ((mv unify-ok alist) (simple-one-way-unify rule.pred lit nil)) ((unless unify-ok) (mv nil target-logbitp-args state)) (restriction-term config.restriction) ((mv er res) (if (equal restriction-term ''t) (mv nil t) (witness-eval-restriction restriction-term alist state))) (- (and er (raise "Restriction term evaluation failed! ~x0" er))) ((unless (and (not er) res)) (mv nil target-logbitp-args state)) ((mv & avail-logbitp-args state) (eqbylbp-eval-example alist var config state)) ((mv example-terms state) (eqbylbp-collect-examples-targets avail-logbitp-args var target-logbitp-args nil config state)) ((mv examples target-logbitp-args state) (eqbylbp-try-examples alist example-terms target-logbitp-args config state)) ((when examples) (mv examples target-logbitp-args state))) (mv (list (make-wcp-example-app :instrule rule :bindings (list var))) (union-equal avail-logbitp-args target-logbitp-args) state)))
other
(define eqbylbp-decide-examples
((clause pseudo-term-listp) (var symbolp)
(target-logbitp-args pseudo-term-list-listp)
(config eqbylbp-config-p)
state)
:mode :program (b* (((when (atom clause)) (mv nil target-logbitp-args state)) ((mv examples1
target-logbitp-args
state) (eqbylbp-decide-examples-lit (car clause)
var
target-logbitp-args
config
state))
((mv rest-examples
target-logbitp-args
state) (eqbylbp-decide-examples (cdr clause)
var
target-logbitp-args
config
state)))
(mv (cons examples1 rest-examples)
target-logbitp-args
state)))
other
(define eqbylbp-decide-examples-passes ((count posp) (clause pseudo-term-listp) (var symbolp) (target-logbitp-args pseudo-term-list-listp) (config eqbylbp-config-p) state) :mode :program (b* (((mv examples target-logbitp-args state) (eqbylbp-decide-examples clause var target-logbitp-args config state)) (count (1- count)) ((when (zp count)) (mv examples target-logbitp-args state))) (eqbylbp-decide-examples-passes count clause var target-logbitp-args config state)))
other
(define wcp-example-apps-listp (x) (if (atom x) (eq x nil) (and (wcp-example-appsp (car x)) (wcp-example-apps-listp (cdr x)))) /// (defopen wcp-example-apps-listp-when-consp (wcp-example-apps-listp x) :hyp (consp x) :hint (:expand ((wcp-example-apps-listp x))) :rule-classes ((:rewrite :backchain-limit-lst 0))))
other
(define eqbylbp-pair-hints ((witness-apps boolean-listp) (examples wcp-example-apps-listp) (config eqbylbp-config-p)) :guard (eql (len witness-apps) (len examples)) :returns (actions wcp-lit-actions-listp) (if (atom witness-apps) nil (cons (make-wcp-lit-actions :witnesses (and (car witness-apps) (mbt (eqbylbp-config-p config)) (list (eqbylbp-config->witness-rule config))) :examples (and (mbt (wcp-example-appsp (car examples))) (car examples))) (eqbylbp-pair-hints (cdr witness-apps) (cdr examples) config))))
other
(define eqbylbp-witness-hints
((clause pseudo-term-listp) (config eqbylbp-config-p)
state)
:mode :program (b* (((eqbylbp-config config) config) ((mv apply-witnesses new-lits) (eqbylbp-check-witnesses clause
config
state))
((mv new-lits-simp state) (eqbylbp-simplify-each (wcp-beta-reduce-list new-lits)
config
'iff
state))
(targets (eqbylbp-collect-terms-list (append new-lits-simp clause)))
(- (and config.verbosep
(cw "Targets: ~x0~%" targets)))
((mv examples ?targets state) (eqbylbp-decide-examples-passes config.passes
clause
'eqbylbp-index
targets
config
state)))
(mv (eqbylbp-pair-hints apply-witnesses
examples
config)
state)))
other
(define logbitp-reasoning-fn
(clause restrict
passes
prune-examples
simp-hint
add-hints
verbosep
stablep
state)
:mode :program (b* (((unless stablep) (value nil)) ((er restrict-term) (translate restrict
t
nil
t
'logbitp-reasoning
(w state)
state))
(witness-rule (cdr (assoc 'unequal-by-logbitp-witnessing
(table-alist 'witness-cp-witness-rules
(w state)))))
(instance-rule (cdr (assoc 'equal-by-logbitp-instancing
(table-alist 'witness-cp-instance-rules
(w state)))))
(config (make-eqbylbp-config :restriction restrict-term
:witness-rule witness-rule
:instance-rule instance-rule
:prune-examples prune-examples
:simp-hint simp-hint
:add-hints add-hints
:passes passes
:verbosep verbosep))
((mv cp-hint state) (eqbylbp-witness-hints clause
config
state)))
(value `(:clause-processor (witness-cp clause
',BITOPS::CP-HINT
state) . ,BITOPS::ADD-HINTS))))
other
(defxdoc logbitp-reasoning :parents (bitops) :short "A computed hint for proving bit-twiddling theorems by smartly sampling bits" :long "<p>@('Logbitp-reasoning') is a computed hint for proving theorems about bitvector operations. Example usage:</p> @({ (defthm pass-context-of-ash (implies (equal (logand (ash mask (- (ifix n))) a1) (logand (ash mask (- (ifix n))) a2)) (equal (logand mask (ash a1 n)) (logand mask (ash a2 n)))) :hints ((logbitp-reasoning))) }) <p>It works by:</p> <ul> <li>Creating <i>witnesses</i> for inequality hyps/equality conclusions, replacing @('(not (equal a b))') with: @({ (implies (and (integerp x) (integerp y)) (and (natp bit) (not (equal (logbitp bit x) (logbitp bit y))))) }) where @('bit') is a fresh variable,</li> <li><i>Instantiating</i> equality hyps/inequality conclusions, replacing @('(equal a b)') with @('(equal (logbitp bit a) (logbitp bit b))'), for one or more several values of @('bit').</li> </ul> <p>The main work done by this computed hint is to decide how to instantiate @('bit') for each of the equality hyps/inequality conclusions. To do this we:</p> <ol> <li>Keep track of a list of logbitp term "targets", which we think of as already appearing in our goal either due to witnessing or instantiation.</li> <li>Try to instantiate equality hyps so as to create more occurrences of existing targets.</li> </ol> <p>We take @('pass-context-of-ash') above as an example.</p> <ol> <li>First we find the literals of the clause that we'll create witnesses for -- in this case, the conclusion. We'll introduce some new variable @('wbit') and our new conclusion will be (omitting some type info that isn't directly relevant) @({ (equal (logbitp wbit (logand mask (ash a1 n))) (logbitp wbit (logand mask (ash a2 n)))) }) </li> <li>Next we simplify this new conclusion and extract the logbitp terms that the simplified term contains: @({ (logbitp wbit mask) (logbitp (+ (- (ifix n)) wbit) a1) (logbitp (+ (- (ifix n)) wbit) a2) }) These are now our target terms.</li> <li>Next we look for instantiations of our hypothesis that, when simplified, will contain one or more of these target terms. To do this, we first instantiate it with a variable @('ibit'), obtaining: @({ (equal (logbitp ibit (logand (ash mask (- (ifix n))) a1)) (logbitp ibit (logand (ash mask (- (ifix n))) a1))) }) </li> <li>Then we simplify the result and extract the resulting logbitp terms: @({ (logbitp ibit a1) (logbitp ibit a2) (logbitp (+ (ifix n) (nfix ibit)) mask) }) </li> <li>Now we try to find values of @('ibit') that will make one or more of these results match one or more of the target terms. We immediately find that by setting @('ibit') to @('(+ (- (nfix n)) wbit)') we create some matches. So we decide to instantiate the hyp using this term as our bit index.</li> <li>All of the above was done just to compute a hint. The actual hint we provide is a call to @(see witness-cp), a clause processor that supports this sort of witness creation and instantiation, with instructions to do the witnessing and instantiation steps that we've settled on. Once this clause processor runs, the resulting proof splits into 8 subgoals that are all quickly proved.</li> </ol> <p>@('Logbitp-reasoning') is a macro that can take a few optional arguments, but reasonable defaults (in the invocation below) are provided:</p> @({ :hints ((logbitp-reasoning :restrict t :passes 1 :verbosep nil :simp-hint (:in-theory (enable* logbitp-case-splits logbitp-when-bit logbitp-of-const-split)) :add-hints (:in-theory (enable* logbitp-case-splits logbitp-when-bit logbitp-of-const-split)))) }) <p>The meanings of these:</p> <ul> <li>@(':restrict') is a term in the variables @('x') and @('y') that restricts the equality literals to which we will apply witnessing/instantiation. For example, @({ :restrict (or (and (consp x) (eq (car x) 'binary-logand)) (and (consp y) (eq (car y) 'binary-logand))) }) will cause the hint to ignore any equality literals that don't have an argument that is a call of logand.</li> <li>@(':passes') determines the number of passes through the clause we use to collect instantiations and target terms. Instantiations can add new targets, and the more targets there are, the more instantiations may be found.</li> <li>@(':simp-hint') is the hint given to the simplifier while deciding on the instantiations.</li> <li>@(':add-hints') are hints given at the same time as the clause processor hint.</li> </ul> ")
logbitp-reasoningmacro
(defmacro logbitp-reasoning (&key (restrict 't) (passes '1) (verbosep 'nil) (prune-examples 't) (simp-hint '(:in-theory (enable* logbitp-case-splits logbitp-when-bit logbitp-of-const-split))) (add-hints '(:in-theory (enable* logbitp-case-splits logbitp-when-bit logbitp-of-const-split)))) `(logbitp-reasoning-fn clause ',BITOPS::RESTRICT ',BITOPS::PASSES ',BITOPS::PRUNE-EXAMPLES ',BITOPS::SIMP-HINT ',BITOPS::ADD-HINTS ',BITOPS::VERBOSEP stable-under-simplificationp state))
other
(local (include-book "std/system/non-parallel-book" :dir :system))
other
(local (non-parallel-book))
other
(local (defthm pass-context-of-logapp (implies (and (equal (logand (ash mask (- (nfix n))) b1) (logand (ash mask (- (nfix n))) b2)) (equal (logand (loghead n mask) a1) (logand (loghead n mask) a2))) (equal (logand mask (logapp n a1 b1)) (logand mask (logapp n a2 b2)))) :hints ((logbitp-reasoning))))