IF*

The function IF* is defined to be IF, but it is used in a
special way by ACL2's BDD package.

As explained elsewhere (see bdd-algorithm), ACL2's BDD
algorithm gives special treatment to terms of the form
(IF* TEST TBR FBR). In such cases, the algorithm simplifies
TEST first, and the result of that simplification must be a
constant (normally t or nil, but any non-nil explicit value is
treated like t here). Otherwise, the algorithm aborts.

Thus, IF* may be used to implement a sort of conditional
rewriting for ACL2's BDD package, even though this package only
nominally supports unconditional rewriting. The following contrived
example should make this point clear.

Suppose that we want to prove that (nthcdr (length x) (append x y))
is equal to y, but that we would be happy to prove this only for
lists having length 4. We can state such a theorem as follows.

If we want to prove this formula with a :BDD hint, then we need to
have appropriate rewrite rules around. First, note that LENGTH is
defined as follows (try :PELENGTH):

(length x)
=
(if (stringp x)
(len (coerce x 'list))
(len x))

Since BDD-based rewriting is merely very simple unconditional
rewriting (see bdd-algorithm), we expect to have to prove a
rule reducing STRINGP of a CONS:

(defthm stringp-cons
(equal (stringp (cons x y))
nil))

Now we need a rule to compute the LEN of X, because the definition
of LEN is recursive and hence not used by the BDD package.

(defthm len-cons
(equal (len (cons a x))
(1+ (len x))))

We imagine this rule simplifying (LEN (LIST X0 X1 X2 X3)) in terms of
(LEN (LIST X1 X2 X3)), and so on, and then finally (LEN nil) should
be computed by execution (see bdd-algorithm).

We also need to imagine simplifying (APPEND X Y), where still X is
bound to (LIST X0 X1 X2 X3). The following two rules suffice for
this purpose (but are needed, since APPEND, actually BINARY-APPEND,
is recursive).

Finally, we imagine needing to simplify calls of NTHCDR, where the
the first argument is a number (initially, the length of
(LIST X0 X1 X2 X3), which is 4). The second lemma below is the
traditional way to accomplish that goal (when not using BDDs), by
proving a conditional rewrite rule. (The first lemma is only proved
in order to assist in the proof of the second lemma.)

The problem with this rule is that its hypothesis makes it a
conditional rewrite rule, and conditional rewrite rules
are not used by the BDD package. (See bdd-algorithm for a
discussion of ``BDD rules.'') (Note that the hypothesis cannot
simply be removed; the resulting formula would be false for n = -1
and x = '(a), for example.) We can solve this problem by using
IF*, as follows; comments follow.

How is nthcdr-add1 applied by the BDD package? Suppose that the BDD
computation encounters a term of the form (NTHCDR (+ 1 N) X).
Then the BDD package will apply the rewrite rule nthcdr-add1. The
first thing it will do when attempting to simplify the right hand
side of that rule is to attempt to simplify the term (ZP (1+ N)).
If N is an explicit number (which is the case in the scenario we
envision), this test will reduce (assuming the executable
counterparts of ZP and BINARY-+ are enabled) to t or
to nil. In fact, the lemmas above (not including the lemma
nthcdr-add1-conditional) suffice to prove our goal:

BDD computation was aborted on Goal, and hence there is no
falsifying assignment that can be constructed. Here is a backtrace
of calls, starting with the top-level call and ending with the one
that led to the abort. See :DOC show-bdd.

Each of these term-alist pairs led to the next, and the test of the
last one, namely (ZP (+ 1 N)) where N is bound to 3, was
not simplified to t or to nil.

What would have happened if we had used IF in place of IF* in
the rule nthcdr-add1? In that case, if ZP and its executable
counterpart were disabled then we would be put into an infinite
loop! For, each time a term of the form (NTHCDR k V) is
encountered by the BDD package (where k is an explicit number), it
will be rewritten in terms of (NTHCDR k-1 (CDR V)). We would
prefer that if for some reason the term (ZP (+ 1 N)) cannot be
decided to be t or to be nil, then the BDD computation should
simply abort.

Even if there were no infinite loop, this kind of use of IF* is
useful in order to provide feedback of the form shown above whenever
the test of an IF term fails to simplify to t or to nil.