;;; -*- Mode:LISP; Package:SYSTEM-INTERNALS; Readtable:CL -*-

(defun myfind-if (predicate sequence &key from-end (start 0) end key)
  "Return the first element of SEQUENCE that satisfies PREDICATE.
Value is NIL if no element satisfies PREDICATE.  SEQUENCE can be a list or an array.
KEY, if non-NIL, is a function to be applied to each element to get a key,
 which is passed to PREDICATE.  If KEY is NIL, the element itself is used.
START and END are indices restricting substitution to a subsequence of SEQUENCE.
 They default to 0 and NIL (which means the end of SEQUENCE).
 Elements outside that range are not tested.
If FROM-END is non-NIL, the value is the LAST element in SEQUENCE
 (or specified subsequence) that satisfies the predicate."
  (check-type sequence sequence)
  (values (myfind-1 nil sequence start end nil nil key from-end predicate)))

(defun myfind-if-not (predicate sequence &key from-end (start 0) end key)
  "Like MYFIND-IF but looks for an element which does NOT satisfy PREDICATE."
  (check-type sequence sequence)
  (values (myfind-1 nil sequence start end nil t key from-end predicate)))

(defun myfind (item sequence &key from-end test test-not (start 0) end key)
  "Return first element of SEQUENCE that matches ITEM.
Value is NIL if no element matches.  SEQUENCE can be a list or an array.
TEST is a function of two args to use to compare ITEM against an element (or key).
 An element matches when the TEST function returns non-NIL.
 Alternatively, specify as TEST-NOT a function to use which returns NIL if there is a match.
KEY, if non-NIL, is a function to be applied to each element to get a key,
 which is passed to TEST or TEST-NOT.  If KEY is NIL, the element itself is used.
START and END are indices restricting substitution to a subsequence of SEQUENCE.
 They default to 0 and NIL (which means the end of SEQUENCE).
 Elements outside that range are not tested.
If FROM-END is non-NIL, the value is the LAST element in SEQUENCE
 (or specified subsequence) that matches."
  (check-type sequence sequence)
  (values (myfind-1 item sequence start end (or test-not test) (not (null test-not))
		  key from-end nil)))

(defun myfind-1 (item sequence &optional (start 0) end
		 test invertp key from-end one-arg-predicate
		 &aux reslt)
  (declare (values item index))
  (format t "~&invertp is ~s" invertp)
  (if (and from-end (arrayp sequence))
      (do ((index (1- (or end (length sequence))) (1- index))
	   elt)
	  ((< index start)
	   nil)
	(setq elt (if key (funcall key (cl:aref sequence index))
		    (cl:aref sequence index)))
	(when (eq invertp (not (cond (one-arg-predicate
				      (funcall one-arg-predicate elt))
				     (test
				      (funcall test item elt))
				     (t
				      (eql item elt)))))
	  (return (values (cl:aref sequence index) index))))
    (do ((index (seq-start sequence start))
	 (i start (1+ i))
	 (stop-index (if (consp end) end (seq-end sequence end)))
	 last-pos elt)
	((eq index stop-index)
	 (if last-pos (values elt last-pos)))
      (setq elt (key-fetch-inc key sequence index))
      (setq reslt
	    (cond (one-arg-predicate
		   (funcall one-arg-predicate elt))
		  (test
		   (funcall test elt item))
		  (t
		   (eql item elt))))
      (format t "~&~s vs. ~s is ~s" item elt reslt)
      (when (eq (not invertp) reslt)
	(format t "~&comparison succeeded ~s ~s" item elt)
	(if from-end
	    (setq last-pos i)
	  (return (values (elt sequence i) i))))
      (format t "~&comparison failed ~s ~s" item elt))
    ))
