Luhn test of credit card numbers
![Task](http://static.miraheze.org/rosettacodewiki/thumb/b/ba/Rcode-button-task-crushed.png/64px-Rcode-button-task-crushed.png)
You are encouraged to solve this task according to the task description, using any language you may know.
The Luhn test is used by some credit card companies to distinguish valid credit card numbers from what could be a random selection of digits.
Those companies using credit card numbers that can be validated by the Luhn test have numbers that pass the following test:
- Reverse the order of the digits in the number.
- Take the first, third, ... and every other odd digit in the reversed digits and sum them to form the partial sum s1
- Taking the second, fourth ... and every other even digit in the reversed digits:
- Multiply each digit by two and sum the digits if the answer is greater than nine to form partial sums for the even digits
- Sum the partial sums of the even digits to form s2
- 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.
For example, if the trail number is 49927398716:
Reverse the digits: 61789372994 Sum the odd digits: 6 + 7 + 9 + 7 + 9 + 4 = 42 = s1 The even digits: 1, 8, 3, 2, 9 Two times each even digit: 2, 16, 6, 4, 18 Sum the digits of each multiplication: 2, 7, 6, 4, 9 Sum the last: 2 + 7 + 6 + 4 + 9 = 28 = s2 s1 + s2 = 70 which ends in zero which means that 49927398716 passes the Luhn test
The task is to write a function/method/procedure/subroutine that will validate a number with the Luhn test, and use it to validate the following numbers:
- 49927398716
- 49927398717
- 1234567812345678
- 1234567812345670
C.f. SEDOL
Clojure
<lang clojure>(defn- digits [n]
(map #(Character/digit % 10) (str n)))
(defn luhn? [n]
(let [sum (reduce + (map (fn [d idx] (if (even? idx) (reduce + (digits (* d 2))) d)) (reverse (digits n)) (iterate inc 1)))] (zero? (mod sum 10))))
(doseq [n [49927398716 49927398717 1234567812345678 1234567812345670]]
(println (luhn? n)))</lang>
Forth
<lang forth>: luhn ( addr len -- ? )
0 >r over + ( R: sum ) begin 1- 2dup <= while \ odd dup c@ [char] 0 - r> + >r 1- 2dup <= while \ even dup c@ [char] 0 - 2* 10 /mod + \ even digits doubled, split, and summed r> + >r repeat then 2drop r> 10 mod 0= ;
s" 49927398716" luhn . \ -1 s" 49927398717" luhn . \ 0 s" 1234567812345678" luhn . \ 0 s" 1234567812345670" luhn . \ -1</lang>
Fortran
<lang fortran>program luhn
implicit none integer :: nargs character(len=20) :: arg integer :: alen, i, dr integer, allocatable :: number(:) integer, parameter :: drmap(0:9) = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
! Get number nargs = command_argument_count() if (nargs /= 1) then stop end if call get_command_argument(1, arg, alen) allocate(number(alen)) do i=1, alen number(alen-i+1) = iachar(arg(i:i)) - iachar('0') end do
! Calculate number dr = 0 do i=1, alen dr = dr + merge(drmap(number(i)), number(i), mod(i,2) == 0) end do
if (mod(dr,10) == 0) then write(*,'(a,i0)') arg(1:alen)//' is valid' else write(*,'(a,i0)') arg(1:alen)//' is not valid' end if
end program luhn
! Results: ! 49927398716 is valid ! 49927398717 is not valid ! 1234567812345678 is not valid ! 1234567812345670 is valid</lang>
J
We can treat the odd digits the same as even digits, except that they are not doubled. Also, we do not need the intermediate sums.
<lang J>luhn=: 0 = 10 (| +/@,) 10 #.inv 1 2 *&|: _2 "."0\ |.</lang>
Example use: <lang J> luhn&> '49927398716';'49927398717';'1234567812345678';'1234567812345670' 1 0 0 1</lang>
Logo
<lang logo>to small? :list
output or [empty? :list] [empty? bf :list]
end to every.other :list
if small? :list [output :list] output fput first :list every.other bf bf :list
end to wordtolist :word
output map.se [?] :word
end
to double.digit :digit
output item :digit {0 2 4 6 8 1 3 5 7 9}@0 ; output ifelse :digit < 5 [2*:digit] [1 + modulo 2*:digit 10]
end
to luhn :credit
localmake "digits reverse filter [number? ?] wordtolist :credit localmake "s1 apply "sum every.other :digits localmake "s2 apply "sum map "double.digit every.other bf :digits output equal? 0 last sum :s1 :s2
end
show luhn "49927398716 ; true show luhn "49927398717 ; false show luhn "1234-5678-1234-5678 ; false show luhn "1234-5678-1234-5670 ; true</lang>
OCaml
<lang ocaml>let luhn s = let rec g r c = function | 0 -> r | i -> let d = c*((int_of_char s.[i-1]) - 48) in g (r + (d/10) + (d mod 10)) (3-c) (i-1) in (g 0 1 (String.length s)) mod 10 = 0;; </lang>
Sample output <lang ocaml># List.map luhn [ "49927398716"; "49927398717"; "1234567812345678"; "1234567812345670" ];; - : bool list = [true; false; false; true] </lang>
Oz
<lang oz>declare
fun {Luhn N} {Sum {List.mapInd {Reverse {Digits N}} fun {$ Idx Dig} if {IsEven Idx} then {Sum {Digits 2*Dig}} else Dig end end}} mod 10 == 0 end
fun {Digits N} {Map {Int.toString N} fun {$ D} D - &0 end} end
fun {Sum Xs} {FoldL Xs Number.'+' 0} end
in
{Show {Map [49927398716 49927398717 1234567812345678 1234567812345670] Luhn}}</lang>
Python
The divmod in the function below conveniently splits a number into its two digits ready for summing: <lang python>>>> def luhn(n): r = [int(ch) for ch in str(n)][::-1] return (sum(r[0::2]) + sum(sum(divmod(d*2,10)) for d in r[1::2])) % 10 == 0
>>> for n in (49927398716, 49927398717, 1234567812345678, 1234567812345670): print(n, luhn(n))
49927398716 True
49927398717 False
1234567812345678 False
1234567812345670 True</lang>
Ruby
<lang ruby>def luhn(code)
s1 = s2 = 0 code.to_s.reverse.chars.each_slice(2) do |odd, even| s1 += odd.to_i
double = even.to_i * 2 double = 1 + double % 10 if double >= 10 s2 += double end (s1 + s2) % 10 == 0
end
[49927398716, 49927398717, 1234567812345678, 1234567812345670].each do |n|
p [n, luhn(n)]
end</lang>
outputs
[49927398716, true] [49927398717, false] [1234567812345678, false] [1234567812345670, true]
Tcl
Based on an algorithmic encoding for the test on Wikipedia. <lang tcl>package require Tcl 8.5 proc luhn digitString {
if {[regexp {[^0-9]} $digitString]} {error "not a number"} set sum 0 set flip 1 foreach ch [lreverse [split $digitString {}]] {
incr sum [lindex { {0 1 2 3 4 5 6 7 8 9} {0 2 4 6 8 1 3 5 7 9} } [expr {[incr flip] & 1}] $ch]
} return [expr {($sum % 10) == 0}]
}</lang> Driver: <lang tcl>foreach testNumber {
49927398716 49927398717 1234567812345678 1234567812345670
} {
puts [format "%s is %s" $testNumber \
[lindex {"NOT valid" "valid"} [luhn $testNumber]]] }</lang> Output:
49927398716 is valid 49927398717 is NOT valid 1234567812345678 is NOT valid 1234567812345670 is valid
Ursala
<lang Ursala>#import std
- import nat
luhn = %nP; %np*hxiNCNCS; not remainder\10+ //sum:-0@DrlrHK32 ~&iK27K28TK25 iota10</lang> usage: <lang>#cast %bL
test = luhn* <49927398716,49927398717,1234567812345678,1234567812345670></lang> output:
<true,false,false,true>