Filtering...

membership

books/std/osets/membership
other
(in-package "SET")
other
(include-book "primitives")
other
(include-book "computed-hints")
other
(set-verify-guards-eagerness 2)
other
(defsection in
  :parents (std/osets)
  :short "@(call in) determines if @('a') is a member of the set @('X')."
  :long "<p>The logical definition of @('in') makes no mention of the set
order, except implicitly by the use of the set @(see primitives) like @(see
head) and @(see tail).</p>

<p>The :exec version just inlines the set primitives and does one level of loop
unrolling.  On CCL, it seems to run about 2.6x faster on the following
loop:</p>

@({
 ;; 4.703 sec logic, 1.811 sec exec
 (let ((big-set (loop for i from 1 to 100000 collect i)))
   (gc$)
   (time (loop for i fixnum from 1 to 30000 do (set::in i big-set))))
})

<p>There are other ways we could optimize @('in').  Since the set is ordered,
we could try to use the set order @(see <<) to stop early when we ran into an
element that is larger than the one we are looking for.  For instance, when
looking for 1 in the set '(2 3 4), we know that since @('1 << 2') that @('1')
cannot be a member of this set.</p>

<p>The simplest way to do this is to use @('<<') at every element.  But set
order comparisons can be very expensive, especially when sets contain large
cons structures.  So while it is easy to contrive situations where exploiting
the order would be advantageous, like</p>

@({
 (in 1 '(2 3 4 .... 100000))
})

<p>where we could return instantly, there are also times where it would be
slower.  For instance, on</p>

@({
 (in 100001 '(1 2 3 4 ... 100000))
})

<p>we would incur the extra cost of 100,000 calls to @('<<').</p>

<p>For this reason, we do not currently implement any short-circuiting.  The
reasoning is:</p>

<ul>

<li>it is not clear which would be faster in all cases,</li>

<li>it is not clear what the typical usage behavior of @('in') is, so even if
we wanted to benchmark alternate implementations, it may be hard to come up
with the right benchmarking suite</li>

<li>both solutions are O(n) anyway, and @('in') isn't a function that should
probably be used in any kind of loop so its performance shouldn't be especially
critical to anything</li>

<li>the current method is arguably no less efficient than an unordered
implementation.</li>

</ul>

<p>Future note.  In principle membership in an ordered list might be done in
@('O(log_2 n)').  We are considering using a <i>galloping</i> membership check
in the future to obtain something along these lines.</p>"
  (defun in
    (a x)
    (declare (xargs :guard (setp x) :verify-guards nil))
    (mbe :logic (and (not (emptyp x))
        (or (equal a (head x))
          (in a (tail x))))
      :exec (and x
        (or (equal a (car x))
          (and (cdr x)
            (or (equal a (cadr x))
              (in a (cddr x))))))))
  (verify-guards in
    :hints (("Goal" :in-theory (enable (:ruleset primitive-rules)))))
  (defthm in-type
    (or (equal (in a x) t)
      (equal (in a x) nil))
    :rule-classes :type-prescription)
  (encapsulate nil
    (local (defthm head-not-whole
        (implies (not (emptyp x))
          (not (equal (head x) x)))
        :hints (("Goal" :in-theory (enable (:ruleset primitive-rules))))))
    (local (defthm lemma
        (implies (> (acl2-count x) (acl2-count y))
          (not (in x y)))))
    (defthm not-in-self (not (in x x))))
  (defthm in-sfix-cancel
    (equal (in a (sfix x))
      (in a x)))
  (defthm never-in-empty
    (implies (emptyp x)
      (not (in a x))))
  (defthm in-set
    (implies (in a x) (setp x)))
  (defthm in-tail
    (implies (in a (tail x))
      (in a x)))
  (defthm in-tail-or-head
    (implies (and (in a x)
        (not (in a (tail x))))
      (equal (head x) a)))
  (defthm in-head
    (equal (in (head x) x)
      (not (emptyp x)))))
other
(defsection head-unique
  :extension head
  (local (defthm lemma
      (implies (and (not (emptyp x))
          (not (equal a (head x)))
          (not (<< a (head (tail x))))
          (<< a (head x)))
        (not (in a x)))
      :hints (("Goal" :in-theory (enable (:ruleset order-rules))
         :cases ((emptyp (tail x)))))))
  (defthm head-minimal
    (implies (<< a (head x))
      (not (in a x)))
    :hints (("Goal" :in-theory (enable (:ruleset order-rules)))))
  (defthm head-minimal-2
    (implies (in a x)
      (not (<< a (head x)))))
  (add-to-ruleset order-rules
    '(head-minimal head-minimal-2))
  (local (defthm lemma2
      (implies (emptyp (tail x))
        (not (in (head x) (tail x))))))
  (local (defthm lemma3
      (implies (not (emptyp (tail x)))
        (not (in (head x) (tail x))))
      :hints (("Goal" :in-theory (enable (:ruleset order-rules))))))
  (defthm head-unique
    (not (in (head x) (tail x)))
    :hints (("Goal" :use ((:instance lemma2) (:instance lemma3))))))
other
(defsection in-insert
  :extension insert
  (defthm insert-identity
    (implies (in a x)
      (equal (insert a x) x))
    :hints (("Goal" :in-theory (enable head-tail-same
         (:ruleset order-rules)))))
  (defthm in-insert
    (equal (in a (insert b x))
      (or (in a x) (equal a b)))
    :hints (("Goal" :in-theory (enable (:ruleset order-rules))
       :induct (insert b x)))))
other
(defsection weak-insert-induction
  :parents (insert)
  :short "Inducting over insert without exposing the set order."
  :long "<p>When we want to insert an element into an ordered set, the set
order obviously has to be involved so that we can decide where to put the new
element.  Accordingly, the set order plays a role in the induction scheme that
we get from @(see insert)'s definition.  This makes insert somewhat different
than other set operations (membership, union, cardinality, etc.) that just use
a simple @(see tail)-based induction, where the set order is already hidden by
@('tail').</p>

<p>When we are proving theorems about sets, we generally want to avoid thinking
about the set order, but we sometimes need to induct over @('insert').  So,
here we introduce a new induction scheme that allows us to induct over insert
but hides the set order.  We disable the ordinary induction scheme that insert
uses, and set up an induction hint so that @('weak-insert-induction') will
automatically be used instead.</p>"
  (defthm weak-insert-induction-helper-1
    (implies (and (not (in a x))
        (not (equal (head (insert a x)) a)))
      (equal (head (insert a x))
        (head x)))
    :hints (("Goal" :in-theory (enable (:ruleset order-rules)))))
  (defthm weak-insert-induction-helper-2
    (implies (and (not (in a x))
        (not (equal (head (insert a x)) a)))
      (equal (tail (insert a x))
        (insert a (tail x))))
    :hints (("Goal" :in-theory (enable (:ruleset order-rules)))))
  (defthm weak-insert-induction-helper-3
    (implies (and (not (in a x))
        (equal (head (insert a x)) a))
      (equal (tail (insert a x))
        (sfix x)))
    :hints (("Goal" :in-theory (enable (:ruleset order-rules)))))
  (defun weak-insert-induction
    (a x)
    (declare (xargs :guard (setp x)))
    (cond ((emptyp x) nil)
      ((in a x) nil)
      ((equal (head (insert a x)) a) nil)
      (t (list (weak-insert-induction a (tail x))))))
  (in-theory (disable (:induction insert)))
  (defthm use-weak-insert-induction
    t
    :rule-classes ((:induction :pattern (insert a x)
       :scheme (weak-insert-induction a x)))))
other
(defsection subset
  :parents (std/osets)
  :short "@(call subset) determines if @('X') is a subset of @('Y')."
  :long "<p>We use a logically simple definition, but using MBE we exploit the
set order to implement a tail-recursive, linear subset check.</p>

<p>The :exec version of fast-subset just inlines the set primitives and tweaks
the way the order check is done.  It is about 3x faster than the :logic version
of fast-subset on the following loop:</p>

@({
 ;; 3.83 sec logic, 1.24 seconds exec
 (let ((x (loop for i from 1 to 1000 collect i)))
   (gc$)
   (time$ (loop for i fixnum from 1 to 100000 do (set::subset x x))))
})

<p>In the future we may investigate developing a faster subset check based on
galloping.</p>"
  (defun fast-subset
    (x y)
    (declare (xargs :guard (and (setp x) (setp y))
        :guard-hints (("Goal" :in-theory (enable (:ruleset primitive-rules) <<)))))
    (mbe :logic (cond ((emptyp x) t)
        ((emptyp y) nil)
        ((<< (head x) (head y)) nil)
        ((equal (head x) (head y)) (fast-subset (tail x) (tail y)))
        (t (fast-subset x (tail y))))
      :exec (cond ((null x) t)
        ((null y) nil)
        ((fast-lexorder (car x) (car y)) (and (equal (car x) (car y))
            (fast-subset (cdr x) (cdr y))))
        (t (fast-subset x (cdr y))))))
  (defun-inline subset
    (x y)
    (declare (xargs :guard (and (setp x) (setp y))
        :verify-guards nil))
    (mbe :logic (if (emptyp x)
        t
        (and (in (head x) y)
          (subset (tail x) y)))
      :exec (fast-subset x y)))
  (defthm subset-type
    (or (equal (subset x y) t)
      (equal (subset x y) nil))
    :rule-classes :type-prescription)
  (encapsulate nil
    (local (defthmd lemma
        (implies (not (in (head y) x))
          (equal (subset x y)
            (subset x (tail y))))))
    (local (defthm case-1
        (implies (and (not (emptyp x))
            (not (emptyp y))
            (not (<< (head x) (head y)))
            (not (equal (head x) (head y)))
            (implies (and (setp x) (setp (tail y)))
              (equal (fast-subset x (tail y))
                (subset x (tail y)))))
          (implies (and (setp x) (setp y))
            (equal (fast-subset x y)
              (subset x y))))
        :hints (("Goal" :in-theory (enable (:ruleset order-rules))
           :use (:instance lemma)))))
    (local (defthm case-2
        (implies (and (not (emptyp x))
            (not (emptyp y))
            (not (<< (head x) (head y)))
            (equal (head x) (head y))
            (implies (and (setp (tail x))
                (setp (tail y)))
              (equal (fast-subset (tail x) (tail y))
                (subset (tail x) (tail y)))))
          (implies (and (setp x) (setp y))
            (equal (fast-subset x y)
              (subset x y))))
        :hints (("Goal" :in-theory (enable (:ruleset order-rules))
           :use (:instance lemma (x (tail x)))))))
    (local (defthm fast-subset-equivalence
        (implies (and (setp x) (setp y))
          (equal (fast-subset x y)
            (subset x y)))
        :hints (("Goal" :in-theory (enable (:ruleset order-rules))
           :induct (fast-subset x y)))))
    (verify-guards subset$inline)))
other
(defsection all-by-membership
  :parents (std/osets)
  :short "A way to quantify over sets."
  :long "<p>@('all-by-membership') is a generic theorem that can be used to
prove that a property holds of a set by showing that a related property holds
of the set elements.</p>

<p>The most important role of @('all-by-membership') is to allow for
pick-a-point proofs of @(see subset).  That is, it allows us to show that
@('(subset X Y)') holds by showing that every element of X satisfies @('(in a
Y)').</p>

<p>More generally, we could show that a set satisfies a predicate like
@('integer-setp') because each of its elements satisfies @('integerp').</p>


<h3>Pick-a-Point Proofs in ACL2</h3>

<p>We begin by explaining how pick-a-point proofs of subset can be carried out.
In traditional mathematics, @(see subset) is defined using quantification over
members, e.g., as follows:</p>

@({
  (equal (subset X Y)
         (forall a (implies (in a X) (in a Y))))
})

<p>This definition is very useful for <b>pick-a-point</b> proofs that some set
@('X') is a subset of @('Y').  Such a proof begins by picking an arbitrary
point @('a') that is a member of @('X').  Then, if we can show that @('a') must
be a member of @('Y'), we have established @('(subset X Y)').</p>

<p>These kinds of arguments are extremely useful, and we would like to be able
to carry them out in ACL2 about osets.  But since ACL2 does not have explicit
quantifiers, we cannot even write a theorem like this:</p>

@({
 (implies (forall a (implies (in a X) (in a Y)))
          (subset X Y))
})

<p>But consider the contrapositive of this theorem:</p>

@({
 (implies (not (subset X Y))
          (exists a (and (in a X) (not (in a Y)))))
})

<p>We <i>can</i> prove something like this, by writing an explicit function to
search for an element of @('X') that is not an element of @('Y').  That is, we
can prove:</p>

@({
 (implies (not (subset X Y))
          (let ((a (find-witness X Y)))
            (and (in a X)
                 (not (in a Y)))))
})

<p>Once we prove the above, we still need to be able to "reduce" a proof of
@('(subset X Y)') to a proof of @('(implies (in a X) (in a Y))').  While we
can't do this with a direct rewrite rule, we can sort of fake it using
functional instantiation.  As groundwork:</p>

<ul>

<li>Using @(see encapsulate), we introduce functions @('sub') and @('super')
with the constraint that @({
 (implies (in a (sub))
          (in a (super)))
})</li>

<li>Using this constraint, we prove the generic theorem:
@({
 (subset (sub) (super))
})</li>

</ul>

<p>Then, when we want to prove @('(subset X Y)') for some particular @('X') and
@('Y'), we can functionally instantiate the generic theorem with</p>

@({
   sub   <-- (lambda () X)
   super <-- (lambda () Y)
})

<p>And this allows us to prove @('(subset X Y)') as long as we can relieve the
constraint, i.e., @('(implies (in a X) (in a Y))').</p>


<h3>Generalizing Pick-a-Point Proofs</h3>

<p>In earlier versions of the osets library, we used an explicit argument to
reduce subset proofs to pick-a-point style membership arguments.  But we later
generalized the approach to arbitrary predicates instead.</p>

<p>First, notice that if you let the predicate @('(P a)') be defined as @('(in
a Y)'), then @('(subset X Y)') is just</p>

@({
 (forall a (implies (in a X) (P a)))
})

<p>Our generalization basically lets you reduce a proof of @('(P-setp X)') to a
proof of @('(implies (in a X) (P a))'), for an arbitrary predicate @('P').
This can be used to prove subset by just chooisng @('P') as described above,
but it can also be used for many other ideas by just changing the meaning of
@('P').  For instance, if @('P') is @(see integerp), then we can show that
@('X') is an @('integer-setp') or similar.</p>

<p>The mechanism is just an adaptation of that described in the previous
section.</p>

<ul>

<li>We begin by introducing a completely arbitrary @('predicate').</li>

<li>Based on @('predicate'), we introduce a new function, @('all'), which
checks to see if every member in a set satisfies @('predicate').</li>

<li>We set up an encapsulate which allows us to assume that some hypotheses are
true and that any member of some set satisfies @('predicate').</li>

</ul>

<p>Finally, we prove @('all-by-membership'), which shows that under these
assumptions, the set satisfies @('all').  This theorem can be functionally
instantiated to reduce a proof of @('(all X)') to a proof of</p>

@({
  (implies (in a X) (P a))
})"
  (encapsulate (((predicate *) => *))
    (local (defun predicate (x) x)))
  (defun all
    (set-for-all-reduction)
    (declare (xargs :guard (setp set-for-all-reduction)))
    (if (emptyp set-for-all-reduction)
      t
      (and (predicate (head set-for-all-reduction))
        (all (tail set-for-all-reduction)))))
  (encapsulate (((all-hyps) => *) ((all-set) => *))
    (local (defun all-hyps nil nil))
    (local (defun all-set nil nil))
    (defthmd membership-constraint
      (implies (all-hyps)
        (implies (in arbitrary-element (all-set))
          (predicate arbitrary-element)))))
  (local (defun find-not
      (x)
      (declare (xargs :guard (setp x)))
      (cond ((emptyp x) nil)
        ((not (predicate (head x))) (head x))
        (t (find-not (tail x))))))
  (local (defthm lemma-find-not-is-a-witness
      (implies (not (all x))
        (and (in (find-not x) x)
          (not (predicate (find-not x)))))))
  (defthmd all-by-membership
    (implies (all-hyps) (all (all-set)))
    :hints (("Goal" :use (:instance membership-constraint
         (arbitrary-element (find-not (all-set))))))))
other
(defsection pick-a-point-subset-strategy
  :parents (std/osets)
  :short "Automatic pick-a-point proofs of @(see subset)."
  :long "<p>The rewrite rule @('pick-a-point-subset-strategy') tries to
automatically reduce proof goals such as:</p>

@({
 (implies hyps
          (subset X Y))
})

<p>To proofs of:</p>

@({
 (implies (and hyps (in a X))
          (in a Y))
})

<p>The mechanism for doing this is somewhat elaborate: the rewrite rule
replaces the @('(subset X Y)') with @('(subset-trigger X Y)').  This trigger is
recognized by a computed hint, which then suggests proving the theorem via
functional instantiation of @(see all-by-membership).</p>

<p>The pick-a-point method is often a good way to prove subset relations.  On
the other hand, this rule is very heavy-handed, and you may need to disable it
if you do not want to use the pick-a-point method to solve your goal.</p>"
  (defun subset-trigger
    (x y)
    (declare (xargs :guard (and (setp x) (setp y))))
    (subset x y))
  (defthm pick-a-point-subset-strategy
    (implies (and (syntaxp (rewriting-goal-lit mfc state))
        (syntaxp (rewriting-conc-lit `(subset$inline ,SET::X ,SET::Y)
            mfc
            state)))
      (equal (subset x y)
        (subset-trigger x y))))
  (in-theory (disable subset-trigger))
  (automate-instantiation :new-hint-name pick-a-point-subset-hint
    :generic-theorem all-by-membership
    :generic-predicate predicate
    :generic-hyps all-hyps
    :generic-collection all-set
    :generic-collection-predicate all
    :actual-collection-predicate subset
    :actual-trigger subset-trigger
    :predicate-rewrite (((predicate ?x ?y) (in ?x ?y)))
    :tagging-theorem pick-a-point-subset-strategy)
  (defthm pick-a-point-subset-constraint-helper
    (implies (syntaxp (equal set-for-all-reduction
          'set-for-all-reduction))
      (equal (subset set-for-all-reduction rhs)
        (cond ((emptyp set-for-all-reduction) t)
          ((in (head set-for-all-reduction) rhs) (subset (tail set-for-all-reduction)
              rhs))
          (t nil))))))
other
(defsection subset-theorems
  :extension subset
  (defthm subset-sfix-cancel-x
    (equal (subset (sfix x) y)
      (subset x y)))
  (defthm subset-sfix-cancel-y
    (equal (subset x (sfix y))
      (subset x y)))
  (defthm emptyp-subset
    (implies (emptyp x)
      (subset x y)))
  (defthm emptyp-subset-2
    (implies (emptyp y)
      (equal (subset x y) (emptyp x))))
  (defthm subset-in
    (and (implies (and (subset x y) (in a x))
        (in a y))
      (implies (and (in a x) (subset x y))
        (in a y))))
  (defthm subset-in-2
    (and (implies (and (subset x y)
          (not (in a y)))
        (not (in a x)))
      (implies (and (not (in a y))
          (subset x y))
        (not (in a x)))))
  (encapsulate nil
    (local (defthm l0
        (equal (subset (insert a nil) y)
          (in a y))
        :hints (("Goal" :in-theory (enable (:ruleset primitive-rules))))))
    (defthm subset-insert-x
      (equal (subset (insert a x) y)
        (and (subset x y) (in a y)))
      :hints (("Goal" :induct (insert a x)))))
  (defthm subset-reflexive
    (subset x x))
  (defthm subset-transitive
    (and (implies (and (subset x y)
          (subset y z))
        (subset x z))
      (implies (and (subset y z)
          (subset x y))
        (subset x z))))
  (defthm subset-membership-tail
    (implies (and (subset x y)
        (in a (tail x)))
      (in a (tail y)))
    :hints (("Goal" :in-theory (enable (:ruleset order-rules)))))
  (defthm subset-membership-tail-2
    (implies (and (subset x y)
        (not (in a (tail y))))
      (not (in a (tail x))))
    :hints (("Goal" :in-theory (disable subset-membership-tail)
       :use (:instance subset-membership-tail))))
  (defthm subset-insert
    (subset x (insert a x)))
  (defthm subset-tail
    (subset (tail x) x)
    :rule-classes ((:rewrite) (:forward-chaining :trigger-terms ((tail x))))))
other
(defsection double-containment
  :parents (std/osets)
  :short "A strategy for proving sets are equal because they are subsets
of one another."
  :long "<p>Double containment can be a good way to prove that two sets are
equal to one another.</p>

<p>Unfortunately, because this rule targets @('equal') it can get quite
expensive.  You may sometimes wish to disable it to speed up your proofs, as
directed by @(see accumulated-persistence).  In fact, this rule is disabled
when importing @('[books]/std/osets/top.lisp'). To reduce its cost when enabled,
this rule has a backchain limit.</p>

<p>On the other hand, there are cases in which this rule works well, and where
the backchain limit is detrimental.  For this reason, we also provide a version
of the rule that has no backchain limit.  This version can be enabled
as needed.</p>"
  (local (defthmd double-containment-lemma-head
      (implies (and (subset x y)
          (subset y x))
        (equal (head x) (head y)))
      :hints (("Goal" :in-theory (enable (:ruleset order-rules))))))
  (local (defthmd in-tail-expand
      (equal (in a (tail x))
        (and (in a x)
          (not (equal a (head x)))))))
  (local (defthmd double-containment-lemma-in-tail
      (implies (and (subset x y)
          (subset y x))
        (implies (in a (tail x))
          (in a (tail y))))
      :hints (("Goal" :in-theory (enable (:ruleset order-rules))
         :use ((:instance in-tail-expand
            (a a)
            (x x)) (:instance in-tail-expand
             (a a)
             (x y)))))))
  (local (defthmd double-containment-lemma-tail
      (implies (and (subset x y)
          (subset y x))
        (subset (tail x) (tail y)))
      :hints (("Goal" :in-theory (enable double-containment-lemma-in-tail)))))
  (local (defun double-tail-induction
      (x y)
      (declare (xargs :guard (and (setp x) (setp y))))
      (if (or (emptyp x) (emptyp y))
        (list x y)
        (double-tail-induction (tail x)
          (tail y)))))
  (local (defthm double-containment-is-equality-lemma
      (implies (and (not (or (emptyp x) (emptyp y)))
          (implies (and (subset (tail x) (tail y))
              (subset (tail y) (tail x)))
            (equal (equal (tail x) (tail y)) t))
          (setp x)
          (setp y)
          (subset x y)
          (subset y x))
        (equal (equal x y) t))
      :hints (("Goal" :in-theory (enable head-tail-same)
         :use ((:instance double-containment-lemma-tail
            (x x)
            (y y)) (:instance double-containment-lemma-tail
             (x y)
             (y x))
           (:instance double-containment-lemma-head
             (x x)
             (y y)))))))
  (local (defthmd double-containment-is-equality
      (implies (and (setp x)
          (setp y)
          (subset x y)
          (subset y x))
        (equal (equal x y) t))
      :hints (("Goal" :in-theory (enable head-tail-same)
         :induct (double-tail-induction x y)))))
  (defthm double-containment
    (implies (and (setp x) (setp y))
      (equal (equal x y)
        (and (subset x y)
          (subset y x))))
    :rule-classes ((:rewrite :backchain-limit-lst 1))
    :hints (("Goal" :use (:instance double-containment-is-equality))))
  (defthmd double-containment-no-backchain-limit
    (implies (and (setp x) (setp y))
      (equal (equal x y)
        (and (subset x y)
          (subset y x))))
    :hints (("Goal" :by double-containment))))
other
(in-theory (disable head-minimal head-minimal-2))
other
(encapsulate nil
  (local (in-theory (enable (:ruleset primitive-rules)
        (:ruleset order-rules))))
  (defthm setp-of-cons
    (equal (setp (cons a x))
      (and (setp x)
        (or (<< a (head x))
          (emptyp x)))))
  (defthm in-to-member
    (implies (setp x)
      (equal (in a x)
        (if (member a x)
          t
          nil))))
  (defthm not-member-when-smaller
    (implies (and (<< a (car x)) (setp x))
      (not (member a x))))
  (defthm subset-to-subsetp
    (implies (and (setp x) (setp y))
      (equal (subset x y) (subsetp x y))))
  (defthm lexorder-<<-equiv
    (implies (not (equal a b))
      (equal (equal (<< a b)
          (lexorder a b))
        t))
    :hints (("Goal" :in-theory (enable <<)))))
other
(def-ruleset low-level-rules
  '(setp-of-cons in-to-member
    not-member-when-smaller
    subset-to-subsetp
    lexorder-<<-equiv
    (:ruleset primitive-rules)
    (:ruleset order-rules)))
other
(in-theory (disable (:ruleset low-level-rules)))
fast-measurefunction
(defun fast-measure
  (x y)
  (+ (acl2-count x) (acl2-count y)))