Skip to content

Commit 55189b4

Browse files
committed
add implementaion for Luhn algorithm.
1 parent 804f5c3 commit 55189b4

File tree

1 file changed

+90
-0
lines changed

1 file changed

+90
-0
lines changed

src/Luhn.hs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
--{-# LANGUAGE ScopedTypeVariables #-}
2+
3+
module Luhn where
4+
5+
import Data.Char (digitToInt)
6+
import Control.Arrow ((>>>))
7+
8+
{--
9+
Reverse the order of the digits in the number.
10+
Take the first, third, ... and every other odd digit in the reversed digits and sum them to form the partial sum s1
11+
Taking the second, fourth ... and every other even digit in the reversed digits:
12+
Multiply each digit by two and sum the digits if the answer is greater than nine to form partial sums for the even digits
13+
Sum the partial sums of the even digits to form s2
14+
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.
15+
16+
For example, if the trial number is 49927398716:
17+
18+
Reverse the digits:
19+
61789372994
20+
Sum the odd digits:
21+
6 + 7 + 9 + 7 + 9 + 4 = 42 = s1
22+
The even digits:
23+
1, 8, 3, 2, 9
24+
Two times each even digit:
25+
2, 16, 6, 4, 18
26+
Sum the digits of each multiplication:
27+
2, 7, 6, 4, 9
28+
Sum the last:
29+
2 + 7 + 6 + 4 + 9 = 28 = s2
30+
31+
s1 + s2 = 70 which ends in zero which means that 49927398716 passes the Luhn test
32+
33+
Task
34+
Write a function/method/procedure/subroutine that will validate a number with the Luhn test, and
35+
use it to validate the following numbers:
36+
37+
49927398716
38+
49927398717
39+
1234567812345678
40+
1234567812345670
41+
--}
42+
43+
44+
45+
{--
46+
luhn :: String -> Bool
47+
luhn = (0 ==) . (`mod` 10) . sum . map (uncurry (+) . (`divMod` 10)) . zipWith (*) (cycle [1,2]) . map digitToInt . reverse
48+
---}
49+
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 ==)
58+
59+
60+
{--
61+
1. Reverse the order of the digits in the number.
62+
2. Take the first, third, ... and every other odd digit in the reversed digits and sum them to form the partial sum s1
63+
3. Taking the second, fourth ... and every other even digit in the reversed digits:
64+
4. Multiply each digit by two and sum the digits if the answer is greater than nine to form partial sums for the even digits
65+
5. Sum the partial sums of the even digits to form s2
66+
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.
67+
--}
68+
luhnTest :: String -> Bool
69+
luhnTest n =
70+
let (odds, evens) = (oddsEvens . map digitToInt . reverse) n -- 1. and 3.
71+
s1 = sum odds -- 2.
72+
s2 = sum (map (crossSum . (* 2)) evens) -- 4. and 5.
73+
in (s1 + s2) `rem` 10 == 0 -- 6.
74+
75+
oddsEvens :: [Int] -> ([Int], [Int])
76+
oddsEvens xs = foldr collectOddsEvens ([], []) (zip xs [1 ..])
77+
where
78+
collectOddsEvens :: (Int, Int) -> ([Int], [Int]) -> ([Int], [Int])
79+
collectOddsEvens (x, i) (odds, evens)
80+
| odd i = (x : odds, evens)
81+
| otherwise = (odds, x : evens)
82+
83+
crossSum :: Int -> Int
84+
crossSum = uncurry (+) . (`divMod` 10) --sum . map digitToInt . show
85+
86+
main :: IO ()
87+
main = do
88+
let testcases = ["49927398716", "49927398717", "1234567812345678", "1234567812345670"]
89+
print $ map luhnTest testcases
90+
print $ map luhn testcases

0 commit comments

Comments
 (0)