Luhn test of credit card numbers: Difference between revisions

From Rosetta Code
Content added Content deleted
Line 1,931: Line 1,931:
1234567812345670 true
1234567812345670 true
</pre>
</pre>

=={{header|TXR}}==

<lang txr>@(do (defun luhn (num)
(for ((i 1) (sum 0))
((not (zerop num)) (zerop (mod sum 10)))
((inc i) (set num (trunc num 10)))
(let ((dig (mod num 10)))
(if (oddp i)
(inc sum dig)
(let ((dig2 (* 2 dig)))
(inc sum (+ (trunc dig2 10) (mod dig2 10)))))))))
@(collect :vars nil)
@{ccnumber /[0-9]+/}
@(output)
@ccnumber -> @(if (luhn (int-str ccnumber 10)) "good" "bad")
@(end)
@(end)</lang>

<pre>$ txr luhn.txr luhn.txt
49927398716 -> good
49927398717 -> bad
1234567812345678 -> bad
1234567812345670 -> good</pre>


=={{header|Ursala}}==
=={{header|Ursala}}==

Revision as of 06:53, 5 January 2012

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

Cf. SEDOL

ABAP

<lang Abap>METHOD luhn_check.

 DATA: sum(1) TYPE n VALUE 0. " Sum of checksum.
 DATA: current TYPE i. " Current digit.
 DATA: odd TYPE i VALUE 1. " Multiplier.
 DATA: len TYPE i. " String crowler.


 " Luhn algorithm.
 len = NUMOFCHAR( pi_string ) - 1.
 WHILE ( len >= 0 ).
   current = pi_string+len(1) * odd.
   IF ( current > 9 ).
     current = current - 9. " Digits sum.
   ENDIF.
   sum = sum + current.
   odd = 3 - odd. " 1 <--> 2 Swich
   len = len - 1. " Move to next charcter.
 ENDWHILE.
 " Validation check.
 IF ( sum = 0 ).
   pr_valid = abap_true.
 ELSE.
   pr_valid = abap_false.
 ENDIF.

ENDMETHOD.</lang>

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

Translation of: C
Works 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

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

APL

Works with: Dyalog 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   
Works with: APL+Win

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

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>

BBC BASIC

<lang bbcbasic> FOR card% = 1 TO 4

       READ cardnumber$
       IF FNluhn(cardnumber$) THEN
         PRINT "Card number " cardnumber$ " is valid"
       ELSE
         PRINT "Card number " cardnumber$ " is invalid"
       ENDIF
     NEXT card%
     END
     
     DATA 49927398716, 49927398717, 1234567812345678, 1234567812345670
     
     DEF FNluhn(card$)
     LOCAL I%, L%, N%, S%
     L% = LEN(card$)
     FOR I% = 1 TO L%
       N% = VAL(MID$(card$, L%-I%+1, 1))
       IF I% AND 1 THEN
         S% += N%
       ELSE
         N% *= 2
         S% += N% MOD 10 + N% DIV 10
       ENDIF
     NEXT
     = (S% MOD 10) = 0</lang>

C

