Hs2lib-0.4.8: Tests/Src/Test_Validation.hs
{-# LINE 24 "Validation.lhs" #-}
{-# OPTIONS -Wall -fno-warn-type-defaults #-}
module Tests.Src.Test_Validation where
import Data.Char
import Data.List
{-# LINE 148 "Validation.lhs" #-}
-- @@ Export
toDigitsRev :: Integer -> [Integer]
{-# LINE 154 "Validation.lhs" #-}
-- @@ Export
toDigits :: Integer -> [Integer]
{-# LINE 158 "Validation.lhs" #-}
-- @@ Export
toDigitsRev = reverse . toDigits
{-# LINE 176 "Validation.lhs" #-}
-- @@ Export
doubleSecond :: [Integer] -> [Integer]
{-# LINE 192 "Validation.lhs" #-}
-- @@ Export
sumDigits :: [Integer] -> Integer
{-# LINE 207 "Validation.lhs" #-}
-- @@ Export
validate :: Integer -> Bool
{-# LINE 237 "Validation.lhs" #-}
-- @@ Export
readCC :: String -> Integer
{-# LINE 254 "Validation.lhs" #-}
-- @@ Export
showCC :: Integer -> String
{-# LINE 291 "Validation.lhs" #-}
-- @@ Export
lookupIssuer :: String -> Integer -> IO String
{-# LINE 328 "Validation.lhs" #-}
-- @@ Export
checkCC :: String -> IO ()
{-# LINE 370 "Validation.lhs" #-}
-- @@ Export
toDigitsRevG :: Integer -> Integer -> [Integer]
{-# LINE 396 "Validation.lhs" #-}
toDigitsRev' :: Integer -> [Integer]
toDigitsRev' 0 = [0]
toDigitsRev' num = go (abs num)
where
go 0 = []
go n = r : go q
where (q, r) = quotRem n 10
toDigits = reverse . toDigitsRev'
-- Tricky cases:
-- * base too small
-- * num == minBound
toDigitsRevG base num
| base < 2 = []
| abs num >= 0 && abs num < base = abs num : []
| num < 0 = let (q, r) = quotRem num base in negate r : go (negate q)
| otherwise = let (q, r) = quotRem num base in r : go q
where
go n
| n < base = n : []
| otherwise = let (q, r) = quotRem n base in r : go q
toDigitsRevG' :: (Integral a) => a -> a -> [a]
toDigitsRevG' base num
| base < 2 = []
| num == 0 = [0]
| otherwise = map fromInteger . go . abs $ toInteger num
where
go 0 = []
go n = r : go q
where (q, r) = quotRem n (toInteger base)
doubleSecond = go1
where
d x = x + x
go1 [] = []
go1 (x:xs) = x : go2 xs
go2 [] = []
go2 (x:xs) = d x : go1 xs
sumDigits = sum . concatMap toDigitsRev
validate n = s `mod` 10 == 0
where
d1 = toDigitsRev n
d2 = doubleSecond d1
s = sumDigits d2
ds :: String -> String
ds = dropWhile isSpace
readCC s = read s3 * (10 ^ 12) + read s2 * 10 ^ 8 + read s1 * 10 ^ 4 + read s0
where
(s3, s210) = splitAt 4 (ds s)
(s2, s10) = splitAt 4 (ds s210)
(s1, s0r) = splitAt 4 (ds s10)
s0 = ds s0r
readCC' :: String -> Integer
readCC' s = n3 * (10 ^ 12) + n2 * 10 ^ 8 + n1 * 10 ^ 4 + n0
where
(n3, s210):_ = reads (ds s)
(n2, s10):_ = reads (ds s210)
(n1, s0r):_ = reads (ds s10)
(n0, _):_ = reads (ds s0r)
showCC n = s3 ++ " " ++ s2 ++ " " ++ s1 ++ " " ++ s0
where
ns = show n
zs = replicate (16 - length ns) '0'
s = zs ++ ns
(s3, s210) = splitAt 4 s
(s2, s10) = splitAt 4 s210
(s1, s0) = splitAt 4 s10
lookupIssuer file num = do
-- Read the file in as String
text <- readFile file
-- Transform string lines into triples
let entries = toEntries text
-- Account for longer prefixes that override shorter ones by doing a
-- reverse lexicographical sort on the prefix digits.
let sorted = reverse $ sort $ entries
-- Get the possible issuer by searching the sorted entries until we arrive at
-- one that matches the prefix of the number and matches the length.
let issuer = toIssuer sorted num
-- Account for unknown issuer
case issuer of
[] -> return "Unknown"
name:_ -> return name
-- Format entries as [(<prefix digits>, <length>, <issuer>)]
toEntries :: String -> [([Integer], Int, String)]
toEntries text = do
ln <- lines text
(prefix, rest) <- reads ln
let prefixDigits = toDigits prefix
(expectedLength, _:issuer) <- reads rest
return (prefixDigits, expectedLength, issuer)
toIssuer :: [([Integer], Int, String)] -> Integer -> [String]
toIssuer entries num = do
(prefixDigits, expectedLength, issuer) <- entries
if prefixMatch prefixDigits numDigits && expectedLength == numLength
then return issuer
else []
where
prefixMatch xs ys = and $ zipWith (==) xs ys
numDigits = toDigits num
numLength = length numDigits
checkCC file = do
putStr "Enter credit card number: "
numText <- getLine
let numIn = readCC numText
issuer <- lookupIssuer file numIn
let s1 = "The number " ++ showCC numIn ++ " is "
let s2 = if validate numIn
then ("valid and the type is " ++ issuer ++ ".")
else ("not a valid credit card number.")
putStrLn (s1 ++ s2)
checkCC file