- | Compute a "full" soundex code; i.e., do not drop any
-- encodable characters from the result.  The leading
-- character of the code will be folded to uppercase.
-- Non-alphabetics are not encoded. If no alphabetics are
-- present, the soundex code will be "0".
--
-- The two commonly encountered forms of soundex are Simplified
-- and another known as American, Miracode, NARA or Knuth.  This
-- code will calculate either---passing True gets NARA, and False
-- gets Simplified.
soundex :: Bool -> String -> String
soundex nara = filter (/= '?')
             . encode
             . map toUpper
             . dropWhile (not . isAlpha)
    where
      narify
          | nara = filter (/= '7')
          | otherwise = map (subst '7' '?')
      filter_multiples = map head . group
      --- The second clause of encode originally had a bug
      --- correctly predicted by STTS (ref above)!
      encode "" = "0"
      encode as@(a : _) = (a :)
                        . drop 1
                        . filter_multiples
                        . narify
                        . map unsound $ as
      unsound c | c >= 'A' && c <= 'Z' = soundexCodes ! c
      unsound _ = '?'

soundex :: Bool -> String -> String
soundex nara = filter (/= '?')
             . encode
             . map toUpper
             . dropWhile (not . isAlpha)
    where
      narify
          | nara = filter (/= '7')
          | otherwise = map (subst '7' '?')
      filter_multiples = map head . group
      --- The second clause of encode originally had a bug
      --- correctly predicted by STTS (ref above)!
      encode "" = "0"
      encode as@(a : _) = (a :)
                        . drop 1
                        . filter_multiples
                        . narify
                        . map unsound $ as
      unsound c | c >= 'A' && c <= 'Z' = soundexCodes ! c
      unsound _ = '?'
soundex nara = filter (/= '?')
             . encode
             . map toUpper
             . dropWhile (not . isAlpha)
    where
      narify
          | nara = filter (/= '7')
          | otherwise = map (subst '7' '?')
      filter_multiples = map head . group
      --- The second clause of encode originally had a bug
      --- correctly predicted by STTS (ref above)!
      encode "" = "0"
      encode as@(a : _) = (a :)
                        . drop 1
                        . filter_multiples
                        . narify
                        . map unsound $ as
      unsound c | c >= 'A' && c <= 'Z' = soundexCodes ! c
      unsound _ = '?'