Filtering...

strrpos

books/std/strings/strrpos
other
(in-package "STR")
other
(include-book "strprefixp")
other
(include-book "std/basic/defs" :dir :system)
other
(local (include-book "arithmetic"))
other
(defsection strrpos-fast
  :parents (strrpos)
  :short "Fast implementation of @(see strrpos)."
  (defund strrpos-fast
    (x y n xl yl)
    (declare (type string x y)
      (type (integer 0 *) n xl yl)
      (xargs :guard (and (stringp x)
          (stringp y)
          (natp xl)
          (natp yl)
          (natp n)
          (<= n (length y))
          (= xl (length x))
          (= yl (length y)))
        :measure (nfix n)))
    (cond ((mbe :logic (prefixp (explode x)
           (nthcdr n (explode y)))
         :exec (strprefixp-impl (the string x)
           (the string y)
           (the integer 0)
           (the (integer 0 *) n)
           (the (integer 0 *) xl)
           (the (integer 0 *) yl))) (lnfix n))
      ((zp n) nil)
      (t (strrpos-fast (the string x)
          (the string y)
          (the (integer 0 *) (+ -1 (lnfix n)))
          (the (integer 0 *) xl)
          (the (integer 0 *) yl)))))
  (local (in-theory (enable strrpos-fast)))
  (defthm strrpos-fast-type
    (or (and (integerp (strrpos-fast x y n xl yl))
        (<= 0
          (strrpos-fast x y n xl yl)))
      (not (strrpos-fast x y n xl yl)))
    :rule-classes :type-prescription)
  (defthm strrpos-fast-upper-bound
    (implies (force (natp n))
      (<= (strrpos-fast x y n xl yl)
        n))
    :rule-classes :linear)
  (defthm strrpos-fast-when-empty
    (implies (and (not (consp (explode x)))
        (equal xl (length x))
        (equal yl (length y))
        (natp n))
      (equal (strrpos-fast x y n xl yl)
        n))))
