Haskell Plagiarism Check
import Data.Char (isAlphaNum, isDigit)
import qualified Data.Map.Strict as Map
data Token = Word String | Sep String
tokenize :: String -> [Token]
tokenize [] = []
tokenize s@(c : _)
| isWordChar c = let (w, rest) = span isWordChar s in Word w : tokenize rest
| otherwise = let (sp, rest) = break isWordChar s in Sep sp : tokenize rest
where
isWordChar ch = isAlphaNum ch || ch == '_'
plagiarismCheck :: [String] -> [String] -> Bool
plagiarismCheck code1 code2
| c1 == c2 = False
| length words1 /= length words2 = False
| otherwise = reconstructed == c2
where
c1 = unwords code1
c2 = unwords code2
toks1 = tokenize c1
words1 = [w | Word w <- toks1]
words2 = [w | Word w <- tokenize c2]
isNumeric w = not (null w) && all isDigit w
-- later mismatches overwrite earlier ones for the same original token,
-- mirroring the source's plain array assignment
rCand =
Map.fromList
[(w1, w2) | (w1, w2) <- zip words1 words2, w1 /= w2, not (isNumeric w1)]
substitute (Word w) = Map.findWithDefault w w rCand
substitute (Sep s) = s
reconstructed = concatMap substitute toks1
This flattens both snippets, tries consistent identifier replacements, and checks whether the rewritten code matches.