<lang c>#include <string.h>

  1. 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 objc>- (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 ((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 (digit-char-p (aref n* i)))) (s2 (loop for i from 1 below l by 2 summing (sum-digits (* 2 (digit-char-p (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 ((sum-digits (n) (if (> n 9) (- n 9) n)))
   (funcall {zerop (mod * 10) (apply #'+) (mapcar #'sum-digits)

(mapcar #'* '#1=(1 2 . #1#)) (map 'list #'digit-char-p) reverse} seq)))</lang>

D

Translation of: C

<lang d>import std.algorithm;

pure bool luhnTest(in string num) {

 uint sum;
 foreach_reverse (i, n; num) {
   const uint ord = n - '\u0030';
   sum += ((num.length - i) & 1) ? ord : ord / 5 + (2 * ord) % 10;
 }
 return sum % 10 == 0;

}

void main() {

 auto data = ["49927398716", "49927398717",
              "1234567812345678", "1234567812345670"];
 assert(equal(map!luhnTest(data), [true, false, false, true]));

}</lang>

Alternative version

Translation of: SPARK

<lang d>import std.stdio;

const struct Interval(T) {

   T a, b;
   this(in T a_, in T b_) pure nothrow {
       this.a = a_;
       this.b = b_;
   }
   bool opBinaryRight(string op="in")(in T x) pure nothrow {
       return x >= a && x <= b;
   }

}

Interval!T interval(T)(in T a, in T b) pure nothrow {

   return Interval!T(a, b);

}


bool luhnTest(in string num) pure nothrow in {

   assert(num.length <= 20);

} body {

   int sum = 0;
   bool od = true;
   bool ok = true;
   immutable int numLen = num.length;
   foreach_reverse (/*const*/ int p; 0 .. numLen) {
        immutable int i = num[p] - '0';
        if (i !in interval(0, 9)) {
            ok = false;
            break;
        }
        immutable int x = ((i * 2) % 10) + (i / 5);
        assert((numLen - p) in interval(0, 19));
        assert(sum in interval(0, (numLen - p) * 10));
        assert(i in interval(0, 9));
        assert(x in interval(0, 9));
        sum += od ? i : x;
        od = !od;
   }
   return ok && (sum % 10) == 0;

}


void main() {

   foreach (n; ["49927398716", "49927398717", "1234567812345678",
                "1234567812345670", "123456781234567D"])
       writeln(n, " is ", (luhnTest(n) ? "" : "not "), "valid");

}</lang>

Euphoria

Translation of: C

<lang euphoria>function luhn(sequence cc)

   integer isOdd, oddSum, evenSum, digit
   isOdd = 1
   oddSum = 0
   evenSum = 0
   for i = length(cc) to 1 by -1 do
       digit = cc[i] - '0'
       if isOdd then
           oddSum += digit
       else
           evenSum += floor(digit / 5) + remainder(2 * digit, 10)
       end if
       isOdd = not isOdd
   end for
   return not remainder(oddSum + evenSum, 10)

end function

constant cc_numbers = {

   "49927398716",
   "49927398717",
   "1234567812345678",
   "1234567812345670"

}

for i = 1 to length(cc_numbers) do

   printf(1,"%s = %d\n", {cc_numbers[i], luhn(cc_numbers[i])})

end for</lang>

Output:

49927398716 = 1
49927398717 = 0
1234567812345678 = 0
1234567812345670 = 1

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>

Factor

<lang factor>USING: kernel math math.parser math.order math.ranges sequences ; IN: luhn

reversed-digits ( n -- list )
   { } swap
   [ dup 0 > ]
       [ 10 /mod  swapd suffix  swap ]
   while drop ;
luhn-digit ( n -- n )
   reversed-digits dup length iota [
       2dup swap nth
       swap odd? [ 2 *  10 /mod + ] when
   ] map sum 10 mod
   nip ;
luhn? ( n -- ? )
   luhn-digit 0 = ;

</lang>

Output:

( scratchpad ) 49927398716 luhn? .
t
( scratchpad ) 49927398717 luhn? .
f
( scratchpad ) 1234567812345678 luhn? .
f
( scratchpad ) 1234567812345670 luhn? .
t

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>

GAP

<lang gap>IsLuhn := function(n) local c, d, i, j, r; d := "0123456789"; j := 1; r := 0; for c in Reversed(String(n)) do i := Position(d, c); if i = fail then continue; fi; i := j*(i - 1); r := r + QuoInt(i, 10) + RemInt(i, 10); j := 3 - j; od; return RemInt(r, 10) = 0; end;

List([49927398716, 49927398717, 1234567812345678, 1234567812345670], IsLuhn);

  1. [ true, false, false, true ]
  1. Will also work on strings, and will skip non-digits

IsLuhn("4-992-739-871-6");

  1. true</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 += int(c - '0')
       }
   }
   return sum%10 == 0

}

func main() {

   for _, s := range strings.Split(input, "\n") {
       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

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

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 = Character.digit(reverse.charAt(i), 10);
           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>

Without any library. <lang javascript>var LuhnCheck = (function() { var luhnArr = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]; return function(str) { var counter = 0; var incNum; var odd = false; var temp = String(str).replace(/[^\d]/g, ""); if ( temp.length == 0) return false; for (var i = temp.length-1; i >= 0; --i) { incNum = parseInt(temp.charAt(i), 10); counter += (odd = !odd)? incNum : luhnArr[incNum]; } return (counter%10 == 0); } })();</lang>

Highly compressed version. <lang javascript>var luhn10 = function(a,b,c,d,e) {

 for(d = +a[b = a.length-1], e=0; b--;)
   c = +a[b], d += ++e % 2 ? 2 * c % 10 + (c > 4) : c;
 return !(d%10)

}

// returns true luhn10('4111111111111111')

// returns false luhn10('4111111111111112') </lang>

K

<lang k>so: {+/x@2*!_ceil(#x)%2} se: {+/{+/0$'$x}'2*x@1+2*!(#x)%2} luhn: {n:|0$'$x; 0=((se n)+so n)!10}</lang>

Translation of: J

<lang k>luhn2: {~(+/,//10_vs'1 2*+-1 2#((#n)!2){x,0}/n:0$'|$x)</lang>

Example:

<lang k> luhn'49927398716 49927398717 1234567812345678 1234567812345670 1 0 0 1

 luhn2'49927398716 49927398717 1234567812345678 1234567812345670

1 0 0 1</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>

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

-- Note that this function takes strings, not numbers. -- 16-digit numbers tend to be problematic -- when looking at individual digits. print(luhn'49927398716') print(luhn'49927398717') print(luhn'1234567812345678') print(luhn'1234567812345670')</lang>

Mathematica

<lang Mathematica>LuhnQ[nb_] := (Mod[Total[(2*ToExpression[#2;;All;;2]) /. {z_?(Function[v, v>9]) -> z-9}]

      + Total[ToExpression[#1;;All;;2]], 10] == 0)& [Characters[StringReverse[ToString[nb]]] ]

LuhnQ /@ {49927398716, 49927398717, 1234567812345678, 1234567812345670} ->{True, False, False, True}</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:

Works with: MATLAB version 2007a

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

Works with: MATLAB version 2007a

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

NetRexx

<lang netrexx> class LuhnTest

 method main(args=String[]) static
   cc	  = 0 
   cc[1] = '49927398716'
   cc[2] = '49927398717'
   cc[3] = '1234567812345678'
   cc[4] = '1234567812345670'

   loop k=1 while cc[k] <> 0
     r = checksum(cc[k])
     if r==0 then say cc[k].right(20) 'passed'
     else say cc[k].right(20) 'failed'
   end
   
   -- Luhn algorithm checksum for credit card numbers
 method checksum(t) static
   if t.length()//2 then t = '0't  --pad # on left with 0
   t = t.reverse()
   s = 0
   loop j = 1 to t.length()-1 by 2
     q = 2*t.substr(j+1,1)
     if q>9 then q = q.left(1) + q.right(1)
     s= s+t.substr(j,1)+q
   end
   return s//10\==0

</lang>

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>

Pascal

<lang Pascal>program luhn;

 function lunh(arg: string): boolean;
 var
   i, sum: integer;
   temp: byte;
 begin
   sum := 0;
   for i:= length(arg) downto 1 do begin  // Run the characters backwards
     temp := byte(arg[i])-48;             // Convert from ASCII to byte
     if (length(arg)-i) mod 2 = 0
       then sum := sum + temp             // Odd characters just add
       else if temp < 5
          then sum := sum + 2*temp        // Even characters add double
          else sum := sum + (2*temp)-9;   // or sum the digits of the doubling
   end;
   result := sum mod 10 = 0;              // Return true if sum ends in a 0
 end;

begin

 writeln('     49927398716: ', lunh('49927398716'));
 writeln('     49927398717: ', lunh('49927398717'));
 writeln('1234567812345678: ', lunh('1234567812345678'));
 writeln('1234567812345670: ', lunh('1234567812345670'));

end.</lang>

Output:

     49927398716: TRUE
     49927398717: FALSE
1234567812345678: FALSE
1234567812345670: TRUE

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;

}

  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

Works with: Rakudo Star version 2010.08

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

Translation of: C

<lang php>$numbers = "49927398716 49927398717 1234567812345678 1234567812345670"; foreach (split(' ', $numbers) as $n)

   echo "$n is ", luhnTest($n) ? 'valid' : 'not valid', '
';

function luhnTest($num) {

   $len = strlen($num);
   for ($i = $len-1; $i >= 0; $i--) {
       $ord = ord($num[$i]);
       if (($len - 1) & $i) {
           $sum += $ord;
       } else {
           $sum += $ord / 5 + (2 * $ord) % 10;
       }
   }       
   return $sum % 10 == 0;

}</lang>

49927398716 is valid
49927398717 is not valid
1234567812345678 is not valid
1234567812345670 is valid

And a more concise example using PHP core methods: <lang php>function luhn_test($num) {

   $str = ;
   foreach( array_reverse( str_split( $num ) ) as $i => $c ) $str .= ($i % 2 ? $c * 2 : $c );
   return array_sum( str_split($str) ) % 10 == 0;

}

foreach (array('49927398716','49927398717','1234567812345678','1234567812345670') as $n)

   echo "$n is ", luhn_test($n) ? 'valid' : 'not valid', "
\n";</lang>
49927398716 is valid
49927398717 is not valid
1234567812345678 is not valid
1234567812345670 is valid

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:

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

Comment: 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 r>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

Alternatively, in a functional style:

<lang scala>

 def luhnTest(number: String): Boolean = {
   val digits = number.reverse.map { _.toString.toInt }
   val s = digits.grouped(2) map { t => t(0) + 
       (if (t.length > 1) (t(1) * 2) % 10 + t(1) / 5 else 0)
   }
   s.sum % 10 == 0
 }

</lang>

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

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

Seed7

<lang seed7>$ include "seed7_05.s7i";

const func boolean: luhnTest (in string: cardNumber) is func

 result
   var boolean: luhnTest is FALSE;
 local
   var integer: index is 0;
   var integer: digit is 0;
   var boolean: isOdd is TRUE;
   var integer: oddSum is 0;
   var integer: evenSum is 0;
 begin
   for index range length(cardNumber) downto 1 do
     digit := integer parse str(cardNumber[index]);
     if isOdd then
       oddSum +:= digit;
     else
       evenSum +:= digit div 5 + (2 * digit) rem 10;
     end if;
     isOdd := not isOdd;
   end for;
   luhnTest := (oddSum + evenSum) rem 10 = 0;
 end func;

const proc: main is func

 local
   var string: cardNumber is "";
 begin
   for cardNumber range [] ("49927398716", "49927398717", "1234567812345678", "1234567812345670") do
     writeln(cardNumber <& ": " <& luhnTest(cardNumber));
   end for;
 end func;</lang>

Output:

49927398716: TRUE
49927398717: FALSE
1234567812345678: FALSE
1234567812345670: TRUE

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 

TXR

<lang txr>@(do (defun luhn (num)

      (for ((i 1) (sum 0))
           ((not (zerop num)) (zerop (mod sum 10)))
           ((inc i) (set num (trunc num 10)))
         (let ((dig (mod num 10)))
           (if (oddp i)
             (inc sum dig)
             (let ((dig2 (* 2 dig)))
               (inc sum (+ (trunc dig2 10) (mod dig2 10)))))))))

@(collect :vars nil) @{ccnumber /[0-9]+/} @(output) @ccnumber -> @(if (luhn (int-str ccnumber 10)) "good" "bad") @(end) @(end)</lang>

$ txr luhn.txr luhn.txt
49927398716 -> good
49927398717 -> bad
1234567812345678 -> bad
1234567812345670 -> good

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 ursala>#cast %bL

test = luhn* <49927398716,49927398717,1234567812345678,1234567812345670></lang> output:

<true,false,false,true>

ZX Spectrum Basic

<lang ZXBasic>10 LET c$="49927398716": GO SUB 1000 20 LET c$="49927398717": GO SUB 1000 30 LET c$="1234567812345678": GO SUB 1000 40 LET c$="1234567812345670": GO SUB 1000 999 STOP 1000 REM ************* 1001 REM * LUHN TEST * 1002 REM ************* 1010 LET r$="" 1020 FOR i=LEN c$ TO 1 STEP -1 1030 LET r$=r$+c$(i) 1040 NEXT i 1050 LET s1=0: LET s2=0 1060 FOR i=1 TO LEN r$ STEP 2 1070 LET s1=s1+VAL r$(i) 1080 NEXT i 1090 FOR i=2 TO LEN r$ STEP 2 1100 LET s2sub=VAL r$(i)*2 1110 IF s2sub>=10 THEN LET s2sub=1+s2sub-10 1120 LET s2=s2+s2sub 1130 NEXT i 1140 LET s$=STR$ (s1+s2) 1150 IF s$(LEN s$)="0" THEN PRINT c$;" VALID!": LET retval=1: RETURN 1160 PRINT c$;" INVALID!": LET retval=0: RETURN </lang> Output:

49927398716 VALID!
49927398717 INVALID!
1234567812345678 INVALID!
1234567812345670 VALID!