Luhn test of credit card numbers

From Rosetta Code
Revision as of 17:34, 2 May 2010 by 94.216.64.176 (talk)
Task
Luhn test of credit card numbers
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:

  1. Reverse the order of the digits in the number.
  2. Take the first, third, ... and every other odd digit in the reversed digits and sum them to form the partial sum s1
  3. Taking the second, fourth ... and every other even digit in the reversed digits:
  1. Multiply each digit by two and sum the digits if the answer is greater than nine to form partial sums for the even digits
  2. Sum the partial sums of the even digits to form s2
  1. 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

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>

ALGOL 68

Translation of: C++

- note: This specimen retains the original C coding style.

Works with: ALGOL 68 version Standard - no extensions to language used
Works with: ALGOL 68G version Any - tested with release 1.18.0-9h.tiny
Works with: ELLA ALGOL 68 version Any (with appropriate job cards)

<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

AutoHotkey

<lang AutoHotkey>; Originally submitted by Laszlo:

http://www.autohotkey.com/forum/post-229412.html#229412

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 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; using System.Collections.Generic; using System.Linq; using System.Text;

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

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>

F#

<lang fsharp>let luhn (s:string) =

 let rec g r c = function
 | 0 -> r
 | i ->
     let d = c * ((System.Convert.ToInt32(s.[i-1]))-48) in 
     g (r + (d/10) + (d % 10)) (3-c) (i-1)
 in
 (g 0 1 (String.length s)) % 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>

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>

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>

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>

<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>

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> 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> 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>

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>

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>

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>

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>

outputs

	
49927398716 True
49927398717 False
1234567812345678 False
1234567812345670 True

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]

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

  1. 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 of iota10 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 of iota10 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 operator K25 in place of X 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 argument b to obtain f(a,b). In this case, the f is sum:-0@DrlrHK32, which is equivalent to the composition of two functions sum:-0 and ~&DrlrHK32, and a 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 of a paired with each b). The a 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 binary sum 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>