Luhn test of credit card numbers
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.
You are encouraged to solve this task according to the task description, using any language you may know.
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 trial 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
ActionScript
<lang ActionScript>function isValid(numString:String):Boolean { var isOdd:Boolean = true; var oddSum:uint = 0; var evenSum:uint = 0; for(var i:int = numString.length - 1; i >= 0; i--) { var digit:uint = uint(numString.charAt(i)) if(isOdd) oddSum += digit; else evenSum += digit/5 + (2*digit) % 10; isOdd = !isOdd; } if((oddSum + evenSum) % 10 == 0) return true; return false; }
trace(isValid("49927398716")); trace(isValid("49927398717")); trace(isValid("1234567812345678")); trace(isValid("1234567812345670")); </lang>
Ada
Same method as used in C code. Tested with GNAT <lang Ada> with Ada.Text_IO; use Ada.Text_IO; procedure luhn is function luhn_test(num : String) return Boolean is sum : Integer := 0; odd : Boolean := True; int : Integer; begin for p in reverse num'Range loop int := Integer'Value(num(p..p)); if odd then sum := sum + int; else sum := sum + (int*2 mod 10) + (int / 5); end if; odd := not odd; end loop; return (sum mod 10)=0; end luhn_test;
begin put_line(Boolean'Image(luhn_test("49927398716"))); put_line(Boolean'Image(luhn_test("49927398717"))); put_line(Boolean'Image(luhn_test("1234567812345678"))); put_line(Boolean'Image(luhn_test("1234567812345670"))); end luhn; </lang> Sample output:
TRUE FALSE FALSE TRUE
ALGOL 68
- note: This specimen retains the original C coding style.
<lang algol68>PROC to int = (CHAR c)INT:
ABS c - ABS "0";
PROC confirm = (STRING id)BOOL: (
BOOL is odd digit := TRUE; INT s := 0; STRING cp; FOR cp key FROM UPB id BY -1 TO LWB id DO INT k := to int(id[cp key]); s +:= IF is odd digit THEN k ELIF k /= 9 THEN 2*k MOD 9 ELSE 9 FI; is odd digit := NOT is odd digit OD; 0 = s MOD 10
);
main: (
[]STRING t cases = ( "49927398716", "49927398717", "1234567812345678", "1234567812345670" ); FOR cp key TO UPB t cases DO STRING cp = t cases[cp key]; print((cp, ": ", confirm(cp), new line)) OD
)</lang> Output:
49927398716: T 49927398717: F 1234567812345678: F 1234567812345670: T
APL
<lang APL> r←LuhnTest digits;even;odd;SumEven (odd even)←↓⍉⍎¨{(n,2)⍴(⍵,'0')↑⍨2×n←⌈(⍴⍵)÷2}⌽digits SumEven←{+/+/¨⍎¨¨⍕¨2×⍵} r←'0'=⊃¯1↑⍕(+/odd)+(SumEven even) </lang> Sample Output:
1 0 0 1
<lang APL> ∇ ret←LuhnTest num;s1;s2 [1] num←⌽((⌈10⍟num)/10)⊤num [2] s1←+/((⍴num)⍴1 0)/num [3] s2←+/∊(⊂10 10)⊤¨2×((⍴num)⍴0 1)/num [4] ret←0=10⊤s1+s2
∇
</lang> Sample Output:
LuhnTest¨ 49927398716 49927398717 1234567812345678 1234567812345670 1 0 0 1
AutoHotkey
<lang AutoHotkey>; Originally submitted by Laszlo:
MsgBox % LuhnTest(49927398716) MsgBox % LuhnTest(49927398717) MsgBox % LuhnTest(1234567812345678) MsgBox % LuhnTest(1234567812345670)
Return
- -----------------------------
LuhnTest(Number) {
MultFactor := 2 - ( StrLen(Number) & 1 ) , Sum := 0 Loop, Parse, Number Sum += ( ( 9 < ( Temp := MultFactor * A_LoopField ) ) ? Temp - 9 : Temp ) , MultFactor := 3 - MultFactor Return !Mod(Sum,10)
}</lang>
C
<lang c>#include <string.h>
- include <stdio.h>
int luhn(const char* cc) {
int isOdd = 1; unsigned int oddSum = 0; unsigned int evenSum = 0; int i;
for (i = strlen(cc) - 1; i >= 0; --i) { unsigned int digit = cc[i] - '0'; if (isOdd) { oddSum += digit; } else { evenSum += digit / 5 + (2 * digit) % 10; } isOdd = !isOdd; } return (oddSum + evenSum) % 10 == 0;
}
int main() {
const char* cc_numbers[] = { "49927398716", "49927398717", "1234567812345678", "1234567812345670", 0 };
const char** cc_number;
for (cc_number = cc_numbers; *cc_number; ++cc_number) { printf("%s = %d\n", *cc_number, luhn(*cc_number)); }
return 0;
}
</lang>
Output:
49927398716 = 1 49927398717 = 0 1234567812345678 = 0 1234567812345670 = 1
C++
<lang cpp>#include <iostream> using namespace std;
int toInt(const char c) {
return c-'0';
}
int confirm( const char *id) {
bool is_odd_dgt = true; int s = 0; const char *cp;
for(cp=id; *cp; cp++); while(cp > id) { --cp; int k = toInt(*cp); if (is_odd_dgt) { s += k; } else { s += (k!=9)? (2*k)%9 : 9; }
is_odd_dgt = !is_odd_dgt;
} return 0 == s%10;
}
int main( ) {
const char * t_cases[] = { "49927398716", "49927398717", "1234567812345678", "1234567812345670", NULL, }; for ( const char **cp = t_cases; *cp; cp++) { cout << *cp << ": " << confirm(*cp) << endl; } return 0;
}</lang>
C#
<lang csharp>using System;
namespace Luhn {
class Program { public static bool luhn(long n) { long nextdigit, sum = 0; bool alt = false; while (n != 0) { nextdigit = n % 10; if (alt) { nextdigit *= 2; nextdigit -= (nextdigit > 9) ? 9 : 0; } sum += nextdigit; alt = !alt; n /= 10; } return (sum % 10 == 0); }
static void Main(string[] args) { long[] given = new long[] {49927398716, 49927398717, 1234567812345678, 1234567812345670}; foreach (long num in given) { string valid = (luhn(num)) ? " is valid" : " is not valid"; Console.WriteLine(num + valid); } } }
} </lang>
49927398716 is valid 49927398717 is not valid 1234567812345678 is not valid 1234567812345670 is valid
Objective-C
<lang> - (NSMutableArray *) toCharArray {
NSMutableArray *characters = [[NSMutableArray alloc] initWithCapacity:[self length]]; for (int i=0; i < [self length]; i++) { NSString *ichar = [NSString stringWithFormat:@"%c", [self characterAtIndex:i]]; [characters addObject:ichar]; }
return [characters autorelease];
}
+ (BOOL) luhnCheck:(NSString *)stringToTest {
NSMutableArray *stringAsChars = [stringToTest toCharArray];
BOOL isOdd = YES; int oddSum = 0; int evenSum = 0;
for (int i = [stringToTest length] - 1; i >= 0; i--) {
int digit = [(NSString *)[stringAsChars objectAtIndex:i] intValue];
if (isOdd) oddSum += digit; else evenSum += digit/5 + (2*digit) % 10;
isOdd = !isOdd;
}
return ((oddSum + evenSum) % 10 == 0);
}
BOOL test0 = [self luhnCheck:@"49927398716"]; //Result = YES BOOL test1 = [self luhnCheck:@"49927398717"]; //Result = NO BOOL test2 = [self luhnCheck:@"1234567812345678"]; //Result = NO BOOL test3 = [self luhnCheck:@"1234567812345670"]; //Result = YES </lang>
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>
Common Lisp
<lang lisp>(defun luhn (n)
(labels ((dig->int (c) (- (char-code c) 48))
(sum-digits (n) (if (> n 9) (- n 9) n)))
(let ((n* (reverse n)) (l (length n))) (let ((s1 (loop for i from 0 below l by 2
summing (dig->int (aref n* i)))) (s2 (loop for i from 1 below l by 2 summing (sum-digits (* 2 (dig->int (aref n* i))))))) (zerop (mod (+ s1 s2) 10))))))</lang> Another version, using Maciej Pasternacki's reader macros for function composition and currying in the curly package: <lang lisp>(require :curly) (use-package :curly) (enable-curly-syntax)
(defun luhn (seq)
(labels ((dig->int (c) (- (char-code c) 48))
(sum-digits (n) (if (> n 9) (- n 9) n)))
(funcall {zerop (mod * 10) (apply #'+) (mapcar #'sum-digits)
(mapcar #'* '#1=(1 2 . #1#)) (map 'list #'dig->int) reverse} seq)))</lang>
D
Translation of C. This function has a clever way to select the odd digits; it's not actually faster than using a bool, but it's funny nevertheless that it works. <lang d>bool luhntest(string number) {
uint len = number.length; uint sum1, sum2;
for (int i = len - 1; i >= 0; i--) { uint num = number[i] - '\u0030'; if ((len - i) & 1) { sum1 += num; } else { sum2 += num / 5 + (2 * num) % 10; } } return (sum1 + sum2) % 10 == 0;
}</lang>
F#
<lang fsharp>let luhn (s:string) =
let rec g r c = function | 0 -> r | i -> let d = ((int s.[i - 1]) - 48) <<< c g (r + if d < 10 then d else d - 9) (1 - c) (i - 1) (g 0 0 s.Length) % 10 = 0</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>
Go
<lang go> package main
import (
"fmt" "strings"
)
const input = `49927398716 49927398717 1234567812345678 1234567812345670`
var t = [...]int{0, 2, 4, 6, 8, 1, 3, 5, 7, 9}
func luhn(s string) bool {
odd := len(s) & 1 var sum int for i, c := range s { if c < '0' || c > '9' { return false } if i&1 == odd { sum += t[c-'0'] } else { sum += c-'0' } } return sum%10 == 0
}
func main() {
for _, s := range strings.Split(input, "\n", -1) { fmt.Println(s, luhn(s)) }
} </lang> Output:
49927398716 true 49927398717 false 1234567812345678 false 1234567812345670 true
Haskell
<lang haskell>import Data.Char (digitToInt) luhn = (0 ==) . (`mod` 10) . sum . map (uncurry (+) . (`divMod` 10)) .
zipWith (*) (cycle [1,2]) . map digitToInt . reverse</lang>
Sample output <lang haskell>map luhn ["49927398716", "49927398717", "1234567812345678", "1234567812345670"] [True,False,False,True]</lang>
HicEst
<lang HicEst>CHARACTER numbers="49927398716 49927398717 1234567812345678 1234567812345670 "
DO nr = 1, 4
EDIT(Text=numbers, ITeM=nr, Parse=number) sum_odds = 0 sum_even = 0 DO i = LEN(number), 1, -2 sum_odds = sum_odds + ICHAR(number(i)) - 48 IF(i > 1) THEN even2 = 2 * (ICHAR(number(i-1)) - 48) sum_even = sum_even + MOD(even2, 10) + INT(even2/10) ENDIF ENDDO valid = (0 == MOD(sum_odds + sum_even, 10)) WRITE() number, " is ", "invalid"(1 + 2*valid:)
ENDDO</lang>
49927398716 is valid 49927398717 is invalid 1234567812345678 is invalid 1234567812345670 is valid
Icon and Unicon
Icon
We use map to pre-compute the sum of doubled digits. <lang icon>procedure main(aL) every write(i := !aL ," - ", ((\isluhn10(i),"valid")|"invalid") \ 1) end
procedure isluhn10(i) #: isluhn10(i) returns i (if i passes luhn10) or fails local sum
sum :=0 reverse(integer(i)) ? while not pos(0) do {
sum +:= move(1) sum +:= map(move(1),"0123456789","0246813579") }
return (sum % 10 = 0,i) end</lang>
Sample output
# luhn10 49927398716 49927398717 1234567812345678 1234567812345670 49927398716 - valid 49927398717 - invalid 1234567812345678 - invalid 1234567812345670 - valid
Unicon
This Icon solution works in Unicon. A solution that uses Unicon extensions has not been provided.
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>
Interpreting that example: In J, 1 is true, 0 is false, so the first and last provided digit sequences were valid and the middle two were not.
Java
<lang java>public class Luhn {
public static void main(String[] args) { System.out.println(luhnTest("49927398716")); System.out.println(luhnTest("49927398717")); System.out.println(luhnTest("1234567812345678")); System.out.println(luhnTest("1234567812345670")); } public static boolean luhnTest(String number){ int s1 = 0, s2 = 0; String reverse = new StringBuffer(number).reverse().toString(); for(int i = 0 ;i < reverse.length();i++){ int digit = Integer.parseInt(reverse.charAt(i) + ""); if(i % 2 == 0){//this is for odd digits, they are 1-indexed in the algorithm s1 += digit; }else{//add 2 * digit for 0-4, add 2 * digit - 9 for 5-9 s2 += 2 * digit; if(digit >= 5){ s2 -= 9; } } } return (s1 + s2) % 10 == 0; }
}</lang> Output:
true false false true
JavaScript
Using prototype. <lang javascript>mod10check = function(cc) {
return $A(cc).reverse().map(Number).inject(0, function(s, d, i) { return s + (i % 2 == 1 ? (d == 9 ? 9 : (d * 2) % 9) : d); }) % 10 == 0;
}; ['49927398716','49927398717','1234567812345678','1234567812345670'].each(function(i){alert(mod10check(i))});</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>
Liberty BASIC
<lang lb>' [RC] Luhn test
card$(1)="49927398716" card$(2)="49927398717" card$(3)="1234567812345678" card$(4)="1234567812345670"
for test = 1 to 4
odd=1 sum = 0 for n = len(card$(test)) to 1 step -1 num=val(mid$(card$(test),n,1)) if odd then sum = sum + num odd=0 else num=num*2 if num<=9 then sum = sum + num else sum = sum + val(left$(str$(num),1)) + val(right$(str$(num),1)) end if odd=1 end if next if sum mod 10 = 0 then print card$(test),"True" else print card$(test),"False" end if
next</lang>
Lua
<lang Lua> function luhn(n)
n=string.reverse(n) print(n) local s1=0 --sum odd digits for i=1,n:len(),2 do s1=s1+n:sub(i,i) end --evens local s2=0 for i=2,n:len(),2 do local doubled=n:sub(i,i)*2 doubled=string.gsub(doubled,'(%d)(%d)',function(a,b)return a+b end) s2=s2+doubled end print(s1) print(s2) local total=s1+s2 if total%10==0 then return true end return false
end print(luhn(49927398716)) print(luhn(49927398717)) print(luhn'1234567812345678') --must pass as strings, or else digit information is lost in conversion to string (uses exponents) print(luhn'1234567812345670') </lang>
MATLAB
There is no easy way to perform digit level manipulation of a number. In order to perform these digit level manipulations, many of the operations have to be performed on strings and char arrays. Using the built-in MATLAB string and numeral manipulation and conversion functions, it is possible to reduce the Luhn Algorithm to two lines of code. Unfortunately, because of the number of type conversions necessary to do this, these two lines of code are extremely obfuscated. The first example is the unobfuscated code. The second example is the vectorized version.
Example 1:
<lang MATLAB>function passes = luhnTest(creditCardNum)
%flip the order of the digits creditCardNum = fliplr( num2str(creditCardNum) )'; %sum the odd indexed digits s1 = sum( str2num( creditCardNum(1:2:length(creditCardNum)) ) ); %multiply the even indexed digits by 2 s2 = 2 * str2num(creditCardNum(2:2:length(creditCardNum))); %convert s2 into a cell array of strings, where each even indexed digit %multiplied by 2 takes one cell in the cell array. s2 = cellstr(num2str(s2)); %go through each cell in the cell array and make the strings a column %array of chars s2 = cellfun(@transpose,s2,'UniformOutput',false); %convert each array of chars into an array of numbers s2 = cellfun(@str2num ,s2,'UniformOutput',false); %Sum the partial sums of the even indexed digits to form s2 s2 = sum( cellfun(@sum, s2)); %Convert the sum of s1 and s2 into a column vector with each digit of %the the sum being an element of the vector passes = str2num(num2str(s1+s2).'); %Find the smallest digit in the checksum, zero will only appear in the %last digit of the number if zero is a digit of the checksum. Then %convert the minimum digit to a boolean. Only zero will convert to a %boolean false, any number larger than zero will convert to true. %Therefore, because we want to determine if a credit card number passes %the Luhn Test, we have to not the result of the conversion to boolean. passes = ~logical(min(passes));
end</lang>
Example 2: (vectorized version of the above code)
<lang MATLAB>function passes = luhnTest(creditCardNum)
%flip the order of the digits creditCardNum = fliplr( num2str(creditCardNum) )'; %create the checksum and check its last digit for zero passes = ~logical(min(str2num(num2str(sum( str2num( creditCardNum(1:2:length(creditCardNum)) ) )+sum( cellfun(@sum, cellfun(@str2num , cellfun(@transpose, cellstr(num2str(2*str2num(creditCardNum(2:2:length(creditCardNum))))) ,'UniformOutput',false) ,'UniformOutput',false)))).')));
end</lang>
This solution is guaranteed to work in older versions of MATLAB.
Due to the lack of function to change a number into a vector (e.g. 12=[1,2]) this function must be created first.
num2vec.m <lang MATLAB> function c=num2vec(a) if a==0
c=0;
else for i=1:100
if floor(a/(10^(i-1)))>0 n=i; end
end c=zeros(1,n); b=zeros(1,n); for i=1:n
b(i)=a/(10^(i-1)); b(i)=floor(b(i));
end for i=1:n-1
b(i)=b(i)-(b(i+1)*10);
end for i=1:n
c(i)=b(n-i+1);
end end </lang>
luhn.m <lang MATLAB> function T=luhn(a) a=num2vec(a); N=length(a); for i=1:N
b(i)=a(N+1-i);
end for i=1:ceil(N/2)
c(i+1)=b(2*i-1);
end s1=sum(c); for i=1:floor(N/2)
d(i)=sum(num2vec(2*b(2*i)));
end s2=sum(d); T=s1+s2; </lang>
MUMPS
<lang>LUHN(C)
NEW ODD,EVEN,S SET S=$REVERSE(C) SET ODD=0 FOR I=1:2:$LENGTH(S) SET ODD=ODD+$EXTRACT(S,I) SET EVEN=0 FOR I=2:2:$LENGTH(S) SET T=$EXTRACT(S,I)*2 SET EVEN=EVEN+$SELECT(T<=9:T,T>9:$EXTRACT(T,1)+$EXTRACT(T,2)) QUIT '((ODD+EVEN)#10)</lang>
USER>W !,$S($$LUHN^ROSETTA("49927398716")=0:"INVALID",1:"VALID") VALID USER>W !,$S($$LUHN^ROSETTA("49927398717")=0:"INVALID",1:"VALID") INVALID USER>W !,$S($$LUHN^ROSETTA("1234567812345678")=0:"INVALID",1:"VALID") INVALID USER>W !,$S($$LUHN^ROSETTA("1234567812345670")=0:"INVALID",1:"VALID") VALID
Objeck
<lang objeck> bundle Default {
class Luhn { function : IsValid(cc : String) ~ Bool { isOdd := true; oddSum := 0; evenSum := 0; for(i := cc->Size() - 1; i >= 0; i -= 1;) { digit : Int := cc->Get(i) - '0'; if(isOdd) { oddSum += digit; } else { evenSum += digit / 5 + (2 * digit) % 10; }; isOdd := isOdd <> true; }; return (oddSum + evenSum) % 10 = 0; } function : Main(args : String[]) ~ Nil { IsValid("49927398716")->PrintLine(); IsValid("49927398717")->PrintLine(); IsValid("1234567812345678")->PrintLine(); IsValid("1234567812345670")->PrintLine(); } }
} </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>
Perl
<lang perl>sub validate {
my @rev = reverse split //,$_[0]; my ($sum1,$sum2,$i) = (0,0,0);
for(my $i=0;$i<@rev;$i+=2) { $sum1 += $rev[$i]; last if $i == $#rev; $sum2 += 2*$rev[$i+1]%10 + int(2*$rev[$i+1]/10); } return ($sum1+$sum2) % 10 == 0;
} print validate('49927398716'); print validate('49927398717'); print validate('1234567812345678'); print validate('1234567812345670');</lang>
Or using map( ) and a precomputed array:
<lang Perl>sub luhn {
my (@n,$i,$sum) = split //, reverse $_[0]; my @a = map {int(2*$_ / 10) + (2*$_ % 10)} (0..9); map {$sum += $i++ % 2 ? $a[$_] : $_} @n; return ($sum % 10) ? 0 : 1;
}
- Test and display
map {print luhn($_), ": $_\n"}
qw(49927398716 49927398717 1234567812345678 1234567812345670);</lang>
Output:
1: 49927398716 0: 49927398717 0: 1234567812345678 1: 1234567812345670
Perl 6
Here we make use of comb, which splits into individual characters, and the sequence operator ..., which can intuit an even or odd sequence from the first two values. The [+] is a reduction metaoperator; with + it just sums the list of values. %% is the divisible-by operator. <lang perl6>sub luhn-test ($cc-number --> Bool) {
my @digits = $cc-number.comb.reverse; my $s1 = [+] @digits[0,2...@digits.end]; my $s2 = [+] @digits[1,3...@digits.end].map({[+] ($^a * 2).comb});
return ($s1 + $s2) %% 10;
}</lang>
And we can test it like this:
<lang perl6>use Test;
my @cc-numbers =
'49927398716' => True, '49927398717' => False, '1234567812345678' => False, '1234567812345670' => True;
plan @cc-numbers.elems;
for @cc-numbers».kv -> $cc, $expected-result {
is luhn-test($cc), $expected-result, "$cc {$expected-result ?? 'passes' !! 'does not pass'} the Luhn test.";
}</lang>
Output:
1..4 ok 1 - 49927398716 passes the Luhn test. ok 2 - 49927398717 does not pass the Luhn test. ok 3 - 1234567812345678 does not pass the Luhn test. ok 4 - 1234567812345670 passes the Luhn test.
PHP
<lang php>function luhn($num){ $sum = 0; $alt = false; for ($i = strlen($num)-1; $i>=0; $i--){ $n = substr($num,$i,1); if($alt){ $n *= 2; $n -= ($n > 9) ? 9 : 0; } $sum += $n; $alt = !$alt; } return ($sum%10==0); }</lang>
PicoLisp
<lang PicoLisp>(de luhn (Num) # 'Num' may be a number or a string
(=0 (% (sum '((C F) (setq C (- (char C) 48)) (if F C # Odd (+ (/ C 5) (% (* 2 C) 10)) ) ) # Even (flip (chop Num)) '(T NIL .) ) 10 ) ) )</lang>
Output:
: (mapcar luhn (49927398716 49927398717 1234567812345678 1234567812345670)) -> (0 NIL NIL 0)
PL/I
<lang PL/I> test: procedure options (main);
declare (cardnumber, rcn) character (20) varying; declare (i, k, s1, s2) fixed binary;
get edit (cardnumber) (L); cardnumber = trim(cardnumber); rcn = reverse(cardnumber); s1, s2 = 0; /* Sum the odd-numbered digits */ do i = 1 to length(rcn) by 2; s1 = s1 + substr(rcn, i, 1); end; /* Twice the even-numbered digits. */ do i = 2 to length(rcn) by 2; k = 2 * substr(rcn, i, 1); s2 = s2 + mod(k,10) + trunc(k/10); end; if mod(s1 + s2, 10) = 0 then put skip edit (cardnumber, ' passes the Luhn test' )(a); else put skip edit (cardnumber, ' does not pass the Luhn test' )(a); put skip list (s1 + s2);
end test; </lang> Output: <lang> 49927398716 passes the Luhn test
70
49927398717 does not pass the Luhn test
71
1234567812345678 does not pass the Luhn test
68
1234567812345670 passes the Luhn test
60
</lang> Coomment: it isn't necessary to reverse the string in order to perform the test.
PureBasic
<lang PureBasic>DataSection
Sample: Data.s "49927398716" Data.s "49927398717" Data.s "1234567812345678" Data.s "1234567812345670" Data.s ""
EndDataSection
Procedure isValid(cardNumber.s)
Protected i, length, s1, s2, s2a cardNumber = ReverseString(cardNumber) length = Len(cardNumber) For i = 1 To length Step 2 s1 + Val(Mid(cardNumber, i, 1)) Next
For i = 2 To length Step 2 s2a = Val(Mid(cardNumber, i, 1)) * 2 If s2a < 10 s2 + s2a Else s2 + 1 + Val(Right(Str(s2a), 1)) EndIf Next If Right(Str(s1 + s2), 1) = "0" ProcedureReturn #True Else ProcedureReturn #False EndIf
EndProcedure
If OpenConsole()
Define cardNumber.s Restore Sample Repeat Read.s cardNumber If cardNumber <> "" Print(cardNumber + " is ") If isValid(cardNumber) PrintN("valid") Else PrintN("not valid") EndIf EndIf Until cardNumber = "" Print(#CRLF$ + #CRLF$ + "Press ENTER to exit") Input() CloseConsole()
EndIf</lang> Sample output:
49927398716 is valid 49927398717 is not valid 1234567812345678 is not valid 1234567812345670 is valid
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))</lang>
Sample output:
49927398716 True 49927398717 False 1234567812345678 False 1234567812345670 True
R
<lang R>luhnTest <- function(cc){
# Reverse the digits, convert to numeric vector cc2 <- as.numeric(unlist(strsplit(as.character(cc), "")))[nchar(cc):1] s1 <- 0 s2 <- 0 for (index in 1:length(cc2)){ if (index %% 2 == 1){ s1 <- sum(s1, cc2[index]) } else if (cc2[index] >= 5){ s2 <- sum(s2, (cc2[index]*2 - 9)) } else { s2 <- sum(s2, (cc2[index]*2)) } } return ((s1 + s2) %% 10 == 0)
} </lang>
Sample Output <lang> sapply(c(49927398716, 49927398717, 1234567812345678, 1234567812345670), luhnTest) [1] TRUE FALSE FALSE TRUE </lang>
REXX
<lang REXX> /*REXX program to verify credit card numbers via the Luhn algorithm.*/
cc.=0 /*define default value of zero for CCs.*/ cc.1='49927398716' /*sample credit card number one. */ cc.2='49927398717' /*sample credit card number two. */ cc.3='1234567812345678' /*sample credit card number three. */ cc.4='1234567812345670' /*sample credit card number four. */
do k=1 while cc.k\==0 r=checksum(cc.k) if r==0 then say right(cc.k,20) ' passed the Luhn test.' else say right(cc.k,20) " flunked the Luhn test!" end
exit
/*-----Luhn algorith checksum for credit card numbers-----*/
checksum: procedure; parse arg t if length(t)//2 then t='0't /*odd #digits? Pad # on left with 0*/ t=reverse(t) s=0
do j=1 to length(t)-1 by 2 q=2*substr(t,j+1,1) if q>9 then q=left(q,1)+right(q,1) s=s+substr(t,j,1)+q end
return s//10\==0 </lang> Output:
49927398716 passed the Luhn test. 49927398717 flunked the Luhn test! 1234567812345678 flunked the Luhn test! 1234567812345670 passed the Luhn test.
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 -= 9 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]
Scala
Same method as used in C code <lang scala> def luhnTest(number: String): Boolean = {
var odd = true var sum = 0
for (int <- number.reverse.map{ i => i.toString.toInt }) { if (odd) sum = sum + int else sum = sum + (int * 2 % 10) + (int / 5)
odd = !odd }
sum % 10 == 0
}
println(luhnTest("49927398716")) println(luhnTest("49927398717")) println(luhnTest("1234567812345678")) println(luhnTest("1234567812345670")) </lang>
Sample output
true false false true
Scheme
<lang scheme> (define luhn
(lambda (n) (let loop ((number n) (index 0) (result 0)) (if (= 0 number) (= 0 (remainder result 10)) (loop (quotient number 10) (+ index 1) (+ result (if (even? index) (remainder number 10) (let ((part (* 2 (remainder number 10)))) (+ (remainder part 10) (quotient part 10))))))))))
</lang>
(map luhn '(49927398716 49927398717 1234567812345678 1234567812345670)) (#t #f #f #t)
sed
<lang sed># Split number into double evens and odds s/.*/&: /
- split
s/\([0-4]\)\([0-9]\):\(.*\) /:\1\1\3 \2/ s/\([5-9]\)\([0-9]\):\(.*\) /:1\1\1\3 \2/ t split
- Set up addition lookup table
s/\([0-9]\)*:\(.*\) \(.*\)/\1\2\3:0123456789012345678/
- add
s/\([0-9]\)0:/\1:/ s/\([0-9]\)1:\(.*\1.\{0\}\([0-9]\).*\)/\3:\2/ s/\([0-9]\)2:\(.*\1.\{1\}\([0-9]\).*\)/\3:\2/ s/\([0-9]\)3:\(.*\1.\{2\}\([0-9]\).*\)/\3:\2/ s/\([0-9]\)4:\(.*\1.\{3\}\([0-9]\).*\)/\3:\2/ s/\([0-9]\)5:\(.*\1.\{4\}\([0-9]\).*\)/\3:\2/ s/\([0-9]\)6:\(.*\1.\{5\}\([0-9]\).*\)/\3:\2/ s/\([0-9]\)7:\(.*\1.\{6\}\([0-9]\).*\)/\3:\2/ s/\([0-9]\)8:\(.*\1.\{7\}\([0-9]\).*\)/\3:\2/ s/\([0-9]\)9:\(.*\1.\{8\}\([0-9]\).*\)/\3:\2/ t add
/0:/a\ Pass /0:/!a\ Fail d</lang>
sample Output
$ sed -f luhn.sed <<! 49927398716 49927398717 1234567812345678 1234567812345670 ! Pass Fail Fail Pass
SNOBOL4
Using a precomputed array.
<lang SNOBOL4> define('luhn(n)a,d,i,j,sum') :(luhn_end) luhn n = reverse(n); a = array('0:9') ln1 a = (2 * i / 10) + remdr(2 * i,10)
i = lt(i,9) i + 1 :s(ln1)
ln2 n len(1) . d = :f(ln3)
d = ne(remdr(j,2),0) a<d>; j = j + 1 sum = sum + d :(ln2)
ln3 luhn = 0; luhn = eq(remdr(sum,10),0) 1 :(return) luhn_end
- # Test and display
test = " output = luhn(n) ': ' n" n = '49927398716'; eval(test) n = '49927398717'; eval(test) n = '1234567812345678'; eval(test) n = '1234567812345670'; eval(test)
end</lang>
Output:
1: 49927398716 0: 49927398717 0: 1234567812345678 1: 1234567812345670
SPARK
Works with SPARK GPL 2010 and GPS GPL 2010.
Based on the Ada version. All code shown to be free of run-time type errors.
A final test has been added which passes as valid unless there is an explicit test for all digits. <lang ada>with Spark_IO; --# inherit Spark_IO; --# main_program; procedure Luhn --# global in out Spark_IO.Outputs; --# derives Spark_IO.Outputs from *; is
function Luhn_Test (Num : String) return Boolean --# pre Num'Last <= 20; is Sum : Integer := 0; Od : Boolean := True; Int : Integer; X : Integer; OK : Boolean := True; begin for P in reverse Integer range Num'Range loop Int := Character'Pos(Num(P)) - Character'Pos('0'); if Int not in 0 .. 9 then OK := False; exit; end if; X := (((Int*2) mod 10) + (Int / 5)); --# assert Num'Last - P in 0 .. 19 --# and Sum in 0 .. (Num'Last - P) * 10 --# and Int in 0 .. 9 --# and X in 0 .. 10; if Od then Sum := Sum + Int; else Sum := Sum + X; end if; Od := not Od; end loop; return OK and (Sum mod 10) = 0; end Luhn_Test;
procedure Do_Test (Num : in String) --# global in out Spark_IO.Outputs; --# derives Spark_IO.Outputs from *, Num; --# pre Num'Last <= 20; is begin Spark_IO.Put_String(Spark_IO.Standard_Output, Num, 16); if Luhn_Test(Num) then Spark_IO.Put_String(Spark_IO.Standard_Output, " is valid.", 0); else Spark_IO.Put_String(Spark_IO.Standard_Output, " is not valid.", 0); end if; Spark_IO.New_Line(Spark_IO.Standard_Output, 1); end Do_Test;
begin
Do_Test("49927398716"); Do_Test("49927398717"); Do_Test("1234567812345678"); Do_Test("1234567812345670"); Do_Test("123456781234567D");
end Luhn;</lang> Output:
49927398716 is valid. 49927398717 is not valid. 1234567812345678 is not valid. 1234567812345670 is valid. 123456781234567D is not valid.
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
TUSCRIPT
<lang tuscript> $$ MODE TUSCRIPT MODE DATA $$ SET cardnumbers=* 49927398716 49927398717 1234567812345678 1234567812345670 $$ MODE TUSCRIPT -> collecting information for output-format SET length=MAX_LENGTH(cardnumbers) SET adjust=length+2
LOOP c=cardnumbers -> ">/" = any digit SET cstring=STRINGS (c,":>/:") SET creverse=REVERSE (cstring) SET s1=evenx2=esum=s2=""
LOOP n,oe=creverse SET modrest=MOD(n,2) IF (modrest==0) THEN SET even=oe*2 IF (even>9) THEN SET estring=STRINGS (even,":>/:") SET esum=SUM (estring) SET s2=APPEND (s2,esum) ELSE SET s2=APPEND (s2,even) ENDIF ELSE SET s1=APPEND(s1,oe) ENDIF ENDLOOP
SET s1=SUM(s1),s2=SUM(s2) SET checksum=s1+s2 SET c=CENTER(c,-adjust) IF (checksum.ew."0") THEN
PRINT c,"true"
ELSE
PRINT c,"false"
ENDIF ENDLOOP </lang> Output:
49927398716 true 49927398717 false 1234567812345678 false 1234567812345670 true
Ursala
<lang Ursala>#import std
- import nat
luhn = %nP; %np*hxiNCNCS; not remainder\10+ //sum:-0@DrlrHK32 ~&iK27K28TK25 iota10</lang>
Some notes on this solution:
iota10
is the list of natural numbers<0,1,2,3,4,5,6,7,8,9>
~&K27
and~&K28
ofiota10
extract the alternate items, respectively<0,2,4,6,8>
and<1,3,5,7,9>
~&K27K28T iota10
is their concatenation,<0,2,4,6,8,1,3,5,7,9>
which is also the list of values obtained by doubling each item ofiota10
and taking digit sums~&iK27K28TX iota10
would be the pair(<0,1,2,3,4,5,6,7,8,9>,<0,2,4,6,8,1,3,5,7,9>)
, but using the reification operatorK25
in place ofX
makes it an executable function taking any item of the left list as an argument and returning the corresponding item of the right.- The part beginning with
//
is a function of the form//f a
, which can be applied to any argumentb
to obtainf(a,b)
. In this case, thef
issum:-0@DrlrHK32
, which is equivalent to the composition of two functionssum:-0
and~&DrlrHK32
, anda
is the function just obtained by reification. - The function
~&D
by itself takes a pair(a,<b0
...bn>)
whose right side is a list, and returns the list of pairs<(a,b0)
...(a,bn)>
(i.e., a copy ofa
paired with eachb
). Thea
here will end up being the aforementioned function. ~&DrlrHK32
not only forms such a list of pairs, but operates on each pair thus obtained, alternately applying~&r
and~&lrH
to each pair in sequence, where~&r
simply returns the right side of the pair, and~&lrH
uses the left side as a function, which is applied to the right.sum:-0
computes the cumulative sum of a list of natural numbers using the binarysum
function, and the reduction operator (:-
) with vacuous sum 0.- The whole thing described up to this point is therefore a function that will take a list of numbers in the range 0 to 9, and compute the summation obtained when doubling and digit summing alternate items.
- The input list to this function is constructed from a single natural number first by
%nP
, which transforms it to text format in decimal, followed by%np*hxiNCNCS
, which reverses the digits, makes a separate text of each, and parses them as individual numbers. - The output from the function is tested for divisibility by 10 with
remainder\10
, with the result negated so that zero values map to true and non-zero to false.
usage: <lang>#cast %bL
test = luhn* <49927398716,49927398717,1234567812345678,1234567812345670></lang> output:
<true,false,false,true>