Lisp Plagiarism Check
(defun word-char-p (ch)
  (or (alphanumericp ch) (char= ch #\_)))

(defun word-tokens (s)
  (let ((tokens '())
        (n (length s))
        (i 0))
    (loop while (< i n)
          do (if (word-char-p (char s i))
                 (let ((start i))
                   (loop while (and (< i n) (word-char-p (char s i)))
                         do (incf i))
                   (push (subseq s start i) tokens))
                 (incf i)))
    (nreverse tokens)))

(defun numeric-token-p (tok)
  (and (> (length tok) 0) (every #'digit-char-p tok)))

(defun boundary-replace (str token replacement)
  (let ((out (make-string-output-stream))
        (tlen (length token))
        (n (length str))
        (i 0))
    (loop while (< i n)
          do (let ((pos (search token str :start2 i)))
               (cond
                 ((null pos)
                  (write-string str out :start i :end n)
                  (setf i n))
                 ((and (> pos 0) (not (word-char-p (char str (1- pos)))))
                  (write-string str out :start i :end pos)
                  (write-string replacement out)
                  (setf i (+ pos tlen)))
                 (t
                  (write-string str out :start i :end (1+ pos))
                  (setf i (1+ pos))))))
    (get-output-stream-string out)))

(defun plagiarism-check (code1 code2)
  (let ((c1 (format nil "~{~A~^ ~}" code1))
        (c2 (format nil "~{~A~^ ~}" code2)))
    (if (string= c1 c2)
        nil
        (let ((d1 (word-tokens c1))
              (d2 (word-tokens c2))
              (r-cand (make-hash-table :test 'equal))
              (r-order '()))
          (loop for v in d1
                for w in d2
                do (when (and (not (string= v w)) (not (numeric-token-p v)))
                     (unless (nth-value 1 (gethash v r-cand))
                       (push v r-order))
                     (setf (gethash v r-cand) w)))
          (setf r-order (nreverse r-order))
          (dolist (orig r-order)
            (setf c1 (boundary-replace c1 orig (concatenate 'string "PLACEHOLDER" orig))))
          (dolist (orig r-order)
            (setf c1 (boundary-replace c1 (concatenate 'string "PLACEHOLDER" orig) (gethash orig r-cand))))
          (string= c1 c2)))))

This flattens both snippets, tries consistent identifier replacements, and checks whether the rewritten code matches.