Filtering...

ieqv

books/std/strings/ieqv
other
(in-package "STR")
other
(include-book "char-case")
other
(include-book "std/lists/list-defuns" :dir :system)
other
(local (include-book "std/lists/nthcdr" :dir :system))
other
(local (include-book "arithmetic"))
other
(define ichareqv
  ((x :type character) (y :type character))
  :returns (bool)
  :parents (equivalences cases)
  :short "Case-insensitive character equivalence test."
  :long "<p>@(call ichareqv) determines if @('x') and @('y') are equivalent
when interpreted as characters without regard to case.  For instance,
upper-case C is equivalent to lower-case c under this relation.</p>

<p>ACL2 has a built-in version of this function, @(see char-equal), but it is
irritating to use because it has @(see standard-char-p) guards.  In contrast,
@('ichareqv') works on arbitrary characters, with some loss of efficiency.</p>"
  :inline t
  (eql (downcase-char x)
    (downcase-char y))
  ///
  (defequiv ichareqv)
  (defrefinement chareqv ichareqv)
  (encapsulate nil
    (local (defthmd l1
        (implies (equal (upcase-char x) (upcase-char y))
          (equal (downcase-char x)
            (downcase-char y)))
        :hints (("Goal" :in-theory (disable downcase-char-of-upcase-char)
           :use ((:instance downcase-char-of-upcase-char
              (x x)) (:instance downcase-char-of-upcase-char
               (x y)))))))
    (local (defthmd l2
        (implies (equal (downcase-char x)
            (downcase-char y))
          (equal (upcase-char x) (upcase-char y)))
        :hints (("Goal" :in-theory (disable upcase-char-of-downcase-char)
           :use ((:instance upcase-char-of-downcase-char
              (x x)) (:instance upcase-char-of-downcase-char
               (x y)))))))
    (defthm equal-of-upcase-char-and-upcase-char
      (equal (equal (upcase-char x) (upcase-char y))
        (ichareqv x y))
      :hints (("Goal" :use ((:instance l1) (:instance l2))))))
  (defcong ichareqv
    equal
    (downcase-char x)
    1)
  (defcong ichareqv
    equal
    (upcase-char x)
    1)
  (defcong ichareqv
    equal
    (upcase-char-str x)
    1)
  (defcong ichareqv
    equal
    (downcase-char-str x)
    1))
other
(define icharlisteqv
  ((x character-listp) (y character-listp))
  :returns (bool)
  :parents (equivalences cases)
  :short "Case-insensitive character-list equivalence test."
  :long "<p>@(call icharlisteqv) determines if @('x') and @('y') are
case-insensitively equivalent character lists.  That is, @('x') and @('y') must
have the same length and their elements must be @(see ichareqv) to one
another.</p>

<p>See also @(see charlisteqv) for a case-sensitive alternative.</p>"
  (if (consp x)
    (and (consp y)
      (ichareqv (car x) (car y))
      (icharlisteqv (cdr x) (cdr y)))
    (atom y))
  ///
  (defequiv icharlisteqv)
  (defrefinement charlisteqv
    icharlisteqv
    :hints (("Goal" :in-theory (enable chareqv))))
  (defcong icharlisteqv
    ichareqv
    (car x)
    1)
  (defcong icharlisteqv
    icharlisteqv
    (cdr x)
    1)
  (defcong ichareqv
    icharlisteqv
    (cons a x)
    1)
  (defcong icharlisteqv
    icharlisteqv
    (cons a x)
    2)
  (defcong icharlisteqv equal (len x) 1)
  (defcong icharlisteqv
    icharlisteqv
    (list-fix x)
    1)
  (defcong icharlisteqv
    ichareqv
    (nth n x)
    2)
  (defcong icharlisteqv
    icharlisteqv
    (nthcdr n x)
    2)
  (defcong icharlisteqv
    icharlisteqv
    (take n x)
    2)
  (defcong icharlisteqv
    icharlisteqv
    (append x y)
    1)
  (defcong icharlisteqv
    icharlisteqv
    (append x y)
    2)
  (defcong icharlisteqv
    icharlisteqv
    (rev x)
    1)
  (defcong icharlisteqv
    icharlisteqv
    (revappend x y)
    2)
  (defcong icharlisteqv
    icharlisteqv
    (revappend x y)
    1)
  (defcong icharlisteqv
    icharlisteqv
    (make-character-list x)
    1)
  (defthm icharlisteqv-when-not-consp-left
    (implies (not (consp x))
      (equal (icharlisteqv x y) (atom y))))
  (defthm icharlisteqv-when-not-consp-right
    (implies (not (consp y))
      (equal (icharlisteqv x y) (atom x))))
  (defthm icharlisteqv-of-cons-right
    (equal (icharlisteqv x (cons a y))
      (and (consp x)
        (ichareqv (car x) (double-rewrite a))
        (icharlisteqv (cdr x)
          (double-rewrite y)))))
  (defthm icharlisteqv-of-cons-left
    (equal (icharlisteqv (cons a x) y)
      (and (consp y)
        (ichareqv (double-rewrite a) (car y))
        (icharlisteqv (double-rewrite x)
          (cdr y)))))
  (defthm icharlisteqv-when-not-same-lens
    (implies (not (equal (len x) (len y)))
      (not (icharlisteqv x y)))))
