1- -- {-# LANGUAGE ScopedTypeVariables #-}
2-
31module Luhn where
42
53import Data.Char (digitToInt )
64import Control.Arrow ((>>>) )
5+ import Data.Natural (Natural )
76
8- {- -
7+ {- - From Rosetta Code:
98Reverse the order of the digits in the number.
109Take the first, third, ... and every other odd digit in the reversed digits and sum them to form the partial sum s1
1110Taking the second, fourth ... and every other even digit in the reversed digits:
@@ -40,22 +39,33 @@ use it to validate the following numbers:
4039 1234567812345670
4140--}
4241
42+ divisibleBy10 :: Int -> Bool
43+ divisibleBy10 = (0 == ) . (`mod` 10 )
4344
45+ sumDigits :: [Int ] -> Int
46+ sumDigits = sum . map (uncurry (+) . (`divMod` 10 )) -- map (uncurry (+) . (`divMod` 10)) [6,2,7,16,9,6,7,4,9,18,4] -> [6,2,7,7,9,6,7,4,9,9,4]
4447
45- {- -
46- luhn :: String -> Bool
47- luhn = (0 ==) . (`mod` 10) . sum . map (uncurry (+) . (`divMod` 10)) . zipWith (*) (cycle [1,2]) . map digitToInt . reverse
48- ---}
48+ double2nd :: [Int ] -> [Int ]
49+ double2nd = zipWith (*) (cycle [1 ,2 ]) -- zipWith (*) [6,1,7,8,9,3,7,2,9,9,4] [1,2,1,2,1,2,1,2,1,2,1] -> [6,2,7,16,9,6,7,4,9,18,4]
4950
50- luhn :: String -> Bool
51- luhn =
52- reverse >>> map digitToInt >>>
53- zipWith (*) (cycle [1 ,2 ]) >>>
54- map (uncurry (+) . (`divMod` 10 )) >>>
55- sum >>>
56- (`mod` 10 ) >>>
57- (0 == )
51+ toDigits :: Natural -> [Int ]
52+ toDigits = map digitToInt . show -- toDigits 49927398716 -> [4,9,9,2,7,3,9,8,7,1,6]
5853
54+ luhn1 :: Natural -> Bool
55+ luhn1 = divisibleBy10 . sumDigits . double2nd . reverse . toDigits
56+
57+ luhn2 :: Natural -> Bool
58+ luhn2 n = divisibleBy10 (sumDigits (double2nd (reverse (toDigits n))))
59+
60+ luhn3 :: Natural -> Bool
61+ luhn3 = toDigits >>>
62+ reverse >>>
63+ double2nd >>>
64+ sumDigits >>>
65+ divisibleBy10
66+
67+ luhn4 :: Natural -> Bool
68+ luhn4 = (0 == ) . (`mod` 10 ) . sum . map (uncurry (+) . (`divMod` 10 )) . zipWith (*) (cycle [1 ,2 ]) . map digitToInt . reverse . show
5969
6070{- -
61711. Reverse the order of the digits in the number.
@@ -65,13 +75,16 @@ luhn =
65755. Sum the partial sums of the even digits to form s2
66766. If s1 + s2 ends in zero then the original number is in the form of a valid credit card number as verified by the Luhn test.
6777--}
68- luhnTest :: String -> Bool
78+ luhnTest :: Natural -> Bool
6979luhnTest n =
70- let (odds, evens) = (oddsEvens . map digitToInt . reverse ) n -- 1. and 3.
80+ let (odds, evens) = (oddsEvens . map digitToInt . reverse . show ) n -- 1. and 3.
7181 s1 = sum odds -- 2.
7282 s2 = sum (map (crossSum . (* 2 )) evens) -- 4. and 5.
73- in (s1 + s2) `rem` 10 == 0 -- 6.
74-
83+ in (s1 + s2) `rem` 10 == 0 -- 6.
84+ where
85+ crossSum :: Int -> Int
86+ crossSum = uncurry (+) . (`divMod` 10 )
87+
7588oddsEvens :: [Int ] -> ([Int ], [Int ])
7689oddsEvens xs = foldr collectOddsEvens ([] , [] ) (zip xs [1 .. ])
7790 where
@@ -80,11 +93,12 @@ oddsEvens xs = foldr collectOddsEvens ([], []) (zip xs [1 ..])
8093 | odd i = (x : odds, evens)
8194 | otherwise = (odds, x : evens)
8295
83- crossSum :: Int -> Int
84- crossSum = uncurry (+) . (`divMod` 10 ) -- sum . map digitToInt . show
8596
8697main :: IO ()
8798main = do
88- let testcases = [" 49927398716" , " 49927398717" , " 1234567812345678" , " 1234567812345670" ]
99+ let testcases = [49927398716 , 49927398717 , 1234567812345678 , 1234567812345670 ]
89100 print $ map luhnTest testcases
90- print $ map luhn testcases
101+ print $ map luhn1 testcases
102+ print $ map luhn2 testcases
103+ print $ map luhn3 testcases
104+ print $ map luhn4 testcases
0 commit comments