Skip to content

Commit 96674c2

Browse files
committed
add implementaion for Luhn algorithm.
1 parent 375349d commit 96674c2

File tree

1 file changed

+37
-23
lines changed

1 file changed

+37
-23
lines changed

src/Luhn.hs

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
--{-# LANGUAGE ScopedTypeVariables #-}
2-
31
module Luhn where
42

53
import Data.Char (digitToInt)
64
import Control.Arrow ((>>>))
5+
import Data.Natural (Natural)
76

8-
{--
7+
{-- From Rosetta Code:
98
Reverse the order of the digits in the number.
109
Take the first, third, ... and every other odd digit in the reversed digits and sum them to form the partial sum s1
1110
Taking 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
{--
6171
1. Reverse the order of the digits in the number.
@@ -65,13 +75,16 @@ luhn =
6575
5. Sum the partial sums of the even digits to form s2
6676
6. 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
6979
luhnTest 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+
7588
oddsEvens :: [Int] -> ([Int], [Int])
7689
oddsEvens 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

8697
main :: IO ()
8798
main = 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

Comments
 (0)