other
(define istreqv
  ((x :type string) (y :type string))
  :returns bool
  :parents (equivalences cases)
  :short "Case-insensitive string equivalence test."
  :long "<p>@(call istreqv) determines if @('x') and @('y') are
case-insensitively equivalent strings.  That is, @('x') and @('y') must have
the same length and their elements must be @(see ichareqv) to one another.</p>

<p>Logically this is identical to</p>

@({
 (icharlisteqv (explode x) (explode y))
})

<p>But we use a more efficient implementation which avoids coercing the
strings into lists.</p>

<p>NOTE: for reasoning, we leave this function enabled and prefer to work with
@(see icharlisteqv) of the coerces as our normal form.</p>"
  :inline t
  :enabled t
  (mbe :logic (icharlisteqv (explode x)
      (explode y))
    :exec (b* (((the (integer 0 *) xl) (length x)) ((the (integer 0 *) yl) (length y)))
      (and (eql xl yl)
        (istreqv-aux x y 0 xl))))
  :prepwork ((defund istreqv-aux
     (x y n l)
     (declare (type string x)
       (type string y)
       (type (integer 0 *) n)
       (type (integer 0 *) l)
       (xargs :guard (and (natp n)
           (natp l)
           (equal (length x) l)
           (equal (length y) l)
           (<= n l))
         :measure (nfix (- (nfix l) (nfix n)))
         :guard-hints (("Goal" :in-theory (enable ichareqv)))))
     (mbe :logic (if (zp (- (nfix l) (nfix n)))
         t
         (and (ichareqv (char x n) (char y n))
           (istreqv-aux x
             y
             (+ (nfix n) 1)
             l)))
       :exec (if (eql n l)
         t
         (and (ichareqv (the character (char x n))
             (the character (char y n)))
           (istreqv-aux x
             y
             (the (integer 0 *) (+ 1 n))
             l))))) (local (defthm lemma
        (implies (and (< n (len x))
            (not (ichareqv (nth n x) (nth n y))))
          (not (icharlisteqv (nthcdr n x)
              (nthcdr n y))))))
    (local (defthm lemma2
        (implies (and (< n (len x))
            (equal (len x) (len y))
            (ichareqv (nth n x) (nth n y)))
          (equal (icharlisteqv (nthcdr n x)
              (nthcdr n y))
            (icharlisteqv (cdr (nthcdr n x))
              (cdr (nthcdr n y)))))))
    (defthm istreqv-aux-removal
      (implies (and (stringp x)
          (stringp y)
          (natp n)
          (<= n l)
          (= l (length x))
          (= l (length y)))
        (equal (istreqv-aux x y n l)
          (icharlisteqv (nthcdr n (explode x))
            (nthcdr n (explode y)))))
      :hints (("Goal" :in-theory (enable istreqv-aux)
         :induct (istreqv-aux x y n l)))))
  ///
  (defequiv istreqv)
  (defrefinement streqv istreqv)
  (defcong istreqv
    ichareqv
    (char x n)
    1)
  (defcong istreqv
    icharlisteqv
    (explode x)
    1)
  (defcong istreqv
    istreqv
    (string-append x y)
    1)
  (defcong istreqv
    istreqv
    (string-append x y)
    2))