other
(defsection strrpos
  :parents (substrings)
  :short "Locate the last occurrence of a substring."
  :long "<p>@(call strrpos) searches through the string @('y') for the last
occurrence of the substring @('x').  If @('x') occurs somewhere in @('y'), it
returns the starting index of the last occurrence.  Otherwise, it returns
@('nil') to indicate that @('x') never occurs in @('y').</p>

<p>The function is "efficient" in the sense that it does not coerce its
arguments into lists, but rather traverses both strings with @(see char).  On
the other hand, it is a naive string search which operates by repeatedly
calling @(see strprefixp), rather than some better algorithm.</p>

<p>Corner case: we say that the empty string <b>is</b> an prefix of any other
string.  As a consequence, @('(strrpos "" x)') is (length x) for all
@('x').</p>"
  (definlined strrpos
    (x y)
    (declare (type string x y))
    (let ((x (mbe :logic (str-fix x) :exec x)) (y (mbe :logic (str-fix y) :exec y))
        (yl (length (the string y))))
      (declare (type string x y)
        (type (integer 0 *) yl))
      (strrpos-fast (the string x)
        (the string y)
        (the (integer 0 *) yl)
        (the (integer 0 *) (length (the string x)))
        (the (integer 0 *) yl))))
  (local (in-theory (enable strrpos strrpos-fast)))
  (defthm strrpos-type
    (or (and (integerp (strrpos x y))
        (<= 0 (strrpos x y)))
      (not (strrpos x y)))
    :rule-classes :type-prescription)
  (local (in-theory (enable str-fix)))
  (encapsulate nil
    (local (defthm lemma
        (implies (and (stringp x)
            (stringp y)
            (natp xl)
            (natp yl)
            (natp n)
            (<= n (length y))
            (= xl (length x))
            (= yl (length y))
            (strrpos-fast x y n xl yl))
          (prefixp (explode x)
            (nthcdr (strrpos-fast x y n xl yl)
              (explode y))))
        :hints (("Goal" :induct (strrpos-fast x y n xl yl)))))
    (defthm prefixp-of-strrpos
      (implies (strrpos x y)
        (prefixp (explode x)
          (nthcdr (strrpos x y) (explode y))))))
  (encapsulate nil
    (local (defun my-induction
        (x y n m xl yl)
        (declare (xargs :measure (nfix n)))
        (cond ((prefixp (explode x)
             (nthcdr n (explode y))) nil)
          ((zp n) (list x y n m xl yl))
          (t (my-induction x
              y
              (- (nfix n) 1)
              (if (= (nfix n) (nfix m))
                (- (nfix m) 1)
                m)
              xl
              yl)))))
    (local (defthm lemma
        (implies (and (stringp x)
            (stringp y)
            (natp xl)
            (natp yl)
            (natp n)
            (natp m)
            (>= n m)
            (<= n (length y))
            (= xl (length x))
            (= yl (length y))
            (prefixp (explode x)
              (nthcdr m (explode y))))
          (and (natp (strrpos-fast x y n xl yl))
            (>= (strrpos-fast x y n xl yl)
              m)))
        :hints (("Goal" :induct (my-induction x
             y
             n
             m
             xl
             yl)
           :do-not '(generalize fertilize)))))
    (defthm completeness-of-strrpos
      (implies (and (prefixp (explode x)
            (nthcdr m (explode y)))
          (<= m (len y))
          (force (natp m)))
        (and (natp (strrpos x y))
          (>= (strrpos x y) m)))))
  (defthm strrpos-upper-bound-weak
    (implies (force (stringp y))
      (<= (strrpos x y)
        (len (explode y))))
    :rule-classes ((:rewrite) (:linear)))
  (encapsulate nil
    (local (defthm lemma
        (implies (and (stringp x)
            (stringp y)
            (posp xl)
            (posp yl)
            (natp n)
            (<= n (length y))
            (= xl (length x))
            (= yl (length y)))
          (< (strrpos-fast x y n xl yl)
            yl))
        :hints (("Goal" :induct (strrpos-fast x y n xl yl)))))
    (defthm strrpos-upper-bound-strong
      (implies (and (not (equal y ""))
          (not (equal x ""))
          (force (stringp x))
          (force (stringp y)))
        (< (strrpos x y)
          (len (explode y))))
      :rule-classes ((:rewrite) (:linear))))
  (encapsulate nil
    (local (defthm lens-same-when-list-equiv
        (implies (list-equiv x y)
          (equal (len x) (len y)))
        :rule-classes :forward-chaining))
    (local (defthm len-of-nthcdr
        (equal (len (nthcdr n x))
          (if (< (nfix n) (len x))
            (- (len x) (nfix n))
            0))))
    (local (defthm len-bounded-by-prefixp
        (implies (prefixp x y)
          (<= (len x) (len y)))
        :rule-classes ((:linear) (:forward-chaining))))
    (local (defthm prefixp-of-self-and-cdr
        (equal (prefixp x (cdr x)) (atom x))))
    (local (defthm prefixp-of-nthcdr-bounds-n
        (implies (and (prefixp x (nthcdr n y))
            (case-split (consp x)))
          (<= (nfix n)
            (- (len y) (len x))))
        :rule-classes ((:rewrite) (:linear))
        :hints (("Goal" :in-theory (enable prefixp nthcdr)))))
    (local (defthm lemma1
        (implies (and (strrpos-fast x y n xl yl)
            (stringp x)
            (stringp y)
            (natp n)
            (= xl (length x))
            (= yl (length y))
            (<= n (length y)))
          (<= (strrpos-fast x y n xl yl)
            (- yl xl)))
        :rule-classes ((:rewrite) (:linear))))
    (defthm strrpos-upper-bound-stronger
      (implies (and (strrpos x y)
          (force (stringp x))
          (force (stringp y)))
        (<= (strrpos x y)
          (- (len (explode y))
            (len (explode x)))))
      :rule-classes ((:rewrite) (:linear)))))