In today’s Programming Praxis exercise our goal is to write to functions related to the Luhn credit card number verification algorithm: one to check if a number is valid and one to make a valid number from a given number by adding one digit. Let’s get started, shall we?
import Data.Char import Data.List.HT import Test.QuickCheck
For both functions we need to calculate the Luhn sum of a number, so let’s start with that. This one simply follows the description given in the exercise text. You could probably speed it up a bit by replacing the multiplication with a lookup table.
luhnSum :: Integral a => a -> a luhnSum n = digitSum a + digitSum (show . (* 2) . digitToInt =<< b) where [a,b] = sliceHorizontal 2 . reverse $ show n digitSum = sum . map (fromIntegral . digitToInt)
Checking if a number is valid is trivial: just see if the Luhn sum is zero.
isValid :: Integral a => a -> Bool isValid n = mod (luhnSum n) 10 == 0
Making a valid number means adding the complement of the Luhn sum of 10 times the number (to account for the digit that will be added on the end) to the end of the number. The outer modulo is to account for the case where the Luhn sum is zero.
makeValid :: Integral a => a -> a makeValid n = 10*n + mod (10 - mod (luhnSum $ 10*n) 10) 10
Some testing to see if everthing is working properly:
prop_luhn :: Integer -> Property prop_luhn n = n >= 0 ==> isValid (makeValid n) main :: IO () main = do print $ isValid 0 print $ isValid 34 print $ isValid 117 print $ isValid 49927398716 print . not $ isValid 49927398715 print $ makeValid 4992739871 == 49927398716 quickCheck prop_luhn
Yup. Have fun committing credit card fraud