Luhn test of credit card numbers: Difference between revisions
→{{header|Go}}: added output |
→{{header|Lua}}: more simple and concise, based on Ada algorithm |
||
(432 intermediate revisions by more than 100 users not shown) | |||
Line 1: | Line 1: | ||
{{task}} |
{{task|Checksums}} |
||
{{omit from|GUISS}} |
|||
The [[wp:Luhn algorithm|Luhn test]] is used by some credit card companies to distinguish valid credit card numbers from what could be a random selection of digits. |
The [[wp:Luhn algorithm|Luhn test]] is used by some credit card companies to distinguish valid credit card numbers from what could be a random selection of digits. |
||
Line 10: | Line 12: | ||
# 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. |
# 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. |
||
<br> |
|||
For example, if the trial number is 49927398716: |
For example, if the trial number is 49927398716: |
||
<pre>Reverse the digits: |
<pre>Reverse the digits: |
||
Line 26: | Line 29: | ||
s1 + s2 = 70 which ends in zero which means that 49927398716 passes the Luhn test</pre> |
s1 + s2 = 70 which ends in zero which means that 49927398716 passes the Luhn test</pre> |
||
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:''' |
|||
;Task: |
|||
:49927398716 |
|||
Write a function/method/procedure/subroutine that will validate a number with the Luhn test, and |
|||
:49927398717 |
|||
<br>use it to validate the following numbers: |
|||
:1234567812345678 |
|||
49927398716 |
|||
:1234567812345670 |
|||
49927398717 |
|||
1234567812345678 |
|||
1234567812345670 |
|||
<br> |
<br> |
||
;Related tasks: |
|||
* [[SEDOLs|SEDOL]] |
|||
* [[Calculate International Securities Identification Number|ISIN]] |
|||
<br><br> |
|||
=={{header|11l}}== |
|||
C.f. [[SEDOLs|SEDOL]] |
|||
{{trans|Python}} |
|||
<syntaxhighlight lang="11l">F luhn(n) |
|||
V ch = String(n) |
|||
V sum = 0 |
|||
V chParity = ch.len % 2 |
|||
L(i) (ch.len-1 .. 0).step(-1) |
|||
V j = Int(ch[i]) |
|||
I (i + 1) % 2 != chParity |
|||
j *= 2 |
|||
I j > 9 |
|||
j -= 9 |
|||
sum += j |
|||
R sum % 10 == 0 |
|||
L(n) (49927398716, |
|||
49927398717, |
|||
1234567812345678, |
|||
1234567812345670) |
|||
print(luhn(n))</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
1B |
|||
0B |
|||
0B |
|||
1B |
|||
</pre> |
|||
=={{header|360 Assembly}}== |
|||
{{trans|VBScript}} |
|||
For maximum compatibility, this program uses only the basic instruction set (S/360) |
|||
and an ASSIST macro (XPRNT) to keep the code as short as possible. |
|||
<syntaxhighlight lang="360asm">* Luhn test of credit card numbers 22/05/2016 |
|||
LUHNTEST CSECT |
|||
USING LUHNTEST,R13 base register |
|||
B 72(R15) skip savearea |
|||
DC 17F'0' savearea |
|||
STM R14,R12,12(R13) prolog |
|||
ST R13,4(R15) " |
|||
ST R15,8(R13) " |
|||
LR R13,R15 " |
|||
LA R9,T @t(k) |
|||
LA R8,N for n |
|||
LOOPK EQU * for k=1 to n |
|||
LR R4,R9 @t(k),@s[1] |
|||
LA R6,1 from i=1 |
|||
LA R7,M to m |
|||
LOOPI1 CR R6,R7 for i=1 to m |
|||
BH ELOOPI1 leave i |
|||
CLI 0(R4),C' ' if mid(s,i,1)=" " |
|||
BNE ITERI1 then |
|||
BCTR R6,0 i-1 |
|||
ST R6,L l=i-1 |
|||
B ELOOPI1 exit for |
|||
* end if |
|||
ITERI1 LA R4,1(R4) next @s[i] |
|||
LA R6,1(R6) i=i+1 |
|||
B LOOPI1 next i |
|||
ELOOPI1 EQU * out of loop i |
|||
MVC W,BLANK w=" " |
|||
LA R4,W iw=@w |
|||
LR R5,R9 is=@s |
|||
A R5,L is=@s+l |
|||
BCTR R5,0 is=s+l-1 |
|||
L R6,L i=l |
|||
LA R7,1 to 1 |
|||
LOOPI2 CR R6,R7 for i=l to 1 by -1 |
|||
BL ELOOPI2 leave i |
|||
MVC 0(1,R4),0(R5) mid(w,iw,1)=mid(s,is,1) |
|||
LA R4,1(R4) iw=iw+1 |
|||
BCTR R5,0 is=is-1 |
|||
BCTR R6,0 i=i-1 |
|||
B LOOPI2 next i |
|||
ELOOPI2 EQU * out of loop i |
|||
LA R11,0 s1=0 |
|||
LA R12,0 s2=0 |
|||
LA R6,1 i=1 |
|||
L R7,L to l |
|||
LOOPI3 CR R6,R7 for i=1 to l |
|||
BH ELOOPI3 leave i |
|||
LA R2,W-1 @w-1 |
|||
AR R2,R6 w[i] |
|||
MVC CI,0(R2) ci=mid(w,i,1) |
|||
NI CI,X'0F' zap upper half byte |
|||
LR R4,R6 i |
|||
SRDA R4,32 >>32 |
|||
D R4,=F'2' i/2 |
|||
LTR R4,R4 if mod(i,2)>0 |
|||
BNH NOTMOD then |
|||
XR R2,R2 clear |
|||
IC R2,CI z=cint(mid(w,i,1)) |
|||
AR R11,R2 s1=s1+cint(mid(w,i,1)) |
|||
B EIFMOD else |
|||
NOTMOD XR R2,R2 clear |
|||
IC R2,CI cint(mid(w,i,1)) |
|||
SLA R2,1 *2 |
|||
ST R2,Z z=cint(mid(w,i,1))*2 |
|||
C R2,=F'10' if z<10 |
|||
BNL GE10 then |
|||
A R12,Z s2=s2+z |
|||
B EIF10 else |
|||
GE10 L R2,Z z |
|||
CVD R2,PL8 binary to packed |
|||
UNPK CL16,PL8 packed to zoned |
|||
OI CL16+15,X'F0' zoned to char (zap sign) |
|||
MVC X(1),CL16+15 x=right(cstr(z),1) |
|||
NI X,X'0F' zap upper half byte |
|||
XR R2,R2 r2=0 |
|||
IC R2,X r2=cint(right(cstr(z),1)) |
|||
AR R12,R2 s2=s2+r2 |
|||
LA R12,1(R12) s2=s2+cint(right(cstr(z),1))+1 |
|||
EIF10 EQU * end if |
|||
EIFMOD EQU * end if |
|||
LA R6,1(R6) i=i+1 |
|||
B LOOPI3 next i |
|||
ELOOPI3 EQU * out of loop i |
|||
LR R1,R11 s1 |
|||
AR R1,R12 s1+s2 |
|||
CVD R1,PL8 binary to packed |
|||
UNPK CL16,PL8 packed to zoned |
|||
CLI CL16+15,X'C0' if right(cstr(s1+s2),1)="0" |
|||
BNE NOTZERO then |
|||
MVC R,=CL8'Valid' r="Valid" |
|||
B ECLI else |
|||
NOTZERO MVC R,=CL8'Invalid' r="Invalid" |
|||
ECLI EQU * end if |
|||
MVC PG(M),0(R9) t(k) |
|||
MVC PG+M+1(L'R),R r |
|||
XPRNT PG,L'PG print buffer |
|||
LA R9,M(R9) at=at+m |
|||
BCT R8,LOOPK next k |
|||
L R13,4(0,R13) epilog |
|||
LM R14,R12,12(R13) " |
|||
XR R15,R15 " |
|||
BR R14 exit |
|||
N EQU (TEND-T)/L'T |
|||
M EQU 20 |
|||
T DC CL(M)'49927398716 ' |
|||
DC CL(M)'49927398717 ' |
|||
DC CL(M)'1234567812345678 ' |
|||
DC CL(M)'1234567812345670 ' |
|||
TEND DS 0C |
|||
W DS CL(M) |
|||
BLANK DC CL(M)' ' |
|||
L DS F |
|||
Z DS F |
|||
PL8 DS PL8 |
|||
CL16 DS CL16 |
|||
CI DS C |
|||
X DS C |
|||
R DS CL8 |
|||
PG DC CL80' ' buffer |
|||
YREGS |
|||
END LUHNTEST</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
49927398716 Valid |
|||
49927398717 Invalid |
|||
1234567812345678 Invalid |
|||
1234567812345670 Valid |
|||
</pre> |
|||
=={{header|8080 Assembly}}== |
|||
<syntaxhighlight lang="8080asm"> org 100h |
|||
jmp demo |
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|||
;;; Check if the 0-terminated string at HL passes the Luhn test. |
|||
;;; Returns with carry clear if the string passes, carry set |
|||
;;; if the string fails. |
|||
luhn: mvi b,0 ; Counter |
|||
mov d,b ; D = S1+S2 (we don't need to keep them separate) |
|||
lscan: mov a,m ; Get byte |
|||
inx h ; Increment pointer |
|||
inr b ; Increment counter |
|||
ana a ; Is it 0? |
|||
jnz lscan ; If not, try next byte |
|||
dcx h ; Go to the byte before the 0 |
|||
dcx h |
|||
dcr b ; Decrement counter |
|||
rz ; If 0, the string was empty, return. |
|||
lloop: mvi c,'0' ; ASCII zero |
|||
mov a,d ; Add odd digit to the total |
|||
add m |
|||
sub c ; Subtract ASCII zero |
|||
mov d,a |
|||
dcr b ; If last digit, we're done |
|||
jz ldone |
|||
dcx h ; Go back one byte |
|||
mov a,m ; Get even digit |
|||
sub c ; Subtract ASCII zero |
|||
add a ; Multiply by two |
|||
mvi c,9 ; 10-1, compensate for extra subtraction loop |
|||
ldiv: inr c ; Find two digits using trial subtraction |
|||
sui 10 |
|||
jnc ldiv |
|||
add c ; Add the possible second digit in |
|||
add d ; Add it to the total |
|||
mov d,a |
|||
dcx h ; Go back one byte |
|||
dcr b ; Done yet? |
|||
jnz lloop |
|||
ldone: mov a,d ; See if total is divisible by 10 |
|||
mvi b,10 |
|||
lchk: sub b ; Trial subtraction, subtract 10 |
|||
rz ; If zero, it is divisible, return (carry clear) |
|||
rc ; If carry, it is not divisible, return (carry set) |
|||
jmp lchk |
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|||
;;; Run the routine on the argument given on the CP/M command line |
|||
demo: lxi h,80h ; Zero-terminate the command line argument |
|||
mov a,m |
|||
add l |
|||
mov l,a |
|||
inr l |
|||
mvi m,0 |
|||
mvi l,82h ; Run the 'luhn' subroutine |
|||
call luhn |
|||
mvi c,9 |
|||
lxi d,pass ; Carry clear = print 'Pass' |
|||
jnc 5 |
|||
lxi d,fail ; Carry set = print 'Fail' |
|||
jmp 5 |
|||
pass: db 'Pass$' |
|||
fail: db 'Fail$'</syntaxhighlight> |
|||
{{out}} |
|||
<pre>A>luhn 49927398716 |
|||
Pass |
|||
A>luhn 49927398717 |
|||
Fail |
|||
A>luhn 1234567812345678 |
|||
Fail |
|||
A>luhn 1234567812345670 |
|||
Pass</pre> |
|||
=={{header|8086 Assembly}}== |
|||
<syntaxhighlight lang="asm"> bits 16 |
|||
cpu 8086 |
|||
org 100h |
|||
section .text |
|||
jmp demo |
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|||
;;; Check whether the 0-terminated string at DS:SI passes the Luhn |
|||
;;; test. Returns with carry clear if the string passes, carry |
|||
;;; set if the string fails. |
|||
luhn: push es ; Keep original ES, and set ES=DS so SCASB can be used. |
|||
push ds ; "REP DS:SCASB" is a bad idea, because the 286 has a |
|||
pop es ; bug where it "forgets" the 2nd prefix if interrupted! |
|||
mov di,si ; DI = pointer |
|||
xor ax,ax ; Zero to test against |
|||
xor bl,bl ; BL = S1+S2 |
|||
mov dx,0A30h ; DH = 10 (divisor), DL = '0' (ASCII zero) |
|||
xor cx,cx ; Set counter to 65535 |
|||
dec cx |
|||
cld ; Seek forwards |
|||
repnz scasb ; Find zero |
|||
dec di ; SCASB goes one byte too far |
|||
xchg si,di ; SI = pointer, DI = end (or rather, beginning) |
|||
mov cx,si ; CX = counter |
|||
sub cx,di |
|||
jcxz .done ; Empty string = stop |
|||
dec si ; We don't need the zero itself |
|||
std ; Seek backwards |
|||
.loop: lodsb ; Get number in odd position |
|||
sub al,dl ; Subtract ASCII zero |
|||
add bl,al ; Add to total |
|||
dec cx ; One fewer character |
|||
jz .done ; No more characters = stop |
|||
lodsb ; Get number in even position |
|||
sub al,dl ; Subtract ASCII zero |
|||
add al,al ; Multiply by two |
|||
xor ah,ah ; AX = AL |
|||
div dh ; Divide by 10; AL=quotient, AH=remainder |
|||
add al,ah ; Add the two "digits" together |
|||
add bl,al ; Add to total |
|||
loop .loop ; Decrement CX and loop |
|||
.done: xor ah,ah ; Divide total by 10 |
|||
mov al,bl |
|||
div dh |
|||
and ah,ah ; If remainder 0, then return with carry clear |
|||
jz .out |
|||
stc ; Set carry (remainder wasn't 0, the test failed) |
|||
.out: cld ; Clean up: clear direction flag, |
|||
pop es ; and restore ES. |
|||
ret |
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|||
;;; Run the 'luhn' routine on the argument given on the MS-DOS |
|||
;;; command line. |
|||
demo: mov si,80h ; Zero-terminate the argument |
|||
xor bh,bh |
|||
mov bl,[si] |
|||
mov [si+bx+1],bh |
|||
inc si |
|||
inc si |
|||
call luhn ; Call the routine |
|||
mov ah,9 |
|||
mov dx,pass ; If carry is clear, print 'Pass' |
|||
jnc print |
|||
mov dx,fail ; Otherwise, print 'fail' |
|||
print: int 21h |
|||
ret |
|||
section .data |
|||
pass: db 'Pass$' |
|||
fail: db 'Fail$'</syntaxhighlight> |
|||
{{out}} |
|||
<pre>C:\>luhn86 49927398716 |
|||
Pass |
|||
C:\>luhn86 49927398717 |
|||
Fail |
|||
C:\>luhn86 1234567812345678 |
|||
Fail |
|||
C:\>luhn86 1234567812345670 |
|||
Pass</pre> |
|||
=={{header|8th}}== |
|||
<syntaxhighlight lang="forth"> |
|||
\ Adapted from the C version: |
|||
: remap \ n1 -- n2 |
|||
[0,2,4,6,8,1,3,5,7,9] |
|||
swap caseof ; |
|||
: luhn \ s -- f |
|||
0 swap |
|||
s:rev |
|||
( |
|||
'0 n:- |
|||
swap 2 n:mod if remap then |
|||
n:+ |
|||
) s:each |
|||
10 n:mod not ; |
|||
: test-luhn \ s -- |
|||
dup . space |
|||
luhn if "OK" else "FAIL" then . cr ; |
|||
"49927398716" test-luhn |
|||
"49927398717" test-luhn |
|||
"1234567812345678" test-luhn |
|||
"1234567812345670" test-luhn |
|||
bye</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
49927398716 OK |
|||
49927398717 FAIL |
|||
1234567812345678 FAIL |
|||
1234567812345670 OK |
|||
</pre> |
|||
=={{header|ABAP}}== |
|||
<syntaxhighlight 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.</syntaxhighlight> |
|||
=={{header|ACL2}}== |
|||
<syntaxhighlight lang="lisp">(include-book "arithmetic-3/top" :dir :system) |
|||
(defun digits (n) |
|||
(if (zp n) |
|||
nil |
|||
(cons (mod n 10) |
|||
(digits (floor n 10))))) |
|||
(defun sum (xs) |
|||
(if (endp xs) |
|||
0 |
|||
(+ (first xs) |
|||
(sum (rest xs))))) |
|||
(defun double-and-sum-digits (xs) |
|||
(if (endp xs) |
|||
nil |
|||
(cons (sum (digits (* 2 (first xs)))) |
|||
(double-and-sum-digits (rest xs))))) |
|||
(defun dmx (xs) |
|||
(if (endp (rest xs)) |
|||
(mv xs nil) |
|||
(mv-let (odds evens) |
|||
(dmx (rest (rest xs))) |
|||
(mv (cons (first xs) odds) |
|||
(cons (second xs) evens))))) |
|||
(defun luhn (n) |
|||
(mv-let (odds evens) |
|||
(dmx (digits n)) |
|||
(= (mod (+ (sum odds) |
|||
(sum (double-and-sum-digits evens))) |
|||
10) |
|||
0)))</syntaxhighlight> |
|||
{{out}} |
|||
<pre>> (luhn 49927398716) |
|||
T |
|||
> (luhn 49927398717) |
|||
NIL |
|||
> (luhn 1234567812345678) |
|||
NIL |
|||
> (luhn 1234567812345670) |
|||
T</pre> |
|||
=={{header|Action!}}== |
|||
<syntaxhighlight lang="action!">PROC ReverseDigits(CHAR ARRAY n,rev) |
|||
BYTE i,j |
|||
i=n(0) |
|||
WHILE i>0 AND n(i)='0 |
|||
DO |
|||
i==-1 |
|||
OD |
|||
j=1 |
|||
WHILE i>0 |
|||
DO |
|||
rev(j)=n(i) |
|||
j==+1 i==-1 |
|||
OD |
|||
rev(0)=j-1 |
|||
RETURN |
|||
BYTE FUNC SumOddDigits(CHAR ARRAY n) |
|||
BYTE sum,i |
|||
sum=0 |
|||
FOR i=1 TO n(0) STEP 2 |
|||
DO |
|||
sum==+ValB(n(i)) |
|||
OD |
|||
RETURN(sum) |
|||
BYTE FUNC SumEvenDigitsMultiplied(CHAR ARRAY n) |
|||
BYTE sum,i,v |
|||
sum=0 |
|||
FOR i=2 TO n(0) STEP 2 |
|||
DO |
|||
v=ValB(n(i))*2 |
|||
IF v>9 THEN v==-9 FI |
|||
sum==+v |
|||
OD |
|||
RETURN(sum) |
|||
BYTE FUNC Luhn(CHAR ARRAY n) |
|||
CHAR ARRAY rev(20) |
|||
BYTE s1,s2 |
|||
ReverseDigits(n,rev) |
|||
s1=SumOddDigits(rev) |
|||
s2=SumEvenDigitsMultiplied(rev) |
|||
IF (s1+s2) MOD 10=0 THEN |
|||
RETURN(1) |
|||
FI |
|||
RETURN(0) |
|||
PROC Test(CHAR ARRAY n) |
|||
PrintF("%S is ",n) |
|||
IF Luhn(n) THEN |
|||
PrintE("valid") |
|||
ELSE |
|||
PrintE("invalid") |
|||
FI |
|||
RETURN |
|||
PROC Main() |
|||
Test("49927398716") |
|||
Test("49927398717") |
|||
Test("1234567812345678") |
|||
Test("1234567812345670") |
|||
RETURN</syntaxhighlight> |
|||
{{out}} |
|||
[https://gitlab.com/amarok8bit/action-rosetta-code/-/raw/master/images/Luhn_test_of_credit_card_numbers.png Screenshot from Atari 8-bit computer] |
|||
<pre> |
|||
49927398716 is valid |
|||
49927398717 is invalid |
|||
1234567812345678 is invalid |
|||
1234567812345670 is valid |
|||
</pre> |
|||
=={{header|ActionScript}}== |
=={{header|ActionScript}}== |
||
< |
<syntaxhighlight lang="actionscript">function isValid(numString:String):Boolean |
||
{ |
{ |
||
var isOdd:Boolean = true; |
var isOdd:Boolean = true; |
||
Line 55: | Line 577: | ||
trace(isValid("49927398717")); |
trace(isValid("49927398717")); |
||
trace(isValid("1234567812345678")); |
trace(isValid("1234567812345678")); |
||
trace(isValid("1234567812345670")); |
trace(isValid("1234567812345670"));</syntaxhighlight> |
||
</lang> |
|||
=={{header|Ada}}== |
=={{header|Ada}}== |
||
{{trans|C}} |
|||
Same method as used in C code. |
|||
{{works with|GNAT}} |
|||
<syntaxhighlight lang="ada">with Ada.Text_IO; |
|||
<lang Ada> |
|||
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; |
|||
procedure Luhn is |
|||
function Luhn_Test (Number: String) return Boolean is |
|||
Sum : Natural := 0; |
|||
Odd : Boolean := True; |
|||
Digit: Natural range 0 .. 9; |
|||
begin |
|||
for p in reverse Number'Range loop |
|||
Digit := Integer'Value (Number (p..p)); |
|||
if Odd then |
|||
Sum := Sum + Digit; |
|||
else |
|||
Sum := Sum + (Digit*2 mod 10) + (Digit / 5); |
|||
end if; |
|||
Odd := not Odd; |
|||
end loop; |
|||
return (Sum mod 10) = 0; |
|||
end Luhn_Test; |
|||
begin |
begin |
||
put_line(Boolean'Image(luhn_test("49927398716"))); |
|||
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> |
|||
end Luhn;</syntaxhighlight> |
|||
Sample output: |
|||
{{out}} |
|||
<pre> |
<pre> |
||
TRUE |
TRUE |
||
Line 97: | Line 621: | ||
=={{header|ALGOL 68}}== |
=={{header|ALGOL 68}}== |
||
{{trans|C++ |
{{trans|C++|Note: This specimen retains the original C coding style. }} |
||
{{works with|ALGOL 68|Standard - no extensions to language used}} |
{{works with|ALGOL 68|Standard - no extensions to language used}} |
||
{{works with|ALGOL 68G|Any - tested with release [http://sourceforge.net/projects/algol68/files/algol68g/algol68g-1.18.0/algol68g-1.18.0-9h.tiny.el5.centos.fc11.i386.rpm/download 1.18.0-9h.tiny]}} |
{{works with|ALGOL 68G|Any - tested with release [http://sourceforge.net/projects/algol68/files/algol68g/algol68g-1.18.0/algol68g-1.18.0-9h.tiny.el5.centos.fc11.i386.rpm/download 1.18.0-9h.tiny]}} |
||
{{works with|ELLA ALGOL 68|Any (with appropriate job cards)}} |
{{works with|ELLA ALGOL 68|Any (with appropriate job cards)}} |
||
< |
<syntaxhighlight lang="algol68">PROC to int = (CHAR c)INT: |
||
ABS c - ABS "0"; |
ABS c - ABS "0"; |
||
Line 137: | Line 659: | ||
print((cp, ": ", confirm(cp), new line)) |
print((cp, ": ", confirm(cp), new line)) |
||
OD |
OD |
||
)</ |
)</syntaxhighlight> |
||
{{out}} |
|||
Output: |
|||
<pre> |
<pre> |
||
49927398716: T |
49927398716: T |
||
Line 144: | Line 666: | ||
1234567812345678: F |
1234567812345678: F |
||
1234567812345670: T |
1234567812345670: T |
||
</pre> |
|||
=={{header|ALGOL W}}== |
|||
Separate source so the LuhnTest procedure can be used in other tasks, e.g.: [[Validate International Securities Identification Number]] |
|||
<syntaxhighlight lang="algolw">% returns true if ccNumber passes the Luhn test, false otherwise % |
|||
% as Algol W has fixed length strings, the length of the number % |
|||
% must be specified in ccLength % |
|||
logical procedure LuhnTest ( string(32) value ccNumber |
|||
; integer value ccLength |
|||
) ; |
|||
begin |
|||
integer checkSum; |
|||
logical oddDigit, isValid; |
|||
checkSum := 0; |
|||
isValid := oddDigit := true; |
|||
for cPos := ccLength step -1 until 1 do begin |
|||
integer digit; |
|||
digit := decode( ccNumber( cPos - 1 // 1 ) ) - decode( "0" ); |
|||
if digit < 0 or digit > 9 then isValid := false |
|||
else if oddDigit |
|||
then checkSum := checkSum + digit |
|||
else checkSum := checkSum + ( case digit + 1 of ( 0, 2, 4, 6, 8 |
|||
, 1, 3, 5, 7, 9 |
|||
) |
|||
); |
|||
oddDigit := not oddDigit |
|||
end for_cPos ; |
|||
isValid and ( ( checkSum rem 10 ) = 0 ) |
|||
end LuhnTest</syntaxhighlight> |
|||
Use the above to test the LuhnTest procedure: |
|||
<syntaxhighlight lang="algolw">begin |
|||
% external procedure that returns true if ccNumber passes the Luhn test, false otherwise % |
|||
logical procedure LuhnTest ( string(32) value ccNumber |
|||
; integer value ccLength |
|||
) ; algol "LUHN" ; |
|||
% task test cases % |
|||
procedure testLuhnTest ( string(32) value ccNumber |
|||
; integer value ccLength |
|||
) ; |
|||
write( s_w := 0, ccNumber, if LuhnTest( ccNumber, ccLength ) then " is valid" else " is invalid" ); |
|||
testLuhnTest( "49927398716", 11 ); |
|||
testLuhnTest( "49927398717", 11 ); |
|||
testLuhnTest( "1234567812345678", 16 ); |
|||
testLuhnTest( "1234567812345670", 16 ) |
|||
end.</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
49927398716 is valid |
|||
49927398717 is invalid |
|||
1234567812345678 is invalid |
|||
1234567812345670 is valid |
|||
</pre> |
</pre> |
||
=={{header|APL}}== |
=={{header|APL}}== |
||
{{works with|Dyalog APL}} |
{{works with|Dyalog APL}} |
||
<syntaxhighlight lang="apl">LuhnTest←{ |
|||
<lang APL> |
|||
digits←⍎¨⍵ ⍝ Characters to digits |
|||
r←LuhnTest digits;even;odd;SumEven |
|||
doubled←2∘×@(⌽⍤~1 0⍴⍨≢)⊢digits ⍝ Double every other digit |
|||
(odd even)←↓⍉⍎¨{(n,2)⍴(⍵,'0')↑⍨2×n←⌈(⍴⍵)÷2}⌽digits |
|||
partial←-∘9@(9∘<)⊢doubled ⍝ Subtract 9 is equivalent to sum of digits for the domain 10≤x≤19 |
|||
SumEven←{+/+/¨⍎¨¨⍕¨2×⍵} |
|||
0=10|+/partial ⍝ Valid if sum is a multiple of 10 |
|||
r←'0'=⊃¯1↑⍕(+/odd)+(SumEven even) |
|||
}</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
|||
Sample Output: |
|||
<pre> |
<pre> |
||
LuhnTest¨'49927398716' '49927398717' '1234567812345678' '1234567812345670' |
|||
1 0 0 1 |
|||
1 0 0 1 |
|||
</pre> |
</pre> |
||
{{works with|APL+Win}} |
{{works with|APL+Win}} |
||
< |
<syntaxhighlight lang="apl"> ∇ ret←LuhnTest num;s1;s2 |
||
[1] num←⌽((⌈10⍟num)/10)⊤num |
[1] num←⌽((⌈10⍟num)/10)⊤num |
||
[2] s1←+/((⍴num)⍴1 0)/num |
[2] s1←+/((⍴num)⍴1 0)/num |
||
[3] s2←+/∊(⊂10 10)⊤¨2×((⍴num)⍴0 1)/num |
[3] s2←+/∊(⊂10 10)⊤¨2×((⍴num)⍴0 1)/num |
||
[4] ret←0=10⊤s1+s2 |
[4] ret←0=10⊤s1+s2 |
||
∇ |
∇</syntaxhighlight> |
||
{{out}} |
|||
</lang> |
|||
Sample Output: |
|||
<pre> LuhnTest¨ 49927398716 49927398717 1234567812345678 1234567812345670 |
<pre> LuhnTest¨ 49927398716 49927398717 1234567812345678 1234567812345670 |
||
1 0 0 1 |
1 0 0 1 |
||
</pre> |
</pre> |
||
=={{header|AppleScript}}== |
|||
===Functional=== |
|||
<syntaxhighlight lang="applescript">-- luhn :: String -> Bool |
|||
on luhn(s) |
|||
-- True if the digit string represents |
|||
-- a valid Luhn credit card number. |
|||
script divMod10Sum |
|||
on |λ|(a, x) |
|||
a + x div 10 + x mod 10 |
|||
end |λ| |
|||
end script |
|||
0 = foldl(divMod10Sum, 0, ¬ |
|||
zipWith(my mul, ¬ |
|||
map(my int, reverse of (characters of s)), ¬ |
|||
cycle({1, 2}))) mod 10 |
|||
end luhn |
|||
--------------------------- TEST --------------------------- |
|||
on run |
|||
map(luhn, ¬ |
|||
{"49927398716", "49927398717", ¬ |
|||
"1234567812345678", "1234567812345670"}) |
|||
--> {true, false, false, true} |
|||
end run |
|||
---------------- REUSABLE GENERIC FUNCTIONS ---------------- |
|||
-- cycle :: [a] -> Generator [a] |
|||
on cycle(xs) |
|||
script |
|||
property lng : 1 + (length of xs) |
|||
property i : missing value |
|||
on |λ|() |
|||
if missing value is i then |
|||
set i to 1 |
|||
else |
|||
set nxt to (1 + i) mod lng |
|||
if 0 = ((1 + i) mod lng) then |
|||
set i to 1 |
|||
else |
|||
set i to nxt |
|||
end if |
|||
end if |
|||
return item i of xs |
|||
end |λ| |
|||
end script |
|||
end cycle |
|||
-- foldl :: (a -> b -> a) -> a -> [b] -> a |
|||
on foldl(f, startValue, xs) |
|||
tell mReturn(f) |
|||
set v to startValue |
|||
set lng to length of xs |
|||
repeat with i from 1 to lng |
|||
set v to |λ|(v, item i of xs, i, xs) |
|||
end repeat |
|||
return v |
|||
end tell |
|||
end foldl |
|||
-- int :: String -> Int |
|||
on int(s) |
|||
s as integer |
|||
end int |
|||
-- length :: [a] -> Int |
|||
on |length|(xs) |
|||
set c to class of xs |
|||
if list is c or string is c then |
|||
length of xs |
|||
else |
|||
(2 ^ 29 - 1) -- (maxInt - simple proxy for non-finite) |
|||
end if |
|||
end |length| |
|||
-- mReturn :: First-class m => (a -> b) -> m (a -> b) |
|||
on mReturn(f) |
|||
-- 2nd class handler function lifted into 1st class script wrapper. |
|||
if script is class of f then |
|||
f |
|||
else |
|||
script |
|||
property |λ| : f |
|||
end script |
|||
end if |
|||
end mReturn |
|||
-- map :: (a -> b) -> [a] -> [b] |
|||
on map(f, xs) |
|||
-- The list obtained by applying f |
|||
-- to each element of xs. |
|||
tell mReturn(f) |
|||
set lng to length of xs |
|||
set lst to {} |
|||
repeat with i from 1 to lng |
|||
set end of lst to |λ|(item i of xs, i, xs) |
|||
end repeat |
|||
return lst |
|||
end tell |
|||
end map |
|||
-- min :: Ord a => a -> a -> a |
|||
on min(x, y) |
|||
if y < x then |
|||
y |
|||
else |
|||
x |
|||
end if |
|||
end min |
|||
-- mul (*) :: Num a => a -> a -> a |
|||
on mul(a, b) |
|||
a * b |
|||
end mul |
|||
-- take :: Int -> [a] -> [a] |
|||
-- take :: Int -> String -> String |
|||
on take(n, xs) |
|||
set c to class of xs |
|||
if list is c then |
|||
if 0 < n then |
|||
items 1 thru min(n, length of xs) of xs |
|||
else |
|||
{} |
|||
end if |
|||
else if string is c then |
|||
if 0 < n then |
|||
text 1 thru min(n, length of xs) of xs |
|||
else |
|||
"" |
|||
end if |
|||
else if script is c then |
|||
set ys to {} |
|||
repeat with i from 1 to n |
|||
set v to |λ|() of xs |
|||
if missing value is v then |
|||
return ys |
|||
else |
|||
set end of ys to v |
|||
end if |
|||
end repeat |
|||
return ys |
|||
else |
|||
missing value |
|||
end if |
|||
end take |
|||
-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] |
|||
on zipWith(f, xs, ys) |
|||
set lng to min(|length|(xs), |length|(ys)) |
|||
if 1 > lng then return {} |
|||
set xs_ to take(lng, xs) -- Allow for non-finite |
|||
set ys_ to take(lng, ys) -- generators like cycle etc |
|||
set lst to {} |
|||
tell mReturn(f) |
|||
repeat with i from 1 to lng |
|||
set end of lst to |λ|(item i of xs_, item i of ys_) |
|||
end repeat |
|||
return lst |
|||
end tell |
|||
end zipWith</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>{true, false, false, true}</pre> |
|||
===Straightforward=== |
|||
<syntaxhighlight lang="applescript">on luhnTest(n) |
|||
-- Accept only text input. |
|||
if (n's class is not text) then return false |
|||
-- Edit out any spaces or dashes. |
|||
set astid to AppleScript's text item delimiters |
|||
set AppleScript's text item delimiters to {space, "-"} |
|||
set n to n's text items |
|||
set AppleScript's text item delimiters to "" |
|||
set n to n as text |
|||
set AppleScript's text item delimiters to astid |
|||
-- Check that what's left is numeric. |
|||
try |
|||
n as number |
|||
on error |
|||
return false |
|||
end try |
|||
-- Do the calculation two digits at a time, starting at the end of the text and working back. |
|||
set sum to 0 |
|||
repeat with i from ((count n) - 1) to 1 by -2 |
|||
set n2 to (text i thru (i + 1) of n) as integer |
|||
tell n2 div 10 mod 10 * 2 to set sum to sum + it div 10 + it mod 10 + n2 mod 10 |
|||
end repeat |
|||
-- If there's an odd digit left over, add that in too. |
|||
if (i is 2) then set sum to sum + (character 1 of n) |
|||
return (sum mod 10 is 0) |
|||
end luhnTest |
|||
-- Test code: |
|||
set testResults to {} |
|||
repeat with testNumber in {"49927398716", "49927398717", "1234567812345678", "1234567812345670"} |
|||
set end of testResults to {testNumber:testNumber's contents, valid:luhnTest(testNumber)} |
|||
end repeat |
|||
return testResults</syntaxhighlight> |
|||
{{output}} |
|||
<syntaxhighlight lang="applescript">{{testNumber:"49927398716", valid:true}, {testNumber:"49927398717", valid:false}, {testNumber:"1234567812345678", valid:false}, {testNumber:"1234567812345670", valid:true}}</syntaxhighlight> |
|||
=={{header|ARM Assembly}}== |
|||
<syntaxhighlight lang="arm_assembly">.text |
|||
.global _start |
|||
_start: |
|||
ldr r0, =example_numbers |
|||
bl test_number |
|||
add r1, r0, #1 |
|||
bl length |
|||
add r0, r1, r0 |
|||
bl test_number |
|||
add r1, r0, #1 |
|||
bl length |
|||
add r0, r1, r0 |
|||
bl test_number |
|||
add r1, r0, #1 |
|||
bl length |
|||
add r0, r1, r0 |
|||
bl test_number |
|||
mov r0, #0 |
|||
mov r7, #1 |
|||
swi 0 |
|||
test_number: |
|||
push {r0, lr} |
|||
bl print_string |
|||
bl luhn_test |
|||
cmp r0, #1 |
|||
ldreq r0, =valid_message |
|||
ldrne r0, =invalid_message |
|||
bl print_string |
|||
pop {r0, lr} |
|||
mov pc, lr |
|||
print_string: |
|||
push {r0-r7, lr} |
|||
mov r1, r0 @ string to print |
|||
bl length |
|||
mov r2, r0 @ length of string |
|||
mov r0, #1 @ write to stdout |
|||
mov r7, #4 @ SYS_WRITE |
|||
swi 0 @ call system interupt |
|||
pop {r0-r7, lr} |
|||
mov pc, lr |
|||
@ r0 address of credit card number string |
|||
@ returns result in r0 |
|||
luhn_test: |
|||
push {r1-r7, lr} |
|||
mov r1, r0 |
|||
bl isNumerical @ check if string is a number |
|||
cmp r0, #1 |
|||
bne .luhn_test_end @ exit if not number |
|||
mov r0, r1 |
|||
ldr r1, =reversed_string @ address to store reversed string |
|||
bl reverse @ reverse string |
|||
push {r0} |
|||
bl length @ get length of string |
|||
mov r4, r0 @ store string length in r4 |
|||
pop {r0} |
|||
mov r2, #0 @ string index |
|||
mov r6, #0 @ sum of odd digits |
|||
mov r7, #0 @ sum of even digits |
|||
.loadNext: |
|||
ldrb r3, [r1, r2] @ load byte into r3 |
|||
sub r3, #'0' @ convert letter to digit |
|||
and r5, r2, #1 @ test if index is even or odd |
|||
cmp r5, #0 |
|||
beq .odd_digit |
|||
bne .even_digit |
|||
.odd_digit: |
|||
add r6, r3 @ add digit to sum if odd |
|||
b .continue @ skip next step |
|||
.even_digit: |
|||
lsl r3, #1 @ multiply digit by 2 |
|||
cmp r3, #10 @ sum digits |
|||
subge r3, #10 @ get digit in 1s place |
|||
addge r3, #1 @ add 1 for the 10s place |
|||
add r7, r3 @ add digit sum to the total |
|||
.continue: |
|||
add r2, #1 @ increment digit index |
|||
cmp r2, r4 @ check if at end of string |
|||
blt .loadNext |
|||
add r0, r6, r7 @ add even and odd sum |
|||
mov r3, r0 @ copy sum to r3 |
|||
ldr r1, =429496730 @ (2^32-1)/10 |
|||
sub r0, r0, r0, lsr #30 @ divide by 10 |
|||
umull r2, r0, r1, r0 |
|||
mov r1, #10 |
|||
mul r0, r1 @ multiply the r0 by 10 to see if divisible |
|||
cmp r0, r3 @ compare with the original value in r3 |
|||
.luhn_test_end: |
|||
movne r0, #0 @ return false if invalid card number |
|||
moveq r0, #1 @ return true if valid card number |
|||
pop {r1-r7, lr} |
|||
mov pc, lr |
|||
length: |
|||
push {r1-r2, lr} |
|||
mov r2, r0 @ start of string address |
|||
.loop: |
|||
ldrb r1, [r2], #1 @ load byte from address r2 and increment |
|||
cmp r1, #0 @ check for end of string |
|||
bne .loop @ load next byte if not 0 |
|||
sub r0, r2, r0 @ subtract end of string address from start |
|||
sub r0, #1 @ end of line from count |
|||
pop {r1-r2, lr} |
|||
mov pc, lr |
|||
@ reverses a string |
|||
@ r0 address of string to reverse |
|||
@ r1 address to store reversed string |
|||
reverse: |
|||
push {r0-r5, lr} |
|||
push {r0, lr} |
|||
bl length @ get length of string to reverse |
|||
mov r3, r0 @ backword index |
|||
pop {r0, lr} |
|||
mov r4, #0 @ fowrard index |
|||
.reverse_next: |
|||
sub r3, #1 @ decrement backword index |
|||
ldrb r5, [r0, r3] @ load byte from original string at index |
|||
strb r5, [r1, r4] @ copy byte to reversed string |
|||
add r4, #1 @ increment fowrard index |
|||
cmp r3, #0 @ check if any characters are left |
|||
bge .reverse_next |
|||
mov r5, #0 |
|||
strb r5, [r1, r4] @ write null byte to terminate reversed string |
|||
pop {r0-r5, lr} |
|||
mov pc, lr |
|||
isNumerical: |
|||
push {r1, lr} |
|||
.isNumerical_checkNext: |
|||
ldrb r1, [r0], #1 |
|||
cmp r1, #0 |
|||
beq .isNumerical_true |
|||
cmp r1, #'0' |
|||
blt .isNumerical_false |
|||
cmp r1, #'9' |
|||
bgt .isNumerical_false |
|||
b .isNumerical_checkNext |
|||
.isNumerical_false: |
|||
mov r0, #0 |
|||
b .isNumerical_end |
|||
.isNumerical_true: |
|||
mov r0, #1 |
|||
.isNumerical_end: |
|||
pop {r1, lr} |
|||
mov pc, lr |
|||
.data |
|||
valid_message: |
|||
.asciz " valid card number\n" |
|||
invalid_message: |
|||
.asciz " invalid card number\n" |
|||
reversed_string: |
|||
.space 32 |
|||
example_numbers: |
|||
.asciz "49927398716" |
|||
.asciz "49927398717" |
|||
.asciz "1234567812345678" |
|||
.asciz "1234567812345670" </syntaxhighlight> |
|||
=={{header|Arturo}}== |
|||
<syntaxhighlight lang="rebol">digits: function [n][ |
|||
res: new [] |
|||
while -> n > 0 [ |
|||
'res ++ n % 10 |
|||
n: n / 10 |
|||
] |
|||
res |
|||
] |
|||
luhn?: function [n][ |
|||
s1: new 0 |
|||
s2: new 0 |
|||
loop.with: 'i digits n 'd [ |
|||
if? even? i -> 's1 + d |
|||
else [ |
|||
'd * 2 |
|||
if d > 9 -> 'd - 9 |
|||
's2 + d |
|||
] |
|||
] |
|||
zero? (s1 + s2) % 10 |
|||
] |
|||
print luhn? 49927398716 |
|||
print luhn? 49927398717 |
|||
print luhn? 1234567812345678 |
|||
print luhn? 1234567812345670 |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>true |
|||
false |
|||
false |
|||
true</pre> |
|||
=={{header|AutoHotkey}}== |
=={{header|AutoHotkey}}== |
||
< |
<syntaxhighlight lang="autohotkey">; Originally submitted by Laszlo: |
||
; http://www.autohotkey.com/forum/post-229412.html#229412 |
; http://www.autohotkey.com/forum/post-229412.html#229412 |
||
Line 190: | Line 1,195: | ||
Sum += ( ( 9 < ( Temp := MultFactor * A_LoopField ) ) ? Temp - 9 : Temp ) , MultFactor := 3 - MultFactor |
Sum += ( ( 9 < ( Temp := MultFactor * A_LoopField ) ) ? Temp - 9 : Temp ) , MultFactor := 3 - MultFactor |
||
Return !Mod(Sum,10) |
Return !Mod(Sum,10) |
||
}</ |
}</syntaxhighlight> |
||
=={{header| |
=={{header|AutoIt}}== |
||
<syntaxhighlight lang="autoit"> |
|||
<lang c>#include <string.h> |
|||
Global $avarray[4] = [49927398716, 49927398717, 1234567812345678, 1234567812345670] |
|||
#include <stdio.h> |
|||
For $i = 0 To 3 |
|||
checkLuhn($avarray[$i]) |
|||
Next |
|||
Func checkLuhn($number) |
|||
int luhn(const char* cc) |
|||
$sum = 0 |
|||
$numDigits = StringSplit($number, "") |
|||
For $i = $numDigits[0] - 1 To 1 Step -2 |
|||
$numDigits[$i] = $numDigits[$i] * 2 |
|||
If $numDigits[$i] >= 10 Then $numDigits[$i] -= 9 |
|||
Next |
|||
For $i = 1 To $numDigits[0] |
|||
$sum += $numDigits[$i] |
|||
Next |
|||
If StringRight($sum, 1) = "0" Then |
|||
ConsoleWrite("Luhn-Check (" & $number & ") : True" & @CRLF) |
|||
Return True |
|||
Else |
|||
ConsoleWrite("Luhn-Check (" & $number & ") : False" & @CRLF) |
|||
Return False |
|||
EndIf |
|||
EndFunc ;==>checkLuhn |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Luhn-Check (49927398716) : True |
|||
Luhn-Check (49927398717) : False |
|||
Luhn-Check (1234567812345678) : False |
|||
Luhn-Check (1234567812345670) : True</pre> |
|||
=={{header|AWK}}== |
|||
<syntaxhighlight lang="awk">#!/usr/bin/awk -f |
|||
BEGIN { |
|||
A[1] = 49927398716; |
|||
A[2] = 49927398717; |
|||
A[3] = 1234567812345678; |
|||
A[4] = 1234567812345670; |
|||
A[5] = "01234567897"; |
|||
A[6] = "01234567890"; |
|||
A[7] = "00000000000"; |
|||
for (k in A) print "isLuhn("A[k]"): ",isLuhn(A[k]); |
|||
} |
|||
function isLuhn(cardno) { |
|||
s = 0; |
|||
m = "0246813579"; |
|||
n = length(cardno); |
|||
for (k = n; 0 < k; k -= 2) { |
|||
s += substr(cardno, k, 1); |
|||
} |
|||
for (k = n-1; 0 < k; k -= 2) { |
|||
s += substr(m, substr(cardno, k, 1)+1, 1); |
|||
} |
|||
return ((s%10)==0); |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>isLuhn(1234567812345670): 1 |
|||
isLuhn(01234567897): 1 |
|||
isLuhn(01234567890): 0 |
|||
isLuhn(00000000000): 1 |
|||
isLuhn(49927398716): 1 |
|||
isLuhn(49927398717): 0 |
|||
isLuhn(1234567812345678): 0</pre> |
|||
=={{header|Bash}}== |
|||
<syntaxhighlight lang="bash">#!/bin/bash |
|||
function luhn_validate # <numeric-string> |
|||
{ |
{ |
||
num=$1 |
|||
shift 1 |
|||
unsigned int oddSum = 0; |
|||
unsigned int evenSum = 0; |
|||
int i; |
|||
len=${#num} |
|||
for (i = strlen(cc) - 1; i >= 0; --i) { |
|||
is_odd=1 |
|||
unsigned int digit = cc[i] - '0'; |
|||
sum=0 |
|||
for((t = len - 1; t >= 0; --t)) { |
|||
digit=${num:$t:1} |
|||
evenSum += digit / 5 + (2 * digit) % 10; |
|||
if [[ $is_odd -eq 1 ]]; then |
|||
sum=$(( sum + $digit )) |
|||
else |
|||
sum=$(( $sum + ( $digit != 9 ? ( ( 2 * $digit ) % 9 ) : 9 ) )) |
|||
fi |
|||
is_odd=$(( ! $is_odd )) |
|||
} |
} |
||
isOdd = !isOdd; |
|||
# NOTE: returning exit status of 0 on success |
|||
} |
|||
return $(( 0 != ( $sum % 10 ) )) |
|||
return (oddSum + evenSum) % 10 == 0; |
|||
} |
} |
||
int main() |
|||
function print_result # <numeric-string> |
|||
{ |
{ |
||
if luhn_validate "$1"; then |
|||
const char* cc_numbers[] = { |
|||
" |
echo "$1 is valid" |
||
else |
|||
echo "$1 is not valid" |
|||
"1234567812345678", |
|||
fi |
|||
"1234567812345670", |
|||
} |
|||
0 |
|||
}; |
|||
print_result "49927398716" |
|||
const char** cc_number; |
|||
print_result "49927398717" |
|||
print_result "1234567812345678" |
|||
print_result "1234567812345670"</syntaxhighlight> |
|||
{{out}} |
|||
for (cc_number = cc_numbers; *cc_number; ++cc_number) { |
|||
<pre>49927398716 is valid |
|||
printf("%s = %d\n", *cc_number, luhn(*cc_number)); |
|||
49927398717 is not valid |
|||
} |
|||
1234567812345678 is not valid |
|||
1234567812345670 is valid |
|||
</pre> |
|||
=={{header|BASIC}}== |
|||
return 0; |
|||
==={{header|BASIC256}}=== |
|||
} |
|||
<syntaxhighlight lang="freebasic">dim card$(5) |
|||
card$[1]="49927398716" |
|||
card$[2]="49927398717" |
|||
card$[3]="1234567812345678" |
|||
card$[4]="1234567812345670" |
|||
for test = 1 to 4 |
|||
</lang> |
|||
odd = True |
|||
sum = 0 |
|||
for n = length(card$[test]) to 1 step -1 |
|||
num = int(mid(card$[test],n,1)) |
|||
if odd then |
|||
sum += num |
|||
odd = False |
|||
else |
|||
num *= 2 |
|||
if num <= 9 then |
|||
sum += num |
|||
else |
|||
sum += int(left(string(num),1)) + int(right(string(num),1)) |
|||
end if |
|||
odd = True |
|||
end if |
|||
next |
|||
if sum mod 10 = 0 then |
|||
print card$[test], "True" |
|||
else |
|||
print card$[test], "False" |
|||
end if |
|||
next test</syntaxhighlight> |
|||
==={{header|Chipmunk Basic}}=== |
|||
{{works with|Chipmunk Basic|3.6.4}} |
|||
<syntaxhighlight lang="qbasic">100 cls |
|||
110 rem Luhn test |
|||
120 dim card$(5) |
|||
130 card$(1) = "49927398716" |
|||
140 card$(2) = "49927398717" |
|||
150 card$(3) = "1234567812345678" |
|||
160 card$(4) = "1234567812345670" |
|||
170 for test = 1 to 4 |
|||
180 odd = true |
|||
190 sum = 0 |
|||
200 for n = len(card$(test)) to 1 step -1 |
|||
210 num = val(mid$(card$(test),n,1)) |
|||
220 if odd then |
|||
230 sum = sum+num |
|||
240 odd = false |
|||
250 else |
|||
260 num = num*2 |
|||
270 if num <= 9 then |
|||
280 sum = sum+num |
|||
290 else |
|||
300 sum = sum+val(left$(str$(num),1))+val(right$(str$(num),1)) |
|||
310 endif |
|||
320 odd = true |
|||
330 endif |
|||
340 next |
|||
350 if sum mod 10 = 0 then |
|||
360 print card$(test),"True" |
|||
370 else |
|||
380 print card$(test),"False" |
|||
390 endif |
|||
400 next test</syntaxhighlight> |
|||
==={{header|IS-BASIC}}=== |
|||
<syntaxhighlight lang="is-basic">100 PROGRAM "CredCard.bas" |
|||
110 DO |
|||
120 PRINT :PRINT "Credit card number:":INPUT PROMPT ">":CCN$ |
|||
130 IF CCN$="" THEN EXIT DO |
|||
140 IF LUHN(TRIM$(CCN$)) THEN |
|||
150 PRINT "Card number is valid." |
|||
160 ELSE |
|||
170 SET #102:INK 3:PRINT "Card number is invalid.":SET #102:INK 1 |
|||
180 END IF |
|||
190 LOOP |
|||
200 DEF LUHN(CCN$) |
|||
210 LET L=LEN(CCN$):LET S=0 |
|||
220 FOR I=1 TO L |
|||
230 LET N=VAL(CCN$(L-I+1)) |
|||
240 IF I BAND 1 THEN |
|||
250 LET S=S+N |
|||
260 ELSE |
|||
270 LET N=N*2:LET S=S+MOD(N,10)+INT(N/10) |
|||
280 END IF |
|||
290 NEXT |
|||
300 LET LUHN=MOD(S,10)=0 |
|||
310 END DEF |
|||
320 DEF TRIM$(S$) |
|||
330 LET T$="" |
|||
340 FOR I=1 TO LEN(S$) |
|||
350 IF S$(I)>CHR$(47) AND S$(I)<CHR$(58) THEN LET T$=T$&S$(I) |
|||
360 NEXT |
|||
370 LET TRIM$=T$ |
|||
380 END DEF</syntaxhighlight> |
|||
Output: |
Output: |
||
<pre> |
<pre>Credit card number: |
||
>49927398716 |
|||
49927398717 = 0 |
|||
Card number is valid. |
|||
1234567812345678 = 0 |
|||
1234567812345670 = 1</pre> |
|||
Credit card number: |
|||
=={{header|C++}}== |
|||
>49927398717 |
|||
<lang cpp>#include <iostream> |
|||
Card number is invalid. |
|||
using namespace std; |
|||
Credit card number: |
|||
>1234 5678 1234 5678 |
|||
Card number is invalid. |
|||
Credit card number: |
|||
>1234 5678 1234 5670 |
|||
Card number is valid.</pre> |
|||
==={{header|QBasic}}=== |
|||
{{works with|QBasic|1.1}} |
|||
{{works with|QuickBasic|4.5}} |
|||
<syntaxhighlight lang="qbasic">CONST True = -1: False = NOT True |
|||
DIM card$(5) |
|||
card$(1) = "49927398716" |
|||
card$(2) = "49927398717" |
|||
card$(3) = "1234567812345678" |
|||
card$(4) = "1234567812345670" |
|||
FOR test = 1 TO 4 |
|||
odd = True |
|||
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 = False |
|||
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 = True |
|||
END IF |
|||
NEXT |
|||
IF sum MOD 10 = 0 THEN |
|||
PRINT card$(test), "True" |
|||
ELSE |
|||
PRINT card$(test), "False" |
|||
END IF |
|||
NEXT test</syntaxhighlight> |
|||
==={{header|True BASIC}}=== |
|||
<syntaxhighlight lang="qbasic">LET true = -1 |
|||
LET false = 0 |
|||
DIM card$(5) |
|||
LET card$(1) = "49927398716" |
|||
LET card$(2) = "49927398717" |
|||
LET card$(3) = "1234567812345678" |
|||
LET card$(4) = "1234567812345670" |
|||
FOR test = 1 TO 4 |
|||
LET odd = true |
|||
LET sum = 0 |
|||
FOR n = LEN(card$(test)) TO 1 STEP -1 |
|||
LET num = VAL((card$(test))[n:n+1-1]) |
|||
IF odd<>0 THEN |
|||
LET sum = sum + num |
|||
LET odd = false |
|||
ELSE |
|||
LET num = num*2 |
|||
IF num <= 9 THEN |
|||
LET sum = sum + num |
|||
ELSE |
|||
LET sum = sum + VAL((STR$(num))[1:1]) + VAL((STR$(num))[LEN(STR$(num))-1+1:maxnum]) |
|||
END IF |
|||
LET odd = true |
|||
END IF |
|||
NEXT n |
|||
IF remainder(round(sum),10) = 0 THEN PRINT card$(test), "True" ELSE PRINT card$(test), "False" |
|||
NEXT test |
|||
END</syntaxhighlight> |
|||
==={{header|uBasic/4tH}}=== |
|||
{{Trans|C}} |
|||
<syntaxhighlight lang="qbasic">Print " 49927398716", Show (Iif(FUNC(_Luhn ("49927398716")), "ok", "fail")) |
|||
Print " 49927398717", Show (Iif(FUNC(_Luhn ("49927398717")), "ok", "fail")) |
|||
Print "1234567812345678", Show (Iif(FUNC(_Luhn ("1234567812345678")), "ok", "fail")) |
|||
Print "1234567812345670", Show (Iif(FUNC(_Luhn ("1234567812345670")), "ok", "fail")) |
|||
End |
|||
_Luhn |
|||
Param (1) |
|||
Local (4) |
|||
c@ = 1 : d@ = 0 |
|||
For b@ = Len(a@)-1 To 0 Step -1 |
|||
e@ = Peek(a@, b@) - Ord("0") |
|||
d@ = d@ + Iif (c@, e@, e@+e@-9*(e@>4)) |
|||
c@ = c@ = 0 |
|||
Next |
|||
Return ((d@ % 10) = 0) |
|||
</syntaxhighlight> |
|||
{{Out}} |
|||
<pre> 49927398716 ok |
|||
49927398717 fail |
|||
1234567812345678 fail |
|||
1234567812345670 ok |
|||
0 OK, 0:333</pre> |
|||
==={{header|Yabasic}}=== |
|||
<syntaxhighlight lang="yabasic">dim card$(5) |
|||
card$(1)="49927398716" |
|||
card$(2)="49927398717" |
|||
card$(3)="1234567812345678" |
|||
card$(4)="1234567812345670" |
|||
for test = 1 to 4 |
|||
odd = true |
|||
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 = false |
|||
else |
|||
num = num * 2 |
|||
if num <= 9 then |
|||
sum = sum + num |
|||
else |
|||
sum = sum + val(left$(str$(num),1)) + val(right$(str$(num),1)) |
|||
fi |
|||
odd = true |
|||
fi |
|||
next |
|||
if mod(sum, 10) = 0 then |
|||
print card$(test), chr$(9), "True" |
|||
else |
|||
print card$(test), chr$(9), "False" |
|||
fi |
|||
next test</syntaxhighlight> |
|||
=={{header|Batch File}}== |
|||
This simple implementation does not reverse the numbers. Instead, it counts from right to left. |
|||
<syntaxhighlight lang="dos">@echo off |
|||
setlocal enabledelayedexpansion |
|||
call :luhn 49927398716 |
|||
call :luhn 49927398717 |
|||
call :luhn 1234567812345678 |
|||
call :luhn 1234567812345670 |
|||
exit /b 0 |
|||
:luhn |
|||
set "input=%1" |
|||
set "cnt=0" |
|||
set "s1=0" |
|||
set "s2=0" |
|||
:digit_loop |
|||
set /a "cnt-=1" |
|||
set /a "isOdd=(-%cnt%)%%2" |
|||
if !isodd! equ 1 ( |
|||
set /a "s1+=!input:~%cnt%,1!" |
|||
) else ( |
|||
set /a "twice=!input:~%cnt%,1!*2" |
|||
if !twice! geq 10 ( |
|||
set /a "s2+=!twice:~0,1!+!twice:~1,1!" |
|||
) else ( |
|||
set /a "s2+=!twice!" |
|||
) |
|||
) |
|||
if "!input:~%cnt%!"=="!input!" ( |
|||
set /a "sum=(!s1!+!s2!)%%10" |
|||
if !sum! equ 0 (echo !input! is valid.) else (echo !input! is not valid.) |
|||
goto :EOF |
|||
) |
|||
goto digit_loop</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>>luhn.bat |
|||
49927398716 is valid. |
|||
49927398717 is not valid. |
|||
1234567812345678 is not valid. |
|||
1234567812345670 is valid. |
|||
></pre> |
|||
=={{header|BBC BASIC}}== |
|||
<syntaxhighlight 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</syntaxhighlight> |
|||
=={{header|bc}}== |
|||
<syntaxhighlight lang="bc">/* Return 1 if number passes Luhn test, else 0 */ |
|||
define l(n) { |
|||
auto m, o, s, x |
|||
o = scale |
|||
scale = 0 |
|||
m = 1 |
|||
while (n > 0) { |
|||
x = (n % 10) * m |
|||
if (x > 9) x -= 9 |
|||
s += x |
|||
m = 3 - m |
|||
n /= 10 |
|||
} |
|||
s %= 10 |
|||
scale = o |
|||
if (s) return(0) |
|||
return(1) |
|||
} |
|||
l(49927398716) |
|||
l(49927398717) |
|||
l(1234567812345678) |
|||
l(1234567812345670)</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>1 |
|||
0 |
|||
0 |
|||
1</pre> |
|||
=={{header|BCPL}}== |
|||
<syntaxhighlight lang="bcpl">get "libhdr" |
|||
let luhn(s) = valof |
|||
$( let sum=0 and fac=1 |
|||
for i = s%0 to 1 by -1 |
|||
$( unless '0' <= s%i <= '9' resultis false |
|||
sum := sum + fac*(s%i - '0') rem 10 + fac*(s%i - '0')/10 |
|||
fac := 3 - fac |
|||
$) |
|||
resultis sum rem 10 = 0 |
|||
$) |
|||
let show(s) be |
|||
writef("%S: %S*N", s, luhn(s) -> "pass", "fail") |
|||
let start() be |
|||
$( show("49927398716") |
|||
show("49927398717") |
|||
show("1234567812345678") |
|||
show("1234567812345670") |
|||
$)</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716: pass |
|||
49927398717: fail |
|||
1234567812345678: fail |
|||
1234567812345670: pass</pre> |
|||
=={{header|Befunge}}== |
|||
<syntaxhighlight lang="befunge">v 1 >$0 v v < |
|||
>&:19+`|v < >v 5 6 7 8 |
|||
^ \ <>09p19p>09g+09p:|>2*:19+%19g+19p19+/19g+19p:| |
|||
2 3 4 > v |
|||
v"invalid"<10 9 |
|||
|%+91+g91g90< |
|||
v "valid"< |
|||
>:#,_@ |
|||
11 |
|||
</syntaxhighlight> |
|||
The labelled points (1 to 11) are: |
|||
1. Read in input until number greater than 10, |
|||
2. Reverse the order, |
|||
3. Set accumulators to 0, |
|||
4. Add odd number to accumulator, |
|||
5. Mod even number with 10, |
|||
6. Add this digit to accumulator, |
|||
7. Integer divide number by 10, |
|||
8. Add this digit to accumulator, |
|||
9. Add odd and even accumulators, |
|||
10. Mod this accumulator with 10, |
|||
11. Print result. |
|||
The code requires input be separated by spaces and ended with a number greater than 10 to exit the reading loop. This could be done by reading characters and ending at a new line, but this way is much simpler. |
|||
'''Inputs''': |
|||
<pre> |
|||
4 9 9 2 7 3 9 8 7 1 6 99 |
|||
4 9 9 2 7 3 9 8 7 1 7 99 |
|||
1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 99 |
|||
1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 0 99 |
|||
</pre> |
|||
{{out}} |
|||
<pre> |
|||
valid |
|||
invalid |
|||
invalid |
|||
valid |
|||
</pre> |
|||
=={{header|BQN}}== |
|||
<syntaxhighlight lang="bqn">Luhn ← (0=10|⊢)∘(+´(10|⊢)+⊢≥10˙)∘(⊢×≠⥊1‿2˙)∘(⌽•Fmt-'0'˙) |
|||
(⍉⊢≍Luhn¨) ⟨49927398716,49927398717,1234567812345678,1234567812345670⟩</syntaxhighlight> |
|||
{{out}} |
|||
<pre>┌─ |
|||
╵ 49927398716 1 |
|||
49927398717 0 |
|||
1234567812345678 0 |
|||
1234567812345670 1 |
|||
┘</pre> |
|||
=={{header|Bracmat}}== |
|||
<syntaxhighlight lang="bracmat"> ( luhn |
|||
= sum odd even |
|||
. 0:?sum |
|||
& rev$!arg:?arg |
|||
& whl |
|||
' ( @( !arg |
|||
: %?odd |
|||
( %?even ?arg |
|||
| :?arg&0:?even |
|||
) |
|||
) |
|||
& !odd+mod$(2*!even.10)+div$(!even.5)+!sum:?sum |
|||
) |
|||
& mod$(!sum.10):0 |
|||
) |
|||
& ( test |
|||
= |
|||
. out |
|||
$ (!arg ":" (luhn$!arg&true|false)) |
|||
) |
|||
& test$49927398716 |
|||
& test$49927398717 |
|||
& test$1234567812345678 |
|||
& test$1234567812345670 |
|||
& ;</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716 : true |
|||
49927398717 : false |
|||
1234567812345678 : false |
|||
1234567812345670 : true</pre> |
|||
=={{header|Brainf***}}== |
|||
<syntaxhighlight lang="text">>>>>>>>>>+>,----------[ READ CHARACTERS UNTIL \N AND |
|||
>++++++++[<----->-]<++>>>>>>+>,----------] SUBTRACT ASCII 0 FROM EACH |
|||
<-<<<<<<< GO TO LAST DIGIT |
|||
[ WHILE THERE ARE DIGITS |
|||
>>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<< ADD RUNNING TOTAL TO ODD DGT |
|||
<<<<<<< GO TO EVEN DIGIT |
|||
[ IF THERE IS ONE |
|||
>[->++<]>[-<+>] MUL BY TWO |
|||
>>>++++++++++<<<<[ DIVMOD BY TEN |
|||
>>>[-]+>-[<-<]<[<<] DECR DIVISOR |
|||
>>[ IF ZERO |
|||
>++++++++++>+<<-]<<< SET TO TEN; INCR QUOTIENT |
|||
-] DECR DIVIDEND UNTIL ZERO |
|||
++++++++++>>>>[-<<<<->>>>] CALCULATE REMAINDER |
|||
>[-<<<<<+>>>>>] ADD QUOTIENT TO IT |
|||
>>[-<<<<<<<+>>>>>>>]<<<<<<<< THEN ADD RUNNING TOTAL |
|||
<<<<< ZERO BEFORE NEXT ODD DIGIT |
|||
]<< GO TO NEXT ODD DIGIT |
|||
] |
|||
>>>>>>>-[+>>-]> GO TO TOTAL |
|||
>>>>++++++++++<<<<[ MODULO TEN |
|||
>>>[-]+>-[<-<]<[<<] DECR DIVISOR |
|||
>>[>++++++++++<-] IF ZERO SET BACK TO TEN |
|||
<<<-] DECR DIVIDEND UNTIL ZERO |
|||
>>>++++++++++>[-<->]< REMAINDER: TEN MINUS DIVISOR |
|||
<<<<<<++++++++++[-> VALUES FOR ASCII OUTPUT |
|||
+++++++++++> 110 |
|||
++++++++++> 100 |
|||
++++++++> 80 |
|||
+++++++<<<<] 70 |
|||
>>>>>+> GO BACK TO REMAINDER |
|||
[<<.<<---.<-----.+++.<] IF NOT ZERO FAIL |
|||
<[<<.<---.<+++++..<] IF ZERO PASS |
|||
++++++++++. NEWLINE |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>$ echo 49927398716 | beef luhn.bf |
|||
Pass |
|||
$ echo 49927398717 | beef luhn.bf |
|||
Fail |
|||
$ echo 1234567812345678 | beef luhn.bf |
|||
Fail |
|||
$ echo 1234567812345670 | beef luhn.bf |
|||
Pass</pre> |
|||
=={{header|Bruijn}}== |
|||
<syntaxhighlight lang="bruijn"> |
|||
:import std/Combinator . |
|||
:import std/Math . |
|||
:import std/List . |
|||
luhn number→list → reverse → check → (\mod (+10)) → zero? |
|||
check y [[[[0 [[[6 \5 (4 + (5 odd even)) 1]]] 1]]]] k (+0) |
|||
odd 2 |
|||
even digit-sum (2 ⋅ (+2)) |
|||
:test (luhn (+61789372994)) ([[1]]) |
|||
:test (luhn (+49927398716)) ([[1]]) |
|||
:test (luhn (+49927398717)) ([[0]]) |
|||
:test (luhn (+1234567812345678)) ([[0]]) |
|||
:test (luhn (+1234567812345670)) ([[1]]) |
|||
</syntaxhighlight> |
|||
=={{header|Burlesque}}== |
|||
<syntaxhighlight lang="text"> |
|||
tt "Remove whitespace"vv |
|||
pe "Eval to number"vv |
|||
<- "Reverse digits"vv |
|||
XX "Split number into digits"vv |
|||
int toInt(const char c) |
|||
{ |
{ |
||
{ "Odd digits"vv |
|||
return c-'0'; |
|||
2EN |
|||
} |
|||
{ "Even digits"vv |
|||
2en |
|||
{ |
|||
2.* "Double"vv |
|||
^^ 9.> "<test>=Duplicate greater than 9"vv |
|||
{ |
|||
XX++ "Sum digits"vv |
|||
}if "If <test>"vv |
|||
}m[ "For each even digit"vv |
|||
} |
|||
}M- "Cool map. Create array of each branch applied to argument."vv |
|||
{++}m[ "Sum each block (odd & even)"vv |
|||
++ "Sum these"vv |
|||
[- "Last digit"vv |
|||
0== "Equal to zero"vv |
|||
Q "Pretty print"vv |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
49927398716 1 |
|||
49927398717 0 |
|||
1234567812345678 0 |
|||
1234567812345670 1 |
|||
=={{header|C}}== |
|||
<syntaxhighlight lang="c">#include <string.h> |
|||
#include <stdio.h> |
|||
int luhn(const char* cc) |
|||
{ |
|||
const int m[] = {0,2,4,6,8,1,3,5,7,9}; // mapping for rule 3 |
|||
int i, odd = 1, sum = 0; |
|||
for (i = strlen(cc); i--; odd = !odd) { |
|||
int digit = cc[i] - '0'; |
|||
sum += odd ? digit : m[digit]; |
|||
} |
|||
return sum % 10 == 0; |
|||
} |
} |
||
int |
int main() |
||
{ |
{ |
||
const char* cc[] = { |
|||
bool is_odd_dgt = true; |
|||
"49927398716", |
|||
int s = 0; |
|||
"49927398717", |
|||
const char *cp; |
|||
"1234567812345678", |
|||
"1234567812345670", |
|||
0 |
|||
}; |
|||
int i; |
|||
for (i = 0; cc[i]; i++) |
|||
printf("%16s\t%s\n", cc[i], luhn(cc[i]) ? "ok" : "not ok"); |
|||
while(cp > id) { |
|||
--cp; |
|||
return 0; |
|||
int k = toInt(*cp); |
|||
}</syntaxhighlight>{{out}} |
|||
if (is_odd_dgt) { |
|||
49927398716 ok |
|||
49927398717 not ok |
|||
1234567812345678 not ok |
|||
1234567812345670 ok |
|||
=={{header|C sharp|C#}}== |
|||
The LuhnCheck method takes an array of integers because values in memory will be integer-aligned. |
|||
<syntaxhighlight lang="csharp"> |
|||
public static class Luhn |
|||
{ |
|||
public static bool LuhnCheck(this string cardNumber) |
|||
{ |
|||
return LuhnCheck(cardNumber.Select(c => c - '0').ToArray()); |
|||
} |
} |
||
else { |
|||
private static bool LuhnCheck(this int[] digits) |
|||
s += (k!=9)? (2*k)%9 : 9; |
|||
{ |
|||
return GetCheckValue(digits) == 0; |
|||
} |
|||
private static int GetCheckValue(int[] digits) |
|||
{ |
|||
return digits.Select((d, i) => i % 2 == digits.Length % 2 ? ((2 * d) % 10) + d / 5 : d).Sum() % 10; |
|||
} |
} |
||
is_odd_dgt = !is_odd_dgt; |
|||
} |
} |
||
return 0 == s%10; |
|||
} |
|||
public static class TestProgram |
|||
int main( ) |
|||
{ |
|||
{ |
|||
public static void Main() |
|||
const char * t_cases[] = { |
|||
{ |
|||
long[] testNumbers = {49927398716, 49927398717, 1234567812345678, 1234567812345670}; |
|||
foreach (var testNumber in testNumbers) |
|||
"1234567812345678", |
|||
Console.WriteLine("{0} is {1}valid", testNumber, testNumber.ToString().LuhnCheck() ? "" : "not "); |
|||
"1234567812345670", |
|||
} |
|||
}; |
|||
for ( const char **cp = t_cases; *cp; cp++) { |
|||
cout << *cp << ": " << confirm(*cp) << endl; |
|||
} |
} |
||
</syntaxhighlight> |
|||
return 0; |
|||
<pre> |
|||
49927398716 is valid |
|||
49927398717 is not valid |
|||
1234567812345678 is not valid |
|||
1234567812345670 is valid |
|||
</pre> |
|||
Note that the original implementation, which follows, is flawed because it assumes that n is a number which, when represented as a string, has an even number of characters. Granted, the brief is for Credit Card Numbers which are all, at the time of writing, an even number of digits. |
|||
=={{header|C sharp|C#}}== |
|||
<lang csharp>using System; |
|||
<syntaxhighlight lang="csharp">using System; |
|||
using System.Linq; |
|||
namespace Luhn |
namespace Luhn |
||
Line 312: | Line 1,988: | ||
} |
} |
||
return (sum % 10 == 0); |
return (sum % 10 == 0); |
||
} |
|||
public static bool luhnLinq(long n) |
|||
{ |
|||
string s = n.ToString(); |
|||
return s.Select((c, i) => (c - '0') << ((s.Length - i - 1) & 1)).Sum(n => n > 9 ? n - 9 : n) % 10 == 0; |
|||
} |
} |
||
Line 325: | Line 2,007: | ||
} |
} |
||
} |
} |
||
}</syntaxhighlight> |
|||
} |
|||
</lang> |
|||
<pre> |
<pre> |
||
49927398716 is valid |
49927398716 is valid |
||
Line 334: | Line 2,015: | ||
</pre> |
</pre> |
||
A solution without using LINQ, works for all versions of .NET. |
|||
=={{header|Objective-C}}== |
|||
<syntaxhighlight lang="csharp"> |
|||
<lang> |
|||
using System; |
|||
- (NSMutableArray *) toCharArray { |
|||
namespace Luhn_Test |
|||
{ |
|||
NSMutableArray *characters = [[NSMutableArray alloc] initWithCapacity:[self length]]; |
|||
public static class Extensions |
|||
for (int i=0; i < [self length]; i++) { |
|||
{ |
|||
NSString *ichar = [NSString stringWithFormat:@"%c", [self characterAtIndex:i]]; |
|||
public static string Reverse(this string s ) |
|||
[characters addObject:ichar]; |
|||
{ |
|||
char[] charArray = s.ToCharArray(); |
|||
Array.Reverse( charArray ); |
|||
return new string( charArray ); |
|||
} |
|||
} |
|||
class Program |
|||
{ |
|||
public static bool Luhn(long x) |
|||
{ |
|||
long s1=0; |
|||
long s2=0; |
|||
bool STATE=x%10!=0; // If it ends with zero, we want the order to be the other way around |
|||
x=long.Parse(x.ToString().Reverse()); |
|||
while (x!=0) |
|||
{ |
|||
s1+=STATE?x%10:0; |
|||
s2+=STATE?0:((x%10)*2>9)?(((x%10)*2/10)+((x%10)*2)%10):((x%10)*2); |
|||
STATE=!STATE; //Switch state |
|||
x/=10; //Cut the last digit and continue |
|||
} |
|||
return ((s1+s2)%10==0); //Check if it ends with zero, if so, return true, otherwise,false. |
|||
} |
|||
public static void Main(string[] args) |
|||
{ |
|||
long[] ks = {1234567812345670, 49927398717, 1234567812345678 ,1234567812345670 }; |
|||
foreach (long k in ks) |
|||
{ |
|||
Console.WriteLine("{0} is {1} Valid.",k,Luhn(k)?"":"Not"); |
|||
} |
|||
Start: |
|||
try { |
|||
Console.WriteLine("Enter your credit:"); |
|||
long x=long.Parse(Console.ReadLine()); |
|||
Console.WriteLine("{0} Valid.",Luhn(x)?"":"Not"); |
|||
goto Start; |
|||
} |
|||
catch (FormatException) |
|||
{ |
|||
goto Start; |
|||
} |
|||
} |
|||
} |
} |
||
return [characters autorelease]; |
|||
} |
} |
||
</syntaxhighlight> |
|||
<pre> |
|||
1234567812345670 is Valid. |
|||
49927398717 is Not Valid. |
|||
1234567812345678 is Not Valid. |
|||
49927398716 is Valid. |
|||
</pre> |
|||
A solution optimized for readability: |
|||
+ (BOOL) luhnCheck:(NSString *)stringToTest { |
|||
<syntaxhighlight lang="csharp"> |
|||
using System; |
|||
NSMutableArray *stringAsChars = [stringToTest toCharArray]; |
|||
using System.Linq; |
|||
BOOL isOdd = YES; |
|||
public class CreditCardLogic |
|||
int oddSum = 0; |
|||
{ |
|||
int evenSum = 0; |
|||
static Func<char, int> charToInt = c => c - '0'; |
|||
for (int i = [stringToTest length] - 1; i >= 0; i--) { |
|||
static Func<int, int> doubleDigit = n => (n * 2).ToString().Select(charToInt).Sum(); |
|||
int digit = [(NSString *)[stringAsChars objectAtIndex:i] intValue]; |
|||
static Func<int, bool> isOddIndex = index => index % 2 == 0; |
|||
if (isOdd) |
|||
public static bool LuhnCheck(string creditCardNumber) |
|||
oddSum += digit; |
|||
{ |
|||
else |
|||
var checkSum = creditCardNumber |
|||
evenSum += digit/5 + (2*digit) % 10; |
|||
.Select(charToInt) |
|||
.Reverse() |
|||
isOdd = !isOdd; |
|||
.Select((digit, index) => isOddIndex(index) ? digit : doubleDigit(digit)) |
|||
.Sum(); |
|||
return checkSum % 10 == 0; |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
Extremely compact version uses Europa rtl library https://github.com/CodeAlkemist/Europa-rtl |
|||
<syntaxhighlight lang="csharp"> |
|||
using System; |
|||
using EuropaRTL.Utilities; |
|||
public static partial class Algoritmhs |
|||
{ |
|||
public static bool CheckLuhn(long n) |
|||
{ |
|||
int s1 = n.Shatter(true).Subset(2).Arithmetic('+'); |
|||
int s2 = n.Shatter(true).Subset(1, -1, 2).ArithmeticRA('*', 2).ShatterAndSum().Arithmetic('+'); |
|||
return (s1 + s2) % 10 == 0 ? true : false; |
|||
} |
|||
} |
|||
class Program |
|||
{ |
|||
static void Main(string[] args) |
|||
{ |
|||
long[] ll = { |
|||
49927398716, |
|||
49927398717, |
|||
1234567812345678, |
|||
1234567812345670 |
|||
}; |
|||
foreach (var item in ll) |
|||
{ |
|||
item.ToString().WriteLine(); |
|||
Algoritmhs.CheckLuhn(item).ToString().WriteLine(); |
|||
} |
|||
Console.ReadKey(); |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
<pre> |
|||
49927398716 |
|||
True |
|||
49927398717 |
|||
False |
|||
1234567812345678 |
|||
False |
|||
1234567812345670 |
|||
False |
|||
</pre> |
|||
=={{header|C++}}== |
|||
<syntaxhighlight 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; |
|||
}</syntaxhighlight> |
|||
===C++11=== |
|||
<syntaxhighlight lang="cpp">#include <iostream> |
|||
#include <vector> |
|||
#include <algorithm> |
|||
using namespace std; |
|||
bool luhn( const string& id) |
|||
{ |
|||
static const int m[10] = {0,2,4,6,8,1,3,5,7,9}; // mapping for rule 3 |
|||
bool is_odd_dgt = false; |
|||
auto lambda = [&](int a, char c) {return a + ((is_odd_dgt = !is_odd_dgt) ? c-'0' : m[c-'0']);}; |
|||
int s = std::accumulate(id.rbegin(), id.rend(), 0, lambda); |
|||
return 0 == s%10; |
|||
} |
|||
int main( ) |
|||
{ |
|||
auto t_cases = {"49927398716", "49927398717", "1234567812345678", "1234567812345670"}; |
|||
auto print = [](const string & s) {cout << s << ": " << luhn(s) << endl;}; |
|||
for_each(t_cases.begin(), t_cases.end(), print); |
|||
return 0; |
|||
} |
|||
</syntaxhighlight> |
|||
It is also possible to achieve a compile-time version using metaprogramming. |
|||
<syntaxhighlight lang="cpp">#include <iostream> |
|||
#include <type_traits> |
|||
template<size_t I, int... Args> |
|||
struct find_impl; |
|||
template<int A, int... Args> |
|||
struct find_impl<0, A, Args...> { |
|||
using type = std::integral_constant<int, A>; |
|||
}; |
|||
template<int A, int B, int... Args> |
|||
struct find_impl<0, A, B, Args...> { |
|||
using type = std::integral_constant<int, A>; |
|||
}; |
|||
template<size_t I, int A, int B, int... Args> |
|||
struct find_impl<I, A, B, Args...> { |
|||
using type = typename find_impl<I-1, B, Args...>::type; |
|||
}; |
|||
namespace detail { |
|||
template<typename, typename> |
|||
struct append_sequence |
|||
{}; |
|||
template<typename T, typename... Ts> |
|||
struct append_sequence<T, std::tuple<Ts...>> { |
|||
using type = std::tuple<Ts..., T>; |
|||
}; |
|||
template<typename... Ts> |
|||
struct reverse_sequence { |
|||
using type = std::tuple<>; |
|||
}; |
|||
template<typename T, typename... Ts> |
|||
struct reverse_sequence<T, Ts...> { |
|||
using type = typename append_sequence< |
|||
T, |
|||
typename reverse_sequence<Ts...>::type |
|||
>::type; |
|||
}; |
|||
} |
|||
template<size_t I> |
|||
using rule3 = typename find_impl<I, 0, 2, 4, 6, 8, 1, 3, 5, 7, 9>::type; |
|||
template<int A, char C, bool dgt> |
|||
struct calc |
|||
: std::integral_constant<int, A + C - '0'> |
|||
{}; |
|||
template<int A, char C> |
|||
struct calc<A, C, false> |
|||
: std::integral_constant<int, A + rule3<C - '0'>::type::value> |
|||
{}; |
|||
template<typename Acc, bool Dgt, char...> |
|||
struct luhn_impl; |
|||
template<typename Acc, bool Dgt, char A, char... Args> |
|||
struct luhn_impl<Acc, Dgt, A, Args...> { |
|||
using type = typename calc<Acc::value, A, Dgt>::type; |
|||
}; |
|||
template<typename Acc, bool Dgt, char A, char B, char... Args> |
|||
struct luhn_impl<Acc, Dgt, A, B, Args...> { |
|||
using type = |
|||
typename luhn_impl<typename calc<Acc::value, A, Dgt>::type, !Dgt, B, Args...>::type; |
|||
}; |
|||
template<typename> |
|||
struct luhn; |
|||
template<typename... Args> |
|||
struct luhn<std::tuple<Args...>> { |
|||
using type = typename luhn_impl<std::integral_constant<int, 0>, true, Args::value...>::type; |
|||
constexpr static bool result = (type::value % 10) == 0; |
|||
}; |
|||
template<char... Args> |
|||
bool operator "" _luhn() { |
|||
return luhn<typename detail::reverse_sequence<std::integral_constant<char, Args>...>::type>::result; |
|||
} |
|||
int main() { |
|||
std::cout << std::boolalpha; |
|||
std::cout << 49927398716_luhn << std::endl; |
|||
std::cout << 49927398717_luhn << std::endl; |
|||
std::cout << 1234567812345678_luhn << std::endl; |
|||
std::cout << 1234567812345670_luhn << std::endl; |
|||
return 0; |
|||
} |
|||
</syntaxhighlight> |
|||
<pre> |
|||
true |
|||
false |
|||
false |
|||
true |
|||
</pre> |
|||
=={{header|Caché ObjectScript}}== |
|||
<syntaxhighlight lang="cos">Class Utils.Check [ Abstract ] |
|||
{ |
|||
ClassMethod Luhn(x As %String) As %Boolean |
|||
{ |
|||
// https://www.simple-talk.com/sql/t-sql-programming/calculating-and-verifying-check-digits-in-t-sql/ |
|||
SET x=$TRANSLATE(x," "), cd=$EXTRACT(x,*) |
|||
SET x=$REVERSE($EXTRACT(x,1,*-1)), t=0 |
|||
FOR i=1:1:$LENGTH(x) { |
|||
SET n=$EXTRACT(x,i) |
|||
IF i#2 SET n=n*2 IF $LENGTH(n)>1 SET n=$EXTRACT(n,1)+$EXTRACT(n,2) |
|||
SET t=t+n |
|||
} |
} |
||
QUIT cd=((t*9)#10) |
|||
return ((oddSum + evenSum) % 10 == 0); |
|||
} |
} |
||
}</syntaxhighlight> |
|||
BOOL test0 = [self luhnCheck:@"49927398716"]; //Result = YES |
|||
{{out|Examples}} |
|||
BOOL test1 = [self luhnCheck:@"49927398717"]; //Result = NO |
|||
<pre>USER>For { Read ccn Quit:ccn="" Write ": "_##class(Utils.Check).Luhn(ccn), ! } |
|||
BOOL test2 = [self luhnCheck:@"1234567812345678"]; //Result = NO |
|||
49927398716: 1 |
|||
BOOL test3 = [self luhnCheck:@"1234567812345670"]; //Result = YES |
|||
49927398717: 0 |
|||
</lang> |
|||
1234567812345678: 0 |
|||
=={{header|Clojure}}== |
|||
1234567812345670: 1 |
|||
<lang clojure>(defn- digits [n] |
|||
(map #(Character/digit % 10) (str n))) |
|||
USER></pre> |
|||
(defn luhn? [n] |
|||
(let [sum (reduce + (map |
|||
=={{header|Ceylon}}== |
|||
(fn [d idx] |
|||
<syntaxhighlight lang="ceylon">shared void run() { |
|||
(if (even? idx) |
|||
value numbers = "49927398716 |
|||
(reduce + (digits (* d 2))) |
|||
49927398717 |
|||
1234567812345678 |
|||
1234567812345670"; |
|||
for(number in numbers.lines) { |
|||
print("``number`` passes? ``luhn(number)``"); |
|||
} |
|||
} |
|||
shared Boolean luhn(String number) { |
|||
value digits = number |
|||
.reversed |
|||
.map(Character.string) |
|||
.map(Integer.parse) |
|||
.narrow<Integer>(); |
|||
value s1 = sum { 0, *digits.by(2) }; |
|||
value s2 = sum { |
|||
0, |
|||
*digits |
|||
.skip(1) |
|||
.by(2) |
|||
.map(curry(times<Integer>)(2)) |
|||
.map((Integer element) => element / 10 + element % 10) |
|||
}; |
|||
return (s1 + s2) % 10 == 0; |
|||
}</syntaxhighlight> |
|||
=={{header|Clojure}}== |
|||
<syntaxhighlight lang="clojure">(defn luhn? [cc] |
|||
(let [factors (cycle [1 2]) |
|||
numbers (map #(Character/digit % 10) cc) |
|||
sum (reduce + (map #(+ (quot % 10) (mod % 10)) |
|||
(map * (reverse numbers) factors)))] |
|||
(zero? (mod sum 10)))) |
(zero? (mod sum 10)))) |
||
(doseq [n [49927398716 49927398717 1234567812345678 1234567812345670]] |
(doseq [n ["49927398716" "49927398717" "1234567812345678" "1234567812345670"]] |
||
(println (luhn? n)))</ |
(println (luhn? n)))</syntaxhighlight> |
||
=={{header|CLU}}== |
|||
<syntaxhighlight lang="clu">luhn = proc (num: string) returns (bool) signals (bad_format) |
|||
total: int := 0 |
|||
even: bool := true |
|||
for i: int in int$from_to_by(string$size(num), 1, -1) do |
|||
digit: int := int$parse(string$c2s(num[i])) resignal bad_format |
|||
even := ~even |
|||
if even then |
|||
digit := 2 * digit |
|||
if digit >= 10 then digit := digit//10 + 1 end |
|||
end |
|||
total := total + digit |
|||
end |
|||
return(total // 10 = 0) |
|||
end luhn |
|||
start_up = proc () |
|||
po: stream := stream$primary_output() |
|||
tests: sequence[string] := sequence[string]$ |
|||
["49927398716", "49927398717", "1234567812345678", "1234567812345670"] |
|||
for test: string in sequence[string]$elements(tests) do |
|||
stream$puts(po, test || ": ") |
|||
if luhn(test) |
|||
then stream$putl(po, "pass") |
|||
else stream$putl(po, "fail") |
|||
end |
|||
end |
|||
end start_up</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716: pass |
|||
49927398717: fail |
|||
1234567812345678: fail |
|||
1234567812345670: pass</pre> |
|||
=={{header|COBOL}}== |
|||
{{works with|OpenCOBOL}} |
|||
{{works with|IBM Enterprise COBOL for z/OS}} |
|||
<syntaxhighlight lang="cobol"> IDENTIFICATION DIVISION. |
|||
PROGRAM-ID. LUHNTEST. |
|||
ENVIRONMENT DIVISION. |
|||
INPUT-OUTPUT SECTION. |
|||
data division. |
|||
WORKING-STORAGE SECTION. |
|||
01 inp-card. |
|||
03 inp-card-ch pic x(01) occurs 20 times. |
|||
01 ws-result pic 9(01). |
|||
88 pass-luhn-test value 0. |
|||
PROCEDURE DIVISION. |
|||
move "49927398716" to inp-card |
|||
perform test-card |
|||
move "49927398717" to inp-card |
|||
perform test-card |
|||
move "1234567812345678" to inp-card |
|||
perform test-card |
|||
move "1234567812345670" to inp-card |
|||
perform test-card |
|||
stop run |
|||
. |
|||
test-card. |
|||
call "LUHN" using inp-card, ws-result |
|||
if pass-luhn-test |
|||
display "input=" inp-card "pass" |
|||
else |
|||
display "input=" inp-card "fail" |
|||
. |
|||
END PROGRAM LUHNTEST. |
|||
IDENTIFICATION DIVISION. |
|||
PROGRAM-ID. LUHN. |
|||
ENVIRONMENT DIVISION. |
|||
INPUT-OUTPUT SECTION. |
|||
DATA DIVISION. |
|||
WORKING-STORAGE SECTION. |
|||
01 maxlen pic 9(02) comp value 16. |
|||
01 inplen pic 9(02) comp value 0. |
|||
01 i pic 9(02) comp value 0. |
|||
01 j pic 9(02) comp value 0. |
|||
01 l pic 9(02) comp value 0. |
|||
01 dw pic 9(02) comp value 0. |
|||
01 ws-total pic 9(03) comp value 0. |
|||
01 ws-prod pic 99. |
|||
01 filler redefines ws-prod. |
|||
03 ws-prod-tens pic 9. |
|||
03 ws-prod-units pic 9. |
|||
01 ws-card. |
|||
03 filler occurs 16 times depending on maxlen. |
|||
05 ws-card-ch pic x(01). |
|||
05 ws-card-digit redefines ws-card-ch pic 9(01). |
|||
LINKAGE SECTION. |
|||
01 inp-card. |
|||
03 inp-card-ch pic x(01) occurs 20 times. |
|||
01 ws-result pic 9(01). |
|||
88 pass-luhn-test value 0. |
|||
PROCEDURE DIVISION using inp-card, ws-result. |
|||
perform varying i from 1 by +1 |
|||
until i > maxlen |
|||
or inp-card-ch (i) = space |
|||
end-perform |
|||
compute l = i - 1 |
|||
compute inplen = l |
|||
perform varying j from 1 by +1 |
|||
until j > inplen |
|||
if l < 1 |
|||
move "0" to ws-card-ch (j) |
|||
else |
|||
move inp-card-ch (l) to ws-card-ch (j) |
|||
compute l = l - 1 |
|||
end-if |
|||
end-perform |
|||
move 0 to ws-total |
|||
perform varying i from 1 by +1 |
|||
until i > inplen |
|||
compute dw = 2 - (i - 2 * function integer (i / 2)) |
|||
compute ws-prod = ws-card-digit (i) * dw |
|||
compute ws-total = ws-total |
|||
+ ws-prod-tens |
|||
+ ws-prod-units |
|||
end-perform |
|||
compute ws-result = ws-total - 10 * function integer (ws-total / 10) |
|||
goback |
|||
. |
|||
END PROGRAM LUHN.</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
input=49927398716 pass |
|||
input=49927398717 fail |
|||
input=1234567812345678 fail |
|||
input=1234567812345670 pass |
|||
</pre> |
|||
=={{header|Comal}}== |
|||
<syntaxhighlight lang="comal">0010 FUNC luhn(s$) CLOSED |
|||
0020 total#:=0 |
|||
0030 even#:=TRUE |
|||
0040 FOR i#:=LEN(s$) TO 1 STEP -1 DO |
|||
0050 digit#:=VAL(s$(i#)) |
|||
0060 even#:=NOT even# |
|||
0070 IF even# THEN digit#:=(2*digit#) DIV 10+(2*digit#) MOD 10 |
|||
0080 total#:+digit# |
|||
0090 ENDFOR i# |
|||
0100 RETURN total# MOD 10=0 |
|||
0110 ENDFUNC luhn |
|||
0120 // |
|||
0130 PROC test(s$) |
|||
0140 PRINT s$,": ", |
|||
0150 IF luhn(s$) THEN |
|||
0160 PRINT "pass" |
|||
0170 ELSE |
|||
0180 PRINT "fail" |
|||
0190 ENDIF |
|||
0200 ENDPROC test |
|||
0210 // |
|||
0220 test("49927398716") |
|||
0230 test("49927398717") |
|||
0240 test("1234567812345678") |
|||
0250 test("1234567812345670") |
|||
0260 END</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716: pass |
|||
49927398717: fail |
|||
1234567812345678: fail |
|||
1234567812345670: pass</pre> |
|||
=={{header|Common Lisp}}== |
=={{header|Common Lisp}}== |
||
< |
<syntaxhighlight lang="lisp">(defun luhn (n) |
||
(labels (( |
(labels ((sum-digits (n) (if (> n 9) (- n 9) n))) |
||
(sum-digits (n) (if (> n 9) (- n 9) n))) |
|||
(let ((n* (reverse n)) (l (length n))) |
(let ((n* (reverse n)) (l (length n))) |
||
(let ((s1 (loop for i from 0 below l by 2 |
(let ((s1 (loop for i from 0 below l by 2 |
||
summing ( |
summing (digit-char-p (aref n* i)))) |
||
(s2 (loop for i from 1 below l by 2 |
(s2 (loop for i from 1 below l by 2 |
||
summing (sum-digits (* 2 ( |
summing (sum-digits (* 2 (digit-char-p (aref n* i))))))) |
||
(zerop (mod (+ s1 s2) 10))))))</ |
(zerop (mod (+ s1 s2) 10))))))</syntaxhighlight> |
||
Another version, using Maciej Pasternacki's reader macros for |
Another version, using Maciej Pasternacki's reader macros for |
||
function composition and currying in the [http://www.cl-user.net/asp/Fr4F/sdataQ1nAQqjvnQ3MDQ3ESH8X8yBX8yBXnMq=/sdataQu3F$sSHnB== curly] package: |
function composition and [[currying]] in the [http://www.cl-user.net/asp/Fr4F/sdataQ1nAQqjvnQ3MDQ3ESH8X8yBX8yBXnMq=/sdataQu3F$sSHnB== curly] package: |
||
< |
<syntaxhighlight lang="lisp">(require :curly) |
||
(use-package :curly) |
(use-package :curly) |
||
(enable-curly-syntax) |
(enable-curly-syntax) |
||
(defun luhn (seq) |
(defun luhn (seq) |
||
(labels (( |
(labels ((sum-digits (n) (if (> n 9) (- n 9) n))) |
||
(sum-digits (n) (if (> n 9) (- n 9) n))) |
|||
(funcall {zerop (mod * 10) (apply #'+) (mapcar #'sum-digits) |
(funcall {zerop (mod * 10) (apply #'+) (mapcar #'sum-digits) |
||
(mapcar #'* '#1=(1 2 . #1#)) (map 'list #' |
(mapcar #'* '#1=(1 2 . #1#)) (map 'list #'digit-char-p) reverse} seq)))</syntaxhighlight> |
||
=={{header|Cowgol}}== |
|||
<syntaxhighlight lang="cowgol">include "cowgol.coh"; |
|||
# Given a string containing the digits of a credit card number, |
|||
# see if it passes the Luhn test. |
|||
sub luhn(card: [uint8]): (ok: uint8) is |
|||
# Scan ahead to last digit, counting digits |
|||
var n: uint8 := 0; |
|||
while [card] != 0 loop |
|||
n := n + 1; |
|||
card := @next card; |
|||
end loop; |
|||
var sum: uint8 := 0; |
|||
while n > 0 loop |
|||
# odd digit is simply added |
|||
card := @prev card; |
|||
n := n - 1; |
|||
sum := sum + ([card] - '0'); |
|||
# if uneven amount of digits, stop |
|||
if n == 0 then break; end if; |
|||
# even digit |
|||
card := @prev card; |
|||
n := n - 1; |
|||
var digit := [card] - '0'; |
|||
# it is good to avoid unnecessary multiplication/ |
|||
# division, since 8-bit processors and microcontrollers |
|||
# don't tend to have that in hardware |
|||
if digit < 5 then |
|||
sum := sum + digit + digit; |
|||
else |
|||
digit := digit - 5; |
|||
sum := sum + digit + digit + 1; |
|||
end if; |
|||
end loop; |
|||
# there is no boolean type, comparisons only work |
|||
# in conditionals; this is the only way to return |
|||
# a status |
|||
if sum % 10 == 0 then |
|||
ok := 1; |
|||
else |
|||
ok := 0; |
|||
end if; |
|||
end sub; |
|||
# Test and print |
|||
sub test(card: [uint8]) is |
|||
var msg: [uint8][] := {"Fail", "Pass"}; |
|||
print(card); |
|||
print(": "); |
|||
print(msg[luhn(card)]); |
|||
print_nl(); |
|||
end sub; |
|||
test("49927398716"); |
|||
test("49927398717"); |
|||
test("1234567812345678"); |
|||
test("1234567812345670");</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716: Pass |
|||
49927398717: Fail |
|||
1234567812345678: Fail |
|||
1234567812345670: Pass</pre> |
|||
=={{header|Crystal}}== |
|||
<syntaxhighlight lang="ruby">def luhn_valid?(n) # Card values can be numbers or strings |
|||
d2sum = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9] |
|||
sum, n = 0, n.to_u64 |
|||
while n > 0; sum += n%10; n //= 10; sum += d2sum[n%10]; n //= 10 end |
|||
sum % 10 == 0 |
|||
end |
|||
cards = [49927398716, "49927398717", 1234567812345678, "1234567812345670"] |
|||
cards.each{ |i| puts "#{i}: #{luhn_valid?(i)}" }</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716: true |
|||
49927398717: false |
|||
1234567812345678: false |
|||
1234567812345670: true</pre> |
|||
=={{header|D}}== |
=={{header|D}}== |
||
===Functional Version=== |
|||
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. |
|||
{{trans|Haskell}} |
|||
<lang d>bool luhntest(string number) { |
|||
<syntaxhighlight lang="d">import std.algorithm, std.range, std.string; |
|||
uint len = number.length; |
|||
uint sum1, sum2; |
|||
enum luhnTest = (in string n) pure /*nothrow*/ @safe /*@nogc*/ => |
|||
for (int i = len - 1; i >= 0; i--) { |
|||
retro(n) |
|||
uint num = number[i] - '\u0030'; |
|||
.zip(only(1, 2).cycle) |
|||
.map!(p => (p[0] - '0') * p[1]) |
|||
.map!(d => d / 10 + d % 10) |
|||
.sum % 10 == 0; |
|||
sum2 += num / 5 + (2 * num) % 10; |
|||
void main() { |
|||
assert("49927398716 49927398717 1234567812345678 1234567812345670" |
|||
.split.map!luhnTest.equal([true, false, false, true])); |
|||
}</syntaxhighlight> |
|||
===More Imperative Version=== |
|||
{{trans|C}} |
|||
<syntaxhighlight lang="d">import std.algorithm; |
|||
bool luhnTest(in string num) @safe pure nothrow @nogc { |
|||
uint sum; |
|||
foreach_reverse (immutable i, immutable n; num) { |
|||
immutable uint ord = n - '\u0030'; |
|||
sum += ((num.length - i) & 1) ? ord : ord / 5 + (2 * ord) % 10; |
|||
} |
|||
return sum % 10 == 0; |
|||
} |
|||
void main() { |
|||
immutable data = ["49927398716", |
|||
"49927398717", |
|||
"1234567812345678", |
|||
"1234567812345670"]; |
|||
assert(data.map!luhnTest.equal([true, false, false, true])); |
|||
}</syntaxhighlight> |
|||
===Stronger Statically Typed Version=== |
|||
This version uses more precise types. |
|||
{{trans|SPARK}} |
|||
<syntaxhighlight lang="d">import std.stdio; |
|||
struct Interval(T) { |
|||
immutable T a, b; |
|||
this(in T a_, in T b_) pure nothrow @nogc { |
|||
this.a = a_; |
|||
this.b = b_; |
|||
} |
|||
bool opBinaryRight(string op="in")(in T x) |
|||
const pure nothrow @nogc { |
|||
return x >= a && x <= b; |
|||
} |
|||
pure nothrow @safe @nogc const invariant { |
|||
assert(a <= b); |
|||
} |
|||
} |
|||
Interval!T interval(T)(in T a, in T b) pure nothrow @nogc { |
|||
return Interval!T(a, b); |
|||
} |
|||
bool luhnTest(in string num) pure nothrow @nogc |
|||
in { |
|||
assert(num.length <= 20); |
|||
} body { |
|||
int sum = 0; |
|||
bool od = true; |
|||
bool ok = true; |
|||
immutable int numLen = num.length; |
|||
foreach_reverse (immutable 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 (sum1 + sum2) % 10 == 0; |
|||
return ok && (sum % 10) == 0; |
|||
}</lang> |
|||
} |
|||
void main() { |
|||
foreach (immutable n; ["49927398716", "49927398717", |
|||
"1234567812345678", "1234567812345670", |
|||
"123456781234567D"]) |
|||
writefln("%s is %svalid", n, luhnTest(n) ? "" : "not "); |
|||
}</syntaxhighlight> |
|||
=={{header|Delphi}}== |
|||
{{works with|Delphi|6.0}} |
|||
{{libheader|SysUtils,StdCtrls}} |
|||
Uses an array to handle the situation where multiply a term by 2, creates a two digit number. The array automatically handles adding each digit of the resulting two digit number. Also, avoids reversing the number by traversing the array backwards |
|||
<syntaxhighlight lang="Delphi"> |
|||
{Test data arrays} |
|||
const Num1: array [0..10] of byte = (4,9,9,2,7,3,9,8,7,1,6); |
|||
const Num2: array [0..10] of byte = (4,9,9,2,7,3,9,8,7,1,7); |
|||
const Num3: array [0..15] of byte = (1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8); |
|||
const Num4: array [0..15] of byte = (1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,0); |
|||
{Simplifies cases where we have to sum a two digit number} |
|||
const DigitSum: array [0..18] of byte = (0,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9); |
|||
function ValidateCreditCard(CardNum: array of byte): boolean; |
|||
{Validate a Credit Card number} |
|||
var I,J,Len,Sum,Sum1,Sum2: integer; |
|||
var Rev: array of byte; |
|||
begin |
|||
Sum1:=0; Sum2:=0; |
|||
Len:=High(CardNum); |
|||
for I:=Len downto 0 do |
|||
if ((I-Len) and 1)=0 then Sum1:=Sum1 + CardNum[I] |
|||
else Sum2:=Sum2 + DigitSum[CardNum[I]*2]; |
|||
Sum:=Sum1+Sum2; |
|||
Result:=(Sum mod 10)=0; |
|||
end; |
|||
function CardNumberToStr(CardNum: array of byte): string; |
|||
{Convert card number to a string} |
|||
var I: integer; |
|||
begin |
|||
Result:=''; |
|||
for I:=0 to High(CardNum) do |
|||
Result:=Result+IntToStr(CardNum[I]); |
|||
end; |
|||
procedure TestDisplayNumber(Memo: TMemo; Num: array of byte); |
|||
{Test a credit card number and display results} |
|||
var S: string; |
|||
begin |
|||
S:=CardNumberToStr(Num); |
|||
if ValidateCreditCard(Num) then S:=S+': Valid' |
|||
else S:=S+': Not Valid'; |
|||
Memo.Lines.Add(S); |
|||
end; |
|||
procedure TestCreditCardNums(Memo: TMemo); |
|||
{Test all credit card numbers} |
|||
begin |
|||
TestDisplayNumber(Memo,Num1); |
|||
TestDisplayNumber(Memo,Num2); |
|||
TestDisplayNumber(Memo,Num3); |
|||
TestDisplayNumber(Memo,Num4); |
|||
end; |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
49927398716: Valid |
|||
49927398717: Not Valid |
|||
1234567812345678: Not Valid |
|||
1234567812345670: Valid |
|||
</pre> |
|||
=={{header|Draco}}== |
|||
<syntaxhighlight lang="draco">proc nonrec luhn(*char num) bool: |
|||
[10] byte map = (0, 2, 4, 6, 8, 1, 3, 5, 7, 9); |
|||
byte total, digit; |
|||
*char start; |
|||
bool even; |
|||
start := num; |
|||
total := 0; |
|||
even := true; |
|||
while num* /= '\e' do num := num + 1 od; |
|||
while |
|||
num := num - 1; |
|||
num >= start |
|||
do |
|||
digit := num* - '0'; |
|||
even := not even; |
|||
if even then digit := map[digit] fi; |
|||
total := total + digit |
|||
od; |
|||
total % 10 = 0 |
|||
corp |
|||
proc nonrec test(*char num) void: |
|||
writeln(num, ": ", if luhn(num) then "pass" else "fail" fi) |
|||
corp |
|||
proc nonrec main() void: |
|||
test("49927398716"); |
|||
test("49927398717"); |
|||
test("1234567812345678"); |
|||
test("1234567812345670") |
|||
corp</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716: pass |
|||
49927398717: fail |
|||
1234567812345678: fail |
|||
1234567812345670: pass</pre> |
|||
=={{header|EasyLang}}== |
|||
<syntaxhighlight> |
|||
func luhn cc$ . |
|||
for i = len cc$ downto 1 |
|||
odd = 1 - odd |
|||
dig = number substr cc$ i 1 |
|||
if odd = 0 |
|||
dig = 2 * dig |
|||
if dig >= 10 |
|||
dig -= 9 |
|||
. |
|||
. |
|||
sum += dig |
|||
. |
|||
return if sum mod 10 = 0 |
|||
. |
|||
cc$[] = [ "49927398716" "49927398717" "1234567812345678" "1234567812345670" ] |
|||
for cc$ in cc$[] |
|||
write cc$ & " " |
|||
if luhn cc$ = 1 |
|||
print "is valid" |
|||
else |
|||
print "is not valid" |
|||
. |
|||
. |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
49927398716 is valid |
|||
49927398717 is not valid |
|||
1234567812345678 is not valid |
|||
1234567812345670 is valid |
|||
</pre> |
|||
=={{header|EchoLisp}}== |
|||
<syntaxhighlight lang="lisp"> |
|||
;; value for 'even' numbers |
|||
(define (even-val n) (if (> n 4) (+ n n -9) (+ n n))) |
|||
;;Luhn test |
|||
;; input : a string of decimal digits |
|||
;; output #t or #f |
|||
(define (valid nums (odd #f )) |
|||
(let ((nums (map string->number (reverse (string->list nums))))) |
|||
(= 0 (modulo |
|||
(for/sum ((n nums)) (set! odd (not odd)) (if odd n (even-val n))) |
|||
10)))) |
|||
(valid "49927398716") → #t |
|||
(valid "1234567812345670") → #t |
|||
(valid "1234567812345678") → #f |
|||
(valid "49927398717") → #f |
|||
</syntaxhighlight> |
|||
=={{header|Elixir}}== |
|||
<syntaxhighlight lang="elixir">defmodule Luhn do |
|||
def valid?(cc) when is_binary(cc), do: String.to_integer(cc) |> valid? |
|||
def valid?(cc) when is_integer(cc) do |
|||
0 == Integer.digits(cc) |
|||
|> Enum.reverse |
|||
|> Enum.chunk(2, 2, [0]) |
|||
|> Enum.reduce(0, fn([odd, even], sum) -> Enum.sum([sum, odd | Integer.digits(even*2)]) end) |
|||
|> rem(10) |
|||
end |
|||
end |
|||
numbers = ~w(49927398716 49927398717 1234567812345678 1234567812345670) |
|||
for n <- numbers, do: IO.puts "#{n}: #{Luhn.valid?(n)}"</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
49927398716: true |
|||
49927398717: false |
|||
1234567812345678: false |
|||
1234567812345670: true |
|||
</pre> |
|||
=={{header|Emacs Lisp}}== |
|||
{{libheader|seq.el}} |
|||
<syntaxhighlight lang="lisp">(require 'seq) |
|||
(defun luhn (str) |
|||
"Check if STR is a valid credit card number using the Luhn algorithm." |
|||
(if (string-match-p "[^0-9]" str) |
|||
(error "String contains invalid character") |
|||
(let ((digit-list (reverse (mapcar #'(lambda (x) (- x 48)) |
|||
(string-to-list str))))) |
|||
(zerop |
|||
(mod (apply #'+ (seq-map-indexed |
|||
(lambda (elt idx) |
|||
(if (not (zerop (% idx 2))) |
|||
(if (> (* 2 elt) 9) |
|||
(- (* 2 elt) 9) |
|||
(* 2 elt)) |
|||
elt)) |
|||
digit-list)) |
|||
10))))) |
|||
(mapcar #'luhn '("49927398716" "49927398717" "1234567812345678" "1234567812345670"))</syntaxhighlight> |
|||
{{out}} |
|||
(t nil nil t) |
|||
=={{header|Erlang}}== |
|||
<syntaxhighlight lang="erlang"> |
|||
-module(luhn_test). |
|||
-export( [credit_card/1, task/0] ). |
|||
luhn_sum([Odd, Even |Rest]) when Even >= 5 -> |
|||
Odd + 2 * Even - 10 + 1 + luhn_sum(Rest); |
|||
luhn_sum([Odd, Even |Rest]) -> |
|||
Odd + 2 * Even + luhn_sum(Rest); |
|||
luhn_sum([Odd]) -> |
|||
Odd; |
|||
luhn_sum([]) -> |
|||
0. |
|||
check( Sum ) when (Sum rem 10) =:= 0 -> valid; |
|||
check( _Sum ) -> invalid. |
|||
credit_card(Digits) -> |
|||
check(luhn_sum(lists:map(fun(D) -> D-$0 end, lists:reverse(Digits)))). |
|||
task() -> |
|||
Numbers = ["49927398716", "49927398717", "1234567812345678", "1234567812345670"], |
|||
[io:fwrite("~s: ~p~n", [X, credit_card(X)]) || X <- Numbers]. |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
16> luhn_test:task(). |
|||
49927398716: valid |
|||
49927398717: invalid |
|||
1234567812345678: invalid |
|||
1234567812345670: valid |
|||
</pre> |
|||
=={{header|Euphoria}}== |
|||
{{trans|C}} |
|||
<syntaxhighlight 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</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716 = 1 |
|||
49927398717 = 0 |
|||
1234567812345678 = 0 |
|||
1234567812345670 = 1</pre> |
|||
=={{header|Excel}}== |
|||
===LAMBDA=== |
|||
Binding the name '''luhnChecked''' to the following lambda expression in the Name Manager of the Excel WorkBook: |
|||
(See [https://www.microsoft.com/en-us/research/blog/lambda-the-ultimatae-excel-worksheet-function/ LAMBDA: The ultimate Excel worksheet function]) |
|||
{{Works with|Office 365 betas 2021}} |
|||
<syntaxhighlight lang="lisp">luhnChecked |
|||
=LAMBDA(s, |
|||
LET( |
|||
ns, REVERSECOLS(VALUE(CHARSROW(s))), |
|||
ixs, SEQUENCE(1, COLUMNS(ns), 1, 1), |
|||
0 = MOD(SUM( |
|||
FILTER(ns, 0 <> MOD(ixs, 2)) |
|||
) + ( |
|||
LAMBDA(n, |
|||
DIGITSUM( |
|||
CONCAT(TEXT(2 * n, "0")) |
|||
) |
|||
)( |
|||
FILTER(ns, 0 = MOD(ixs, 2)) |
|||
) |
|||
), |
|||
10 |
|||
) |
|||
) |
|||
)</syntaxhighlight> |
|||
and also assuming the following generic bindings in the Name Manager for the WorkBook: |
|||
<syntaxhighlight lang="lisp">CHARSROW |
|||
=LAMBDA(s, |
|||
MID(s, |
|||
SEQUENCE(1, LEN(s), 1, 1), |
|||
1 |
|||
) |
|||
) |
|||
DIGITSUM |
|||
=LAMBDA(s, |
|||
SUM(VALUE( |
|||
MID(s, |
|||
SEQUENCE(LEN(s), 1, 1, 1), |
|||
1 |
|||
) |
|||
)) |
|||
) |
|||
REVERSECOLS |
|||
=LAMBDA(xs, |
|||
LET( |
|||
n, COLUMNS(xs), |
|||
SORTBY( |
|||
xs, |
|||
SEQUENCE(1, n, n, -1) |
|||
) |
|||
) |
|||
)</syntaxhighlight> |
|||
{{Out}} |
|||
{| class="wikitable" |
|||
|- |
|||
|||style="text-align:right; font-family:serif; font-style:italic; font-size:120%;"|fx |
|||
! colspan="2" style="text-align:left; vertical-align: bottom; font-family:Arial, Helvetica, sans-serif !important;"|=luhnChecked(A2) |
|||
|- style="text-align:center; font-family:Arial, Helvetica, sans-serif !important; background-color:#000000; color:#ffffff;" |
|||
| |
|||
| A |
|||
| B |
|||
|- |
|||
| style="text-align:center; font-family:Arial, Helvetica, sans-serif !important; background-color:#000000; color:#ffffff" | 1 |
|||
| style="font-weight:bold" | Digit strings |
|||
| style="font-weight:bold" | Luhn result |
|||
|- |
|||
| style="text-align:center; font-family:Arial, Helvetica, sans-serif !important; background-color:#000000; color:#ffffff" | 2 |
|||
| style="text-align:right" | 49927398716 |
|||
| style="background-color:#cbcefb" | TRUE |
|||
|- |
|||
| style="text-align:center; font-family:Arial, Helvetica, sans-serif !important; background-color:#000000; color:#ffffff" | 3 |
|||
| style="text-align:right" | 49927398717 |
|||
| FALSE |
|||
|- |
|||
| style="text-align:center; font-family:Arial, Helvetica, sans-serif !important; background-color:#000000; color:#ffffff" | 4 |
|||
| style="text-align:right" | 1234567812345678 |
|||
| FALSE |
|||
|- |
|||
| style="text-align:center; font-family:Arial, Helvetica, sans-serif !important; background-color:#000000; color:#ffffff" | 5 |
|||
| style="text-align:right" | 1234567812345670 |
|||
| TRUE |
|||
|} |
|||
=={{header|F Sharp|F#}}== |
=={{header|F Sharp|F#}}== |
||
< |
<syntaxhighlight lang="fsharp">let luhn (s:string) = |
||
let rec g r c = function |
let rec g r c = function |
||
| 0 -> r |
| 0 -> r |
||
Line 440: | Line 3,153: | ||
let d = ((int s.[i - 1]) - 48) <<< c |
let d = ((int s.[i - 1]) - 48) <<< c |
||
g (r + if d < 10 then d else d - 9) (1 - c) (i - 1) |
g (r + if d < 10 then d else d - 9) (1 - c) (i - 1) |
||
(g 0 0 s.Length) % 10 = 0</ |
(g 0 0 s.Length) % 10 = 0</syntaxhighlight> |
||
=={{header|Factor}}== |
|||
{{works with|Factor|0.98}} |
|||
<syntaxhighlight 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 = ; |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
( scratchpad ) 49927398716 luhn? . |
|||
t |
|||
( scratchpad ) 49927398717 luhn? . |
|||
f |
|||
( scratchpad ) 1234567812345678 luhn? . |
|||
f |
|||
( scratchpad ) 1234567812345670 luhn? . |
|||
t |
|||
</pre> |
|||
=={{header|Forth}}== |
=={{header|Forth}}== |
||
< |
<syntaxhighlight lang="forth">: luhn ( addr len -- ? ) |
||
0 >r over + ( R: sum ) |
0 >r over + ( R: sum ) |
||
begin 1- 2dup <= |
begin 1- 2dup <= |
||
Line 460: | Line 3,207: | ||
s" 49927398717" luhn . \ 0 |
s" 49927398717" luhn . \ 0 |
||
s" 1234567812345678" luhn . \ 0 |
s" 1234567812345678" luhn . \ 0 |
||
s" 1234567812345670" luhn . \ -1</ |
s" 1234567812345670" luhn . \ -1</syntaxhighlight> |
||
=={{header|Fortran}}== |
=={{header|Fortran}}== |
||
< |
<syntaxhighlight lang="fortran">program luhn |
||
implicit none |
implicit none |
||
integer :: nargs |
integer :: nargs |
||
Line 499: | Line 3,246: | ||
! 49927398717 is not valid |
! 49927398717 is not valid |
||
! 1234567812345678 is not valid |
! 1234567812345678 is not valid |
||
! 1234567812345670 is valid</ |
! 1234567812345670 is valid</syntaxhighlight> |
||
=={{header|FreeBASIC}}== |
|||
<syntaxhighlight lang="freebasic">' version 05-07-2015 |
|||
' compile with: fbc -s console |
|||
#Ifndef TRUE ' define true and false for older freebasic versions |
|||
#Define FALSE 0 |
|||
#Define TRUE Not FALSE |
|||
#EndIf |
|||
Function luhntest(cardnr As String) As Integer |
|||
cardnr = Trim(cardnr) ' we don't want spaces |
|||
Dim As String reverse_nr = cardnr |
|||
Dim As Integer i, j, s1, s2, l = Len(cardnr) - 1 |
|||
' reverse string |
|||
For i = 0 To l |
|||
reverse_nr[i] = cardnr[l - i] |
|||
Next |
|||
' sum odd numbers |
|||
For i = 0 To l Step 2 |
|||
s1 = s1 + (reverse_nr[i] - Asc("0")) |
|||
Next |
|||
' sum even numbers |
|||
For i = 1 To l Step 2 |
|||
j = reverse_nr[i] - Asc("0") |
|||
j = j * 2 |
|||
If j > 9 Then j = j Mod 10 + 1 |
|||
s2 = s2 + j |
|||
Next |
|||
If (s1 + s2) Mod 10 = 0 Then |
|||
Return TRUE |
|||
Else |
|||
Return FALSE |
|||
End If |
|||
End Function |
|||
' ------=< MAIN >=------ |
|||
Dim As String input_nr(1 To ...) = {"49927398716", "49927398717",_ |
|||
"1234567812345678", "1234567812345670"} |
|||
Dim As Integer a |
|||
Print "Task test number 49927398716 should be TRUE, report back as "; |
|||
Print IIf(luhntest("49927398716" ) = TRUE, "TRUE", "FALSE") |
|||
Print : Print |
|||
Print "test card nr:" |
|||
For a = 1 To UBound(input_nr) |
|||
Print input_nr(a); " = "; IIf(luhntest(input_nr(a)) = TRUE, "TRUE", "FALSE") |
|||
Next |
|||
' empty keyboard buffer |
|||
While InKey <> "" : Wend |
|||
Print : Print "hit any key to end program" |
|||
Sleep |
|||
End</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Task test number 49927398716 should be TRUE, report back as TRUE |
|||
test card nr: |
|||
49927398716 = TRUE |
|||
49927398717 = FALSE |
|||
1234567812345678 = FALSE |
|||
1234567812345670 = TRUE</pre> |
|||
=={{header|Free Pascal}}== |
|||
''see also: [[#Pascal|Pascal]]'' |
|||
<syntaxhighlight 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.</syntaxhighlight> |
|||
{{out}} |
|||
<pre> 49927398716: TRUE |
|||
49927398717: FALSE |
|||
1234567812345678: FALSE |
|||
1234567812345670: TRUE</pre> |
|||
=={{header|FunL}}== |
|||
<syntaxhighlight lang="funl">def luhn_checksum( card_number ) = |
|||
def digits_of( n ) = [int(d) | d <- n.toString()] |
|||
digits = digits_of( card_number ).reverse() |
|||
odd_digits = digits(0:digits.length():2) |
|||
even_digits = digits(1:digits.length():2) |
|||
(sum( odd_digits ) + sum( sum(digits_of(d*2)) | d <- even_digits )) mod 10 |
|||
def is_luhn_valid( card_number ) = luhn_checksum( card_number ) == 0 |
|||
for n <- [49927398716, 49927398717, 1234567812345678, 1234567812345670] |
|||
println( n + ' is ' + (if is_luhn_valid(n) then 'valid' else 'invalid') )</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
49927398716 is valid |
|||
49927398717 is invalid |
|||
1234567812345678 is invalid |
|||
1234567812345670 is valid |
|||
</pre> |
|||
=={{header|FutureBasic}}== |
|||
<syntaxhighlight lang="futurebasic"> |
|||
include "NSLog.incl" |
|||
local fn LuhnCheck( cardStr as CFStringRef ) as BOOL |
|||
NSInteger i, j, count, s1 = 0, s2 = 0 |
|||
BOOL result = NO |
|||
// Build array of individual numbers in credit card string |
|||
NSUInteger strLength = len(cardStr) |
|||
CFMUtableArrayRef mutArr = fn MutableArrayWithCapacity(strLength) |
|||
for i = 0 to strLength - 1 |
|||
CFStringRef tempStr = fn StringWithFormat( @"%C", fn StringCharacterAtIndex( cardStr, i ) ) |
|||
MutableArrayInsertObjectAtIndex( mutArr, tempStr, i ) |
|||
next |
|||
// Reverse the number array |
|||
CFArrayRef reversedArray = fn EnumeratorAllObjects( fn ArrayReverseObjectEnumerator( mutArr ) ) |
|||
// Get number of array elements |
|||
count = len(reversedArray) |
|||
// Handle odd numbers |
|||
for i = 0 to count - 1 step 2 |
|||
s1 = s1 + fn StringIntegerValue( reversedArray[i] ) |
|||
next |
|||
// Hnadle even numbers |
|||
for i = 1 to count - 1 step 2 |
|||
j = fn StringIntegerValue( reversedArray[i] ) |
|||
j = j * 2 |
|||
if j > 9 then j = j mod 10 + 1 |
|||
s2 = s2 + j |
|||
next |
|||
if (s1 + s2) mod 10 = 0 then result = YES else result = NO |
|||
end fn = result |
|||
NSLogClear |
|||
if fn LuhnCheck( @"49927398716" ) then NSLog (@"%@ is valid.", @"49927398716" ) else NSLog (@"%@ is not valid.", @"49927398716" ) |
|||
if fn LuhnCheck( @"49927398717" ) then NSLog (@"%@ is valid.", @"49927398717" ) else NSLog (@"%@ is not valid.", @"49927398717" ) |
|||
if fn LuhnCheck( @"1234567812345678" ) then NSLog (@"%@ is valid.", @"1234567812345678" ) else NSLog (@"%@ is not valid.", @"1234567812345678" ) |
|||
if fn LuhnCheck( @"1234567812345670" ) then NSLog (@"%@ is valid.", @"1234567812345670" ) else NSLog (@"%@ is not valid.", @"1234567812345670" ) |
|||
HandleEvents |
|||
</syntaxhighlight> |
|||
{{output}} |
|||
<pre> |
|||
49927398716 is valid. |
|||
49927398717 is not valid. |
|||
1234567812345678 is not valid. |
|||
1234567812345670 is valid. |
|||
</pre> |
|||
=={{header|Gambas}}== |
|||
'''[https://gambas-playground.proko.eu/?gist=966f04aa2a1e9af43ba9553f5cb9160d Click this link to run this code]''' |
|||
<syntaxhighlight lang="gambas">Public Sub Main() |
|||
Dim sTrial As String[] = ["49927398716", "49927398717", "1234567812345678", "1234567812345670"] |
|||
Dim sRev As String |
|||
Dim siCount, siOdd, siEven, siHold, siQty As Short |
|||
For siQty = 0 To sTrial.Max |
|||
siOdd = 0 |
|||
siEven = 0 |
|||
sRev = "" |
|||
For siCount = Len(sTrial[siQty]) DownTo 1 |
|||
sRev &= Mid(sTrial[siQty], siCount, 1) |
|||
Next |
|||
For siCount = 1 To Len(sRev) |
|||
If Odd(siCount) Then siOdd += Val(Mid(sRev, siCount, 1)) |
|||
If Even(siCount) Then |
|||
siHold = Val(Mid(sRev, siCount, 1)) * 2 |
|||
If siHold > 9 Then |
|||
siEven += Val(Mid(Str(siHold), 1, 1)) + Val(Mid(Str(siHold), 2, 1)) |
|||
Else |
|||
siEven += Val(Mid(sRev, siCount, 1)) * 2 |
|||
End If |
|||
End If |
|||
Next |
|||
sRev = Str(siOdd + siEven) |
|||
If sRev Ends "0" Then |
|||
Print sTrial[siQty] & " is a valid number" |
|||
Else |
|||
Print sTrial[siQty] & " is NOT a valid number" |
|||
End If |
|||
Next |
|||
End</syntaxhighlight> |
|||
Output: |
|||
<pre> |
|||
49927398716 is a valid number |
|||
49927398717 is NOT a valid number |
|||
1234567812345678 is NOT a valid number |
|||
1234567812345670 is a valid number |
|||
</pre> |
|||
=={{header|GAP}}== |
|||
<syntaxhighlight 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); |
|||
# [ true, false, false, true ] |
|||
# Will also work on strings, and will skip non-digits |
|||
IsLuhn("4-992-739-871-6"); |
|||
# true</syntaxhighlight> |
|||
=={{header|Go}}== |
=={{header|Go}}== |
||
<syntaxhighlight lang="go">package main |
|||
<lang go> |
|||
package main |
|||
import ( |
import ( |
||
Line 526: | Line 3,525: | ||
sum += t[c-'0'] |
sum += t[c-'0'] |
||
} else { |
} else { |
||
sum += c-'0' |
sum += int(c - '0') |
||
} |
} |
||
} |
} |
||
Line 533: | Line 3,532: | ||
func main() { |
func main() { |
||
for _, s := range strings.Split(input, "\n" |
for _, s := range strings.Split(input, "\n") { |
||
fmt.Println(s, luhn(s)) |
fmt.Println(s, luhn(s)) |
||
} |
} |
||
}</syntaxhighlight> |
|||
} |
|||
{{out}} |
|||
</lang> |
|||
Output: |
|||
<pre> |
<pre> |
||
49927398716 true |
49927398716 true |
||
Line 546: | Line 3,544: | ||
</pre> |
</pre> |
||
=={{header| |
=={{header|Groovy}}== |
||
<syntaxhighlight lang="groovy">def checkLuhn(number) { |
|||
int total |
|||
(number as String).reverse().eachWithIndex { ch, index -> |
|||
def digit = Integer.parseInt(ch) |
|||
total += (index % 2 ==0) ? digit : [0, 2, 4, 6, 8, 1, 3, 5, 7, 9][digit] |
|||
} |
|||
total % 10 == 0 |
|||
}</syntaxhighlight> |
|||
Testing the function: |
|||
<syntaxhighlight lang="groovy">def verifyLuhn(number, expected) { |
|||
println "Checking: $number (${checkLuhn(number)})" |
|||
assert expected == checkLuhn(number) |
|||
} |
|||
[49927398716: true, 49927398717: false, 1234567812345678: false, 1234567812345670: true].each { number, expected -> |
|||
<lang haskell>import Data.Char (digitToInt) |
|||
verifyLuhn number, expected |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Checking: 49927398716 (true) |
|||
Checking: 49927398717 (false) |
|||
Checking: 1234567812345678 (false) |
|||
Checking: 1234567812345670 (true)</pre> |
|||
=={{header|Haskell}}== |
|||
<syntaxhighlight lang="haskell">import Data.Char (digitToInt) |
|||
luhn = (0 ==) . (`mod` 10) . sum . map (uncurry (+) . (`divMod` 10)) . |
luhn = (0 ==) . (`mod` 10) . sum . map (uncurry (+) . (`divMod` 10)) . |
||
zipWith (*) (cycle [1,2]) . map digitToInt . reverse</ |
zipWith (*) (cycle [1,2]) . map digitToInt . reverse</syntaxhighlight> |
||
{{out}} |
|||
<syntaxhighlight lang="haskell">map luhn ["49927398716", "49927398717", "1234567812345678", "1234567812345670"] |
|||
[True,False,False,True]</syntaxhighlight> |
|||
Or, aiming for a legible relationship with the stages shown in the task description: |
|||
<syntaxhighlight lang="haskell">import Data.Char (digitToInt) |
|||
import Data.List (transpose) |
|||
import Data.List.Split (chunksOf) |
|||
luhn :: String -> Bool |
|||
luhn x = 0 == rem (s1 + s2) 10 |
|||
where |
|||
stringInts = fmap digitToInt |
|||
[odds, evens] = |
|||
(transpose . chunksOf 2) |
|||
(stringInts $ reverse x) |
|||
s1 = sum odds |
|||
s2 = sum $ sum . stringInts . show . (2 *) <$> evens |
|||
main :: IO () |
|||
Sample output |
|||
main = |
|||
<lang haskell>map luhn ["49927398716", "49927398717", "1234567812345678", "1234567812345670"] |
|||
mapM_ |
|||
[True,False,False,True]</lang> |
|||
(print . ((,) <*> luhn)) |
|||
[ "49927398716", |
|||
"49927398717", |
|||
"1234567812345678", |
|||
"1234567812345670" |
|||
]</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>("49927398716",True) |
|||
("49927398717",False) |
|||
("1234567812345678",False) |
|||
("1234567812345670",True)</pre> |
|||
=={{header|HicEst}}== |
=={{header|HicEst}}== |
||
< |
<syntaxhighlight lang="hicest">CHARACTER numbers="49927398716 49927398717 1234567812345678 1234567812345670 " |
||
DO nr = 1, 4 |
DO nr = 1, 4 |
||
Line 572: | Line 3,623: | ||
valid = (0 == MOD(sum_odds + sum_even, 10)) |
valid = (0 == MOD(sum_odds + sum_even, 10)) |
||
WRITE() number, " is ", "invalid"(1 + 2*valid:) |
WRITE() number, " is ", "invalid"(1 + 2*valid:) |
||
ENDDO</ |
ENDDO</syntaxhighlight> |
||
<pre>49927398716 is valid |
<pre>49927398716 is valid |
||
49927398717 is invalid |
49927398717 is invalid |
||
Line 578: | Line 3,629: | ||
1234567812345670 is valid</pre> |
1234567812345670 is valid</pre> |
||
== |
=={{header|Icon}} and {{header|Unicon}}== |
||
==={{header|Icon}}=== |
|||
We use map to pre-compute the sum of doubled digits. |
We use map to pre-compute the sum of doubled digits. |
||
< |
<syntaxhighlight lang="icon">procedure main(aL) |
||
every write(i := !aL ," - ", ((\isluhn10(i),"valid")|"invalid") \ 1) |
every write(i := !aL ," - ", ((\isluhn10(i),"valid")|"invalid") \ 1) |
||
end |
end |
||
Line 596: | Line 3,645: | ||
return (sum % 10 = 0,i) |
return (sum % 10 = 0,i) |
||
end</ |
end</syntaxhighlight> |
||
{{out}} |
|||
Sample output |
|||
<pre> |
<pre> |
||
# luhn10 49927398716 49927398717 1234567812345678 1234567812345670 |
# luhn10 49927398716 49927398717 1234567812345678 1234567812345670 |
||
Line 607: | Line 3,656: | ||
1234567812345670 - valid |
1234567812345670 - valid |
||
</pre> |
</pre> |
||
==={{header|Unicon}}=== |
|||
This Icon solution works in Unicon. A solution that uses Unicon extensions has not been provided. |
|||
=={{header|J}}== |
=={{header|J}}== |
||
We can treat the odd digits the same as even digits, |
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. |
|||
< |
<syntaxhighlight lang="j">luhn=: 0 = 10 (| +/@,) 10 #.inv 1 2 *&|: _2 "."0\ |.</syntaxhighlight> |
||
Example use: |
Example use: |
||
< |
<syntaxhighlight lang="j"> luhn&> '49927398716';'49927398717';'1234567812345678';'1234567812345670' |
||
1 0 0 1</ |
1 0 0 1</syntaxhighlight> |
||
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. |
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. |
||
=={{header|Java}}== |
=={{header|Java}}== |
||
< |
<syntaxhighlight lang="java">public class Luhn { |
||
public static void main(String[] args) { |
public static void main(String[] args) { |
||
System.out.println(luhnTest("49927398716")); |
System.out.println(luhnTest("49927398716")); |
||
Line 635: | Line 3,684: | ||
String reverse = new StringBuffer(number).reverse().toString(); |
String reverse = new StringBuffer(number).reverse().toString(); |
||
for(int i = 0 ;i < reverse.length();i++){ |
for(int i = 0 ;i < reverse.length();i++){ |
||
int digit = |
int digit = Character.digit(reverse.charAt(i), 10); |
||
if(i % 2 == 0){//this is for odd digits, they are 1-indexed in the algorithm |
if(i % 2 == 0){//this is for odd digits, they are 1-indexed in the algorithm |
||
s1 += digit; |
s1 += digit; |
||
Line 647: | Line 3,696: | ||
return (s1 + s2) % 10 == 0; |
return (s1 + s2) % 10 == 0; |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
|||
Output: |
|||
<pre>true |
<pre>true |
||
false |
false |
||
false |
false |
||
true</pre> |
true</pre> |
||
=={{header|Java Long type version}}== |
|||
<syntaxhighlight lang="java">public class Luhn { |
|||
public static void main(String[] args) { |
|||
System.out.println(luhnTest(49927398716L)); |
|||
System.out.println(luhnTest(499273987163L)); |
|||
System.out.println(luhnTest(1234567L)); |
|||
System.out.println(luhnTest(0L)); |
|||
} |
|||
public static boolean luhnTest(Long digits) { |
|||
int s1 = 0, s2 = 0; |
|||
//Use an alternator for separate odd/even processing |
|||
boolean alternator = true; |
|||
//Confine digit numbers to 8 - 19 per ISO |
|||
if (digits < 1e7 || digits >= 1e19) return false; |
|||
for ( int i = 0; digits > 0; ++i) { |
|||
Long oneDigit = digits % 10; |
|||
if (alternator) { |
|||
s1 += oneDigit.intValue(); |
|||
} else { |
|||
oneDigit *= 2; |
|||
s2 += oneDigit > 9 ? oneDigit.intValue() - 9: oneDigit.intValue(); |
|||
} |
|||
digits /= 10; |
|||
alternator = !alternator; |
|||
} |
|||
return (s1 + s2) % 10 == 0 ? true : false; |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>true |
|||
false |
|||
false |
|||
false</pre> |
|||
=={{header|JavaScript}}== |
=={{header|JavaScript}}== |
||
Using prototype. |
Using prototype. |
||
< |
<syntaxhighlight lang="javascript">mod10check = function(cc) { |
||
return $A(cc).reverse().map(Number).inject(0, function(s, d, i) { |
return $A(cc).reverse().map(Number).inject(0, function(s, d, i) { |
||
return s + (i % 2 == 1 ? (d == 9 ? 9 : (d * 2) % 9) : d); |
return s + (i % 2 == 1 ? (d == 9 ? 9 : (d * 2) % 9) : d); |
||
}) % 10 == 0; |
}) % 10 == 0; |
||
}; |
}; |
||
['49927398716','49927398717','1234567812345678','1234567812345670'].each(function(i){alert(mod10check(i))});</ |
['49927398716','49927398717','1234567812345678','1234567812345670'].each(function(i){alert(mod10check(i))});</syntaxhighlight> |
||
=={{header|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 |
|||
Without any library. |
|||
to double.digit :digit |
|||
<syntaxhighlight lang="javascript">var LuhnCheck = (function() |
|||
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] |
|||
var luhnArr = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]; |
|||
end |
|||
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); |
|||
} |
|||
})();</syntaxhighlight> |
|||
Highly compressed version. |
|||
to luhn :credit |
|||
<syntaxhighlight lang="javascript">var luhn10 = function(a,b,c,d,e) { |
|||
localmake "digits reverse filter [number? ?] wordtolist :credit |
|||
for(d = +a[b = a.length-1], e=0; b--;) |
|||
localmake "s1 apply "sum every.other :digits |
|||
c = +a[b], d += ++e % 2 ? 2 * c % 10 + (c > 4) : c; |
|||
localmake "s2 apply "sum map "double.digit every.other bf :digits |
|||
return !(d%10) |
|||
output equal? 0 last sum :s1 :s2 |
|||
}; |
|||
// returns true |
|||
luhn10('4111111111111111') |
|||
// returns false |
|||
luhn10('4111111111111112') |
|||
</syntaxhighlight> |
|||
Naive implementation |
|||
<syntaxhighlight lang="javascript">const lunhCheck = (str) => { |
|||
const sumDigit = (c) => (c < 10) ? c : |
|||
sumDigit( Math.trunc(c / 10) + (c % 10)); |
|||
return str.split('').reverse() |
|||
.map(Number) |
|||
.map((c, i) => i % 2 !== 0 ? sumDigit(c * 2) : c) |
|||
.reduce((acc,v) => acc + v) % 10 === 0; |
|||
}; |
|||
lunhCheck('49927398716'); // returns true |
|||
lunhCheck('49927398717'); // returns false |
|||
lunhCheck('1234567812345678'); // returns false |
|||
lunhCheck('1234567812345670'); // returns true |
|||
</syntaxhighlight> |
|||
=={{header|jq}}== |
|||
{{works with|jq|1.4 or later}} |
|||
'''Works with gojq, the Go implementation of jq''' |
|||
For the specific task defined here, both jq (version 1.4 or later) |
|||
and gojq should suffice, but for very large integers (greater than |
|||
2^53), either gojq or a version of the C implementation of jq with |
|||
support for very large external integers would be required. |
|||
<syntaxhighlight lang="jq">def luhn: |
|||
def odds: . as $in | reduce range(0; length) as $i |
|||
([]; if ($i % 2) == 0 then . + [$in[$i]] else . end); |
|||
def evens: . as $in | reduce range(1; length) as $i |
|||
([]; if ($i % 2) == 1 then . + [$in[$i]] else . end); |
|||
def digits: map([.]|implode|tonumber); |
|||
def sumdigits: tostring | explode | digits | add; |
|||
(tostring | explode | reverse ) as $reverse |
|||
| ($reverse | odds | digits | add) as $s1 |
|||
| ($reverse | evens | digits | map(. * 2 | sumdigits) | add) as $s2 |
|||
| 0 == ($s1 + $s2) % 10 ;</syntaxhighlight> |
|||
'''Example''' |
|||
<syntaxhighlight lang="jq"> ( 49927398716, |
|||
49927398717, |
|||
1234567812345678, |
|||
1234567812345670 |
|||
) | "\(.) => \(luhn)";</syntaxhighlight> |
|||
{{Out}} |
|||
$ jq -r -M -n -f luhn.jq |
|||
49927398716 => true |
|||
49927398717 => false |
|||
1234567812345678 => false |
|||
1234567812345670 => true |
|||
=={{header|Julia}}== |
|||
The test function itself is only a single line of code: |
|||
<syntaxhighlight lang="julia">luhntest(x::Integer) = (sum(digits(x)[1:2:end]) + sum(map(x -> sum(digits(x)), 2 * digits(x)[2:2:end]))) % 10 == 0</syntaxhighlight> |
|||
More readable version: |
|||
<syntaxhighlight lang="julia">function luhntest(x::Integer) |
|||
d = reverse(digits(x)) |
|||
s = sum(d[1:2:end]) |
|||
s += sum(sum.(digits.(2d[2:2:end]))) |
|||
return s % 10 == 0 |
|||
end |
end |
||
for card in [49927398716, 49927398717, 1234567812345678, 1234567812345670] |
|||
println(luhntest(card) ? "PASS " : "FAIL ", card) |
|||
show luhn "49927398717 ; false |
|||
end</syntaxhighlight> |
|||
show luhn "1234-5678-1234-5678 ; false |
|||
show luhn "1234-5678-1234-5670 ; true</lang> |
|||
{{out}} |
|||
<pre>PASS 49927398716 |
|||
FAIL 49927398717 |
|||
FAIL 1234567812345678 |
|||
PASS 1234567812345670</pre> |
|||
=={{header|K}}== |
|||
<syntaxhighlight 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}</syntaxhighlight> |
|||
{{trans|J}} |
|||
<syntaxhighlight lang="k">luhn2: {~(+/,//10_vs'1 2*+-1 2#((#n)!2){x,0}/n:0$'|$x)</syntaxhighlight> |
|||
'''Example:''' |
|||
<syntaxhighlight lang="k"> luhn'49927398716 49927398717 1234567812345678 1234567812345670 |
|||
1 0 0 1 |
|||
luhn2'49927398716 49927398717 1234567812345678 1234567812345670 |
|||
1 0 0 1</syntaxhighlight> |
|||
{{Works with|K|3}} |
|||
<syntaxhighlight lang="k">luhn:{[cc] |
|||
digits:0$/:|cc / convert chars to digit ints |
|||
s:digits*(#cc)#1 2 / evens doubled, odds not |
|||
nines:+/s>9 / number of sums greater than 9 |
|||
:~((+/s)-(9*nines))!10 / sum minus the nines is mod ten? |
|||
}</syntaxhighlight> |
|||
'''Example:''' |
|||
<syntaxhighlight lang="k"> test:("49927398716";"49927398717";"1234567812345678";"1234567812345670") |
|||
luhn'test |
|||
1 0 0 1</syntaxhighlight> |
|||
=={{header|Kotlin}}== |
|||
<syntaxhighlight lang="scala">// version 1.0 |
|||
fun checkLuhn(number: String): Boolean { |
|||
var isOdd = true |
|||
var sum = 0 |
|||
for (index in number.indices.reversed()) { |
|||
val digit = number[index] - '0' |
|||
sum += if (isOdd) digit else (digit * 2).let { (it / 10) + (it % 10) } |
|||
isOdd = !isOdd |
|||
} |
|||
return (sum % 10) == 0 |
|||
} |
|||
fun main(args: Array<String>) { |
|||
val numbers = arrayOf("49927398716", "49927398717", "1234567812345678", "1234567812345670") |
|||
for (number in numbers) |
|||
println("${number.padEnd(16)} is ${if(checkLuhn(number)) "valid" else "invalid"}") |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
49927398716 is valid |
|||
49927398717 is invalid |
|||
1234567812345678 is invalid |
|||
1234567812345670 is valid |
|||
</pre> |
|||
=={{header|langur}}== |
|||
{{trans|Go}} |
|||
<syntaxhighlight lang="langur"> |
|||
val luhntest = fn(s) { |
|||
val t = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9] |
|||
val numbers = s -> s2n |
|||
val oddeven = len(numbers) rem 2 |
|||
for[=0] i of numbers { |
|||
_for += if(i rem 2 == oddeven: numbers[i]; t[numbers[i]+1]) |
|||
} div 10 |
|||
} |
|||
val tests = { |
|||
"49927398716": true, |
|||
"49927398717": false, |
|||
"1234567812345678": false, |
|||
"1234567812345670": true, |
|||
} |
|||
for key in sort(keys(tests)) { |
|||
val pass = luhntest(key) |
|||
write key, ": ", pass |
|||
writeln if(pass == tests[key]: ""; " (LUHN TEST FAILED)") |
|||
} |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398717: false |
|||
49927398716: true |
|||
1234567812345678: false |
|||
1234567812345670: true</pre> |
|||
=={{header|Lasso}}== |
|||
Part of the Lasso's implementation of "valid_creditcard". |
|||
<syntaxhighlight lang="lasso">#!/usr/bin/lasso9 |
|||
define luhn_check(number) => { |
|||
local( |
|||
rev = #number->asString, |
|||
checksum = 0 |
|||
) |
|||
#rev->reverse |
|||
iterate(#rev, local(digit)) => { |
|||
if((loop_count % 2) == 0) => { |
|||
#checksum += (2 * integer(#digit)) |
|||
integer(#digit) >= 5 ? #checksum -= 9 |
|||
else |
|||
#checksum += integer(#digit) |
|||
} |
|||
} |
|||
(#checksum % 10) != 0 ? return false |
|||
return true |
|||
} |
|||
stdoutnl(luhn_check(49927398716)) // true |
|||
stdoutnl(luhn_check(49927398717)) // false |
|||
stdoutnl(luhn_check(1234567812345678)) // false |
|||
stdoutnl(luhn_check(1234567812345670)) // true</syntaxhighlight> |
|||
=={{header|Liberty BASIC}}== |
=={{header|Liberty BASIC}}== |
||
< |
<syntaxhighlight lang="lb">' [RC] Luhn test |
||
card$(1)="49927398716" |
card$(1)="49927398716" |
||
Line 721: | Line 4,011: | ||
print card$(test),"False" |
print card$(test),"False" |
||
end if |
end if |
||
next</ |
next</syntaxhighlight> |
||
=={{header|LiveCode}}== |
|||
<syntaxhighlight lang="livecode">function LuhnTest cc |
|||
local s1,evens, s2 |
|||
repeat with n = 1 to len(cc) |
|||
if n mod 2 is not 0 then |
|||
add (char -n of cc) to s1 |
|||
else |
|||
put (char -n of cc) * 2 into evens |
|||
if evens > 9 then subtract 9 from evens |
|||
add evens to s2 |
|||
end if |
|||
end repeat |
|||
return the last char of (s1 + s2) is 0 |
|||
end LuhnTest |
|||
-- test |
|||
repeat for each item ccno in "49927398716,49927398717,1234567812345678,1234567812345670" |
|||
put ccno && LuhnTest(ccno) & cr after luhncheck |
|||
end repeat |
|||
put luhncheck |
|||
49927398716 true |
|||
49927398717 false |
|||
1234567812345678 false |
|||
1234567812345670 true |
|||
</syntaxhighlight> |
|||
=={{header|Logo}}== |
|||
<syntaxhighlight 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</syntaxhighlight> |
|||
=={{header|Lua}}== |
=={{header|Lua}}== |
||
<syntaxhighlight lang="lua"> |
|||
<lang Lua> |
|||
function |
function luhn_test(cc_number) |
||
assert(type(cc_number) == 'string') |
|||
local sum = 0 |
|||
print(n) |
|||
local |
local is_odd = true |
||
for idx = #cc_number , 1 , -1 do -- reverse order |
|||
--sum odd digits |
|||
-- extract single character as string then convert to integer |
|||
for i=1,n:len(),2 do |
|||
local digit = cc_number:sub(idx, idx) + 0 |
|||
if is_odd then |
|||
end |
|||
sum = sum + digit |
|||
--evens |
|||
else |
|||
local s2=0 |
|||
sum = sum + ((digit * 2) % 10) + (digit // 5) |
|||
for i=2,n:len(),2 do |
|||
end -- if |
|||
local doubled=n:sub(i,i)*2 |
|||
is_odd = not is_odd -- toggle between odd and even |
|||
doubled=string.gsub(doubled,'(%d)(%d)',function(a,b)return a+b end) |
|||
end -- for |
|||
s2=s2+doubled |
|||
return (sum % 10) == 0 |
|||
end |
|||
end -- function luhn_test |
|||
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> |
|||
=={{header|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. |
|||
for _, val in ipairs{"49927398716", "49927398717", "1234567812345678", "1234567812345670"} do |
|||
Example 1: |
|||
print(val, luhn_test(val)) |
|||
end -- for |
|||
</syntaxhighlight> |
|||
=={{header|M2000 Interpreter}}== |
|||
{{works with|MATLAB|2007a}} |
|||
{{trans|FreeBASIC}} |
|||
<lang MATLAB>function passes = luhnTest(creditCardNum) |
|||
<syntaxhighlight lang="m2000 interpreter">Module Checkit { |
|||
Function luhntest(cardnr$) { |
|||
cardnr$ = Trim$(cardnr$) ' we don't want spaces |
|||
if len(cardnr$)=0 then exit |
|||
Dim Base 0, reverse_nr$(Len(cardnr$)) |
|||
Def integer i, j, s1, s2, l , l2 |
|||
Let l=Len(cardnr$)-1, l2=l+1 |
|||
' reverse string |
|||
For i = 0 To l |
|||
reverse_nr$(i) = mid$(cardnr$,l2-i,1) |
|||
Next i |
|||
' sum odd numbers |
|||
For i = 0 To l Step 2 |
|||
s1 = s1 + (Asc(reverse_nr$(i)) - Asc("0")) |
|||
Next i |
|||
' sum even numbers |
|||
For i = 1 To l Step 2 |
|||
j = Asc(reverse_nr$(i)) - Asc("0") |
|||
j = j * 2 |
|||
If j > 9 Then j = j Mod 10 + 1 |
|||
s2 = s2 + j |
|||
Next i |
|||
If (s1 + s2) Mod 10 = 0 Then |
|||
= 1=1 |
|||
Else |
|||
= 1=0 |
|||
End If |
|||
} |
|||
Flush |
|||
Data "49927398716", "49927398717", "1234567812345678", "1234567812345670" |
|||
while not empty |
|||
over |
|||
print letter$;" = ";luhntest(letter$) |
|||
end while |
|||
} |
|||
Checkit</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716 = True |
|||
49927398717 = False |
|||
1234567812345678 = False |
|||
1234567812345670 = True</pre> |
|||
=={{header|MACRO-11}}== |
|||
%flip the order of the digits |
|||
<syntaxhighlight lang="macro11"> .TITLE LUHN |
|||
creditCardNum = fliplr( num2str(creditCardNum) )'; |
|||
.MCALL .GTLIN,.PRINT,.EXIT |
|||
LUHN:: .GTLIN #5$ |
|||
%sum the odd indexed digits |
|||
MOV #5$,R0 |
|||
s1 = sum( str2num( creditCardNum(1:2:length(creditCardNum)) ) ); |
|||
TSTB (R0) |
|||
BEQ 2$ |
|||
JSR PC,TEST |
|||
s2 = 2 * str2num(creditCardNum(2:2:length(creditCardNum))); |
|||
BNE 1$ |
|||
.PRINT #3$ |
|||
%convert s2 into a cell array of strings, where each even indexed digit |
|||
BR LUHN |
|||
1$: .PRINT #4$ |
|||
s2 = cellstr(num2str(s2)); |
|||
BR LUHN |
|||
2$: .EXIT |
|||
%go through each cell in the cell array and make the strings a column |
|||
3$: .ASCIZ /PASS/ |
|||
4$: .ASCIZ /FAIL/ |
|||
s2 = cellfun(@transpose,s2,'UniformOutput',false); |
|||
5$: .BLKB 200 |
|||
.EVEN |
|||
%convert each array of chars into an array of numbers |
|||
s2 = cellfun(@str2num ,s2,'UniformOutput',false); |
|||
TEST: MOV R0,R1 |
|||
CLR R2 |
|||
%Sum the partial sums of the even indexed digits to form s2 |
|||
1$: TSTB (R0)+ |
|||
BNE 1$ |
|||
DEC R0 |
|||
%Convert the sum of s1 and s2 into a column vector with each digit of |
|||
2$: MOVB -(R0),R3 |
|||
%the the sum being an element of the vector |
|||
SUB #60,R3 |
|||
passes = str2num(num2str(s1+s2).'); |
|||
ADD R3,R2 |
|||
CMP R0,R1 |
|||
%Find the smallest digit in the checksum, zero will only appear in the |
|||
BLOS 3$ |
|||
%last digit of the number if zero is a digit of the checksum. Then |
|||
MOVB -(R0),R3 |
|||
%convert the minimum digit to a boolean. Only zero will convert to a |
|||
SUB #60,R3 |
|||
%boolean false, any number larger than zero will convert to true. |
|||
MOVB 4$(R3),R3 |
|||
%Therefore, because we want to determine if a credit card number passes |
|||
ADD R3,R2 |
|||
%the Luhn Test, we have to not the result of the conversion to boolean. |
|||
CMP R0,R1 |
|||
passes = ~logical(min(passes)); |
|||
BHI 2$ |
|||
3$: SUB #12,R2 |
|||
end</lang> |
|||
BHI 3$ |
|||
RTS PC |
|||
4$: .BYTE ^D0,^D2,^D4,^D6,^D8 |
|||
.BYTE ^D1,^D3,^D5,^D7,^D9 |
|||
.END LUHN</syntaxhighlight> |
|||
{{out}} |
|||
<pre>.LUHN 49927398716 |
|||
PASS |
|||
.LUHN 49927398717 |
|||
Example 2: (vectorized version of the above code) |
|||
FAIL |
|||
.LUHN 1234567812345678 |
|||
{{works with|MATLAB|2007a}} |
|||
FAIL |
|||
<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)))).'))); |
|||
.LUHN 1234567812345670 |
|||
end</lang> |
|||
PASS</pre> |
|||
=={{header|Mathematica}}/{{header|Wolfram Language}}== |
|||
This solution is guaranteed to work in older versions of MATLAB. |
|||
<syntaxhighlight 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} |
|||
Due to the lack of function to change a number into a vector (e.g. 12=[1,2]) this function must be created first. |
|||
->{True, False, False, True}</syntaxhighlight> |
|||
===Alternate Code=== |
|||
Eliminates conversion of numbers to strings and back |
|||
<syntaxhighlight lang="mathematica">LuhnQ[n_Integer] := |
|||
Block[{digits = Reverse@IntegerDigits@n}, |
|||
Mod[Total[{digits[[;; ;; 2]], |
|||
IntegerDigits[2 #] & /@ digits[[2 ;; ;; 2]]}, -1], 10] == 0] |
|||
LuhnQ /@ {49927398716, 49927398717, 1234567812345678, 1234567812345670}</syntaxhighlight> |
|||
num2vec.m |
|||
{{out}}<pre>{True,False,False,True}</pre> |
|||
<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> |
|||
=={{header|MATLAB}}== |
|||
luhn.m |
|||
The solution is basically the same as for [[#Octave|Octave]]. |
|||
<lang MATLAB> |
|||
function |
<syntaxhighlight lang="matlab">function passed = luhn(num) |
||
if nargin == 0 % evaluate test cases |
|||
a=num2vec(a); |
|||
testnum = [49927398716 49927398717 1234567812345678 1234567812345670]; |
|||
N=length(a); |
|||
for |
for num = testnum |
||
disp([int2str(num) ': ' int2str(luhn(num))]) |
|||
b(i)=a(N+1-i); |
|||
end |
|||
return |
|||
end |
end |
||
% luhn function starts here |
|||
for i=1:ceil(N/2) |
|||
d = int2str(num) - '0'; % convert number into vector of digits |
|||
c(i+1)=b(2*i-1); |
|||
m = [2:2:8,1:2:9]; % rule 3: maps 1:9 to [2 4 6 8 1 3 5 7 9] |
|||
end |
|||
passed = ~mod(sum(d(end:-2:1)) + sum(m(d(end-1:-2:1))), 10); |
|||
s1=sum(c); |
|||
end</syntaxhighlight> |
|||
for i=1:floor(N/2) |
|||
{{out}} |
|||
d(i)=sum(num2vec(2*b(2*i))); |
|||
<pre>49927398716: 1 |
|||
end |
|||
49927398717: 0 |
|||
s2=sum(d); |
|||
1234567812345678: 0 |
|||
T=s1+s2; |
|||
1234567812345670: 1</pre> |
|||
</lang> |
|||
=={{header|min}}== |
|||
{{works with|min|0.19.3}} |
|||
<syntaxhighlight lang="min">((dup 10 <) 'quote (((10 mod) (10 div)) cleave) 'cons linrec) :digits |
|||
((0 0) dip (pop 'succ dip over even?) partition ((pop pop) dip) dip) :evens/odds |
|||
((2 * digits sum) map sum) :evens-sum |
|||
(digits evens/odds ('evens-sum 'sum) => spread + 10 mod 0 ==) :luhn? |
|||
(49927398716 49927398717 1234567812345678 1234567812345670) |
|||
(dup print! " " print! luhn? puts!) foreach</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
49927398716 true |
|||
49927398717 false |
|||
1234567812345678 false |
|||
1234567812345670 true |
|||
</pre> |
|||
=={{header|MiniScript}}== |
|||
<syntaxhighlight lang="miniscript">isValid = function(s) |
|||
sum = 0 |
|||
odd = true |
|||
for i in range(s.len-1) |
|||
d = val(s[i]) |
|||
sum = sum + d * (2 - odd) |
|||
if not odd and d > 4 then sum = sum - 9 |
|||
odd = not odd |
|||
end for |
|||
return sum % 10 == 0 |
|||
end function |
|||
test = function(s) |
|||
if isValid(s) then print s + ": valid" else print s + ": invalid" |
|||
end function |
|||
test "49927398716" |
|||
test "49927398717" |
|||
test "1234567812345678" |
|||
test "1234567812345670"</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716: valid |
|||
49927398717: invalid |
|||
1234567812345678: invalid |
|||
1234567812345670: valid</pre> |
|||
=={{header|MUMPS}}== |
=={{header|MUMPS}}== |
||
<lang>LUHN(C) |
<syntaxhighlight lang="mumps">LUHN(C) |
||
NEW ODD,EVEN,S |
NEW ODD,EVEN,S |
||
SET S=$REVERSE(C) |
SET S=$REVERSE(C) |
||
SET ODD=0 FOR I=1:2:$LENGTH(S) SET ODD=ODD+$EXTRACT(S,I) |
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)) |
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)</ |
QUIT '((ODD+EVEN)#10)</syntaxhighlight> |
||
<pre>USER>W !,$S($$LUHN^ROSETTA("49927398716")=0:"INVALID",1:"VALID") |
<pre>USER>W !,$S($$LUHN^ROSETTA("49927398716")=0:"INVALID",1:"VALID") |
||
Line 879: | Line 4,295: | ||
VALID</pre> |
VALID</pre> |
||
=={{header|Nanoquery}}== |
|||
{{trans|Java}} |
|||
<syntaxhighlight lang="nanoquery">def reverse_str(string) |
|||
to_return = "" |
|||
for i in range(len(string) - 1, 0, -1) |
|||
to_return += string[i] |
|||
end for |
|||
return to_return |
|||
end reverse_str |
|||
def luhnTest(number) |
|||
s1 = 0; s2 = 0 |
|||
reversed = reverse_str(number) |
|||
for i in range(0, len(reversed) - 1) |
|||
digit = int(reversed[i]) |
|||
if (i % 2) = 0 |
|||
s1 += digit |
|||
else |
|||
s2 += 2 * digit |
|||
if digit >= 5 |
|||
s2 -= 9 |
|||
end if |
|||
end if |
|||
end for |
|||
return ((s1 + s2) % 10) = 0 |
|||
end luhnTest |
|||
println luhnTest("49927398716") |
|||
println luhnTest("49927398717") |
|||
println luhnTest("1234567812345678") |
|||
println luhnTest("1234567812345670")</syntaxhighlight> |
|||
{{out}} |
|||
<pre>true |
|||
false |
|||
false |
|||
true</pre> |
|||
=={{header|NetRexx}}== |
|||
{{trans|REXX}}<syntaxhighlight 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 |
|||
</syntaxhighlight> |
|||
=={{header|Nim}}== |
|||
<syntaxhighlight lang="nim">proc luhn(cc: string): bool = |
|||
const m = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9] |
|||
var sum = 0 |
|||
var odd = true |
|||
for i in countdown(cc.high, 0): |
|||
let digit = ord(cc[i]) - ord('0') |
|||
sum += (if odd: digit else: m[digit]) |
|||
odd = not odd |
|||
result = sum mod 10 == 0 |
|||
for cc in ["49927398716", "49927398717", "1234567812345678", "1234567812345670"]: |
|||
echo cc, ' ', luhn(cc)</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716 true |
|||
49927398717 false |
|||
1234567812345678 false |
|||
1234567812345670 true</pre> |
|||
=={{header|Objeck}}== |
=={{header|Objeck}}== |
||
<syntaxhighlight lang="objeck">bundle Default { |
|||
<lang objeck> |
|||
bundle Default { |
|||
class Luhn { |
class Luhn { |
||
function : IsValid(cc : String) ~ Bool { |
function : IsValid(cc : String) ~ Bool { |
||
Line 910: | Line 4,415: | ||
} |
} |
||
} |
} |
||
}</syntaxhighlight> |
|||
=={{header|Objective-C}}== |
|||
<syntaxhighlight lang="objc">- (NSArray *) 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; |
|||
} |
} |
||
</lang> |
|||
+ (BOOL) luhnCheck:(NSString *)stringToTest { |
|||
=={{header|OCaml}}== |
|||
NSArray *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[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 |
|||
<lang ocaml>let luhn s = |
|||
BOOL test1 = [self luhnCheck:@"49927398717"]; //Result = NO |
|||
BOOL test2 = [self luhnCheck:@"1234567812345678"]; //Result = NO |
|||
BOOL test3 = [self luhnCheck:@"1234567812345670"]; //Result = YES</syntaxhighlight> |
|||
=={{header|OCaml}}== |
|||
<syntaxhighlight lang="ocaml">let luhn s = |
|||
let rec g r c = function |
let rec g r c = function |
||
| 0 -> r |
| 0 -> r |
||
Line 923: | Line 4,466: | ||
in |
in |
||
(g 0 1 (String.length s)) mod 10 = 0 |
(g 0 1 (String.length s)) mod 10 = 0 |
||
;;</ |
;;</syntaxhighlight> |
||
{{out}} |
|||
<syntaxhighlight lang="ocaml"># List.map luhn [ "49927398716"; "49927398717"; "1234567812345678"; "1234567812345670" ];; |
|||
- : bool list = [true; false; false; true]</syntaxhighlight> |
|||
=={{header|Octave}}== |
|||
<syntaxhighlight lang="octave"> function y = isluhn(s); |
|||
if isnumeric(s) s = mat2str(s); end; % make sure s is a string |
|||
d = s-'0'; % convert string into vector of digits |
|||
m = [2:2:8,1:2:9]; % rule 3: maps [1:9] -> i |
|||
y = ~mod(sum(d(end:-2:1)) + sum(m(d(end-1:-2:1))),10); |
|||
end; </syntaxhighlight> |
|||
{{out}} |
|||
<syntaxhighlight lang="octave"> isluhn('49927398716') |
|||
ans = 1 |
|||
isluhn('49927398717') |
|||
ans = 0 |
|||
isluhn('1234567812345678') |
|||
ans = 0 |
|||
isluhn('1234567812345670') |
|||
ans = 1 |
|||
</syntaxhighlight> |
|||
=={{header|Oforth}}== |
|||
<syntaxhighlight lang="oforth">: luhnTest(n) |
|||
| s i | |
|||
n asString reverse ->s |
|||
0 s size loop: i [ |
|||
i s at asDigit |
|||
i isEven ifTrue: [ 2 * dup 10 >= ifTrue: [ 9 - ] ] + |
|||
] |
|||
10 mod ==0 ; </syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
[ 49927398716, 49927398717, 1234567812345678, 1234567812345670 ] map(#luhnTest) println |
|||
[1, 0, 0, 1] |
|||
</pre> |
|||
=={{header|OpenEdge/Progress}}== |
|||
<syntaxhighlight lang="progress">FUNCTION fnLuhnAlgorithm RETURNS LOGICAL |
|||
(INPUT pcNumber AS CHARACTER): |
|||
/*------------------------------------------------------------------------------ |
|||
Purpose: Applies Luhn Algorithm to check a Number |
|||
Notes: Returns True/False Validation based on check digit |
|||
------------------------------------------------------------------------------*/ |
|||
DEFINE VARIABLE cNum AS CHARACTER NO-UNDO. |
|||
DEFINE VARIABLE iCheck AS INTEGER NO-UNDO. |
|||
DEFINE VARIABLE iLength AS INTEGER NO-UNDO. |
|||
DEFINE VARIABLE iLoopCnt AS INTEGER NO-UNDO. |
|||
DEFINE VARIABLE iNum AS INTEGER NO-UNDO. |
|||
DEFINE VARIABLE iNum1 AS INTEGER NO-UNDO. |
|||
DEFINE VARIABLE iNum2 AS INTEGER NO-UNDO. |
|||
DEFINE VARIABLE iTestLength AS INTEGER NO-UNDO. |
|||
ASSIGN |
|||
iLength = LENGTH(pcNumber) |
|||
iTestLength = iLength - 1 |
|||
iCheck = 1. /* 1 for the check digit we skip */ |
|||
DO iLoopCnt = iTestLength TO 1 BY -1: |
|||
ASSIGN |
|||
iNum = INTEGER(SUBSTR(pcNumber,iLoopCnt,1)) |
|||
iCheck = iCheck + 1. |
|||
IF iCheck MODULO 2 = 1 THEN |
|||
ASSIGN iNum1 = iNum1 + iNum. |
|||
ELSE |
|||
DO: |
|||
ASSIGN iNum2 = iNum * 2. |
|||
IF iNum2 < 10 THEN |
|||
ASSIGN iNum1 = iNum1 + iNum2. |
|||
ELSE |
|||
ASSIGN |
|||
cNum = STRING(iNum2) |
|||
iNum1 = iNum1 + INTEGER(SUBSTR(cNum,1,1)) + INTEGER(SUBSTR(cNum,2,1)). |
|||
END. |
|||
END. |
|||
ASSIGN |
|||
iNum2 = iNum1 * 9 |
|||
iNum = iNum2 MODULO 10. |
|||
IF iNum = INTEGER(SUBSTR(pcNumber,iLength,1)) THEN |
|||
RETURN TRUE. |
|||
ELSE |
|||
RETURN FALSE. |
|||
END FUNCTION. /* fnLuhnAlgorithm */</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716 - yes |
|||
49927398717 - no |
|||
1234567812345678 - no |
|||
1234567812345670 - yes</pre> |
|||
=={{header|Order}}== |
|||
This example highlights Order's unusual treatment of numbers. |
|||
Numbers larger than 100 are not recognized by the interpreter in literal form and must instead be entered as "native" numbers (i.e. listing the separate digits as arguments to <code>8nat</code>). |
|||
Since internally, a native number is just a sequence of the digits in reverse order with an end digit marker, converting the number into a reversed list of digits mainly involves removing this terminator, so that we can immediately treat the digits as number elements. |
|||
<syntaxhighlight lang="c">#include <order/interpreter.h> |
|||
#define ORDER_PP_DEF_8luhn ORDER_PP_FN( \ |
|||
8fn(8N, 8if(8is_seq(8N), 8luhn_wk(8num_to_seq(8N)), 8false)) ) |
|||
#define ORDER_PP_DEF_8num_to_seq ORDER_PP_FN( \ |
|||
8fn(8N, 8seq_push_back(8seq(8seq_last(8N)), 8seq_pop_back(8N))) ) |
|||
#define ORDER_PP_DEF_8luhn_wk ORDER_PP_FN( \ |
|||
8fn(8N, \ |
|||
8lets((8P, 8unzip(8N, 8nil, 8nil, 8true)) \ |
|||
(8O, 8seq_fold(8plus, 0, 8tuple_at_0(8P))) \ |
|||
(8E, 8seq_fold(8plus, 0, \ |
|||
8seq_map(8dig_map, 8tuple_at_1(8P)))), \ |
|||
8is_0(8remainder(8plus(8O, 8E), 10)))) ) |
|||
#define ORDER_PP_DEF_8dig_map ORDER_PP_FN( \ |
|||
8fn(8X, 8tuple_at(8X, 8tuple(0,2,4,6,8,1,3,5,7,9))) ) |
|||
#define ORDER_PP_DEF_8unzip ORDER_PP_FN( \ |
|||
8fn(8S, 8L, 8R, 8O, \ |
|||
8if(8is_nil(8S), \ |
|||
8pair(8L, 8R), \ |
|||
8if(8O, \ |
|||
8unzip(8seq_tail(8S), 8seq_push_back(8seq_head(8S), 8L), \ |
|||
8R, 8false), \ |
|||
8unzip(8seq_tail(8S), 8L, \ |
|||
8seq_push_back(8seq_head(8S), 8R), 8true)))) ) |
|||
ORDER_PP(8seq_to_tuple( |
|||
Sample output |
|||
8seq_map(8luhn, 8seq(8nat(4,9,9,2,7,3,9,8,7,1,6), |
|||
<lang ocaml># List.map luhn [ "49927398716"; "49927398717"; "1234567812345678"; "1234567812345670" ];; |
|||
8nat(4,9,9,2,7,3,9,8,7,1,7), |
|||
- : bool list = [true; false; false; true]</lang> |
|||
8nat(1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8), |
|||
8nat(1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,0))) |
|||
))</syntaxhighlight> |
|||
{{out}} |
|||
<syntaxhighlight lang="text">(8true,8false,8false,8true)</syntaxhighlight> |
|||
=={{header|Oz}}== |
=={{header|Oz}}== |
||
< |
<syntaxhighlight lang="oz">declare |
||
fun {Luhn N} |
fun {Luhn N} |
||
{Sum {List.mapInd {Reverse {Digits N}} |
{Sum {List.mapInd {Reverse {Digits N}} |
||
Line 952: | Line 4,630: | ||
{Map |
{Map |
||
[49927398716 49927398717 1234567812345678 1234567812345670] |
[49927398716 49927398717 1234567812345678 1234567812345670] |
||
Luhn}}</ |
Luhn}}</syntaxhighlight> |
||
=={{header|Pascal}}== |
|||
''see also: [[#Free Pascal|Free Pascal]]'' |
|||
{{works with|Extended Pascal}} |
|||
This implementation skips the ''reversal'' step and simply reads the input backwards. |
|||
<syntaxhighlight lang="pascal">program luhnTestOfCreditCardNumbers(input, output); |
|||
type |
|||
{ |
|||
`string(…)` is an Extended Pascal, ISO 10206, extension. |
|||
`string(64)` discriminates the “schema” data type `string` |
|||
to contain at most 64 `char` values. |
|||
} |
|||
creditCardNumber = string(64); |
|||
{ |
|||
\brief determines whether a string contains digits only |
|||
\param sample the string to inspect |
|||
\return `false` iff `sample` contains non-digit characters |
|||
} |
|||
{ Extended Pascal: `protected` means the function cannot modify `sample` } |
|||
function containsDigitsOnly(protected sample: creditCardNumber): Boolean; |
|||
var |
|||
{ EP: the `… value []` initializes this variable as an empty set } |
|||
characters: set of char value []; |
|||
{ `sample.capacity` refers to `64` in this code. } |
|||
i: 1..sample.capacity; |
|||
begin |
|||
for i := 1 to length(sample) do |
|||
begin |
|||
{ Union of sets indicated by `+`. } |
|||
characters := characters + [sample[i]] |
|||
end; |
|||
{ |
|||
In a Pascal `function` definition, |
|||
there must be one assignment to the (implicit) variable |
|||
bearing the same name as of the function. |
|||
This will be the return value. |
|||
} |
|||
{ NB: This will return `true` even if `length(sample)` is zero. } |
|||
containsDigitsOnly := card(characters - ['0'..'9']) = 0 |
|||
{ `card` is an Extended Pascal extension. } |
|||
end; |
|||
{ |
|||
\brief determines whether a string complies with ISO/IEC 7812-1 Luhn test |
|||
\param sample the potentially correct credit card number |
|||
\return `true` if verification succeeds |
|||
} |
|||
function luhnCheck(protected sample: creditCardNumber): Boolean; |
|||
{ |
|||
This _nested_ function is only accessible _within_ `luhnCheck`. |
|||
Outsourcing this code allows us to write a neat expression below. |
|||
} |
|||
function check: Boolean; |
|||
var |
|||
{ Using `integer` sub-ranges ensures only these values are assigned. } |
|||
sum: 0..maxInt value 0; |
|||
i: 0..sample.capacity-1; |
|||
begin |
|||
for i := 0 to length(sample) - 1 do |
|||
begin |
|||
{ `1 + ord(odd(i))` produces an alternating scale factor `* 1`/`* 2`. } |
|||
sum := sum + (1 + ord(odd(i))) * |
|||
{ Obtain digit value for `integer` calculation. } |
|||
(ord(sample[length(sample) - i]) - ord('0')) - |
|||
{ Reverse operation if digit sum > 9, i.e. we added “too much”. } |
|||
ord(odd(i) and (sample[length(sample) - i] >= '5')) * 9 |
|||
end; |
|||
check := sum mod 10 = 0 |
|||
end; |
|||
begin |
|||
{ |
|||
The Extended Pascal Boolean operator `and_then` (and `or_else`) |
|||
allows for “short-circuit evaluation”. |
|||
Otherwise, in Pascal `and` and `or` mandate complete evaluation. |
|||
} |
|||
luhnCheck := (length(sample) > 0) and_then containsDigitsOnly(sample) |
|||
and_then check |
|||
end; |
|||
{ === MAIN ============================================================= } |
|||
var |
|||
s: creditCardNumber; |
|||
begin |
|||
{ `EOF` is short for `EOF(input)`. } |
|||
while not EOF do |
|||
begin |
|||
readLn(s); { equivalent to `readLn(input, s)` } |
|||
writeLn(luhnCheck(s)) { = `writeLn(output, …)` } |
|||
end |
|||
end.</syntaxhighlight> |
|||
{{in}} |
|||
<pre>49927398716 |
|||
49927398717 |
|||
1234567812345678 |
|||
1234567812345670</pre> |
|||
{{out}} |
|||
<pre>True |
|||
False |
|||
False |
|||
True</pre> |
|||
The shown output was generated by a program compiled with the GPC, the [[GNU Pascal]] Compiler. |
|||
A different compiler may emit a different rendition of <tt>true</tt> and <tt>false</tt>, for example in all-caps. |
|||
=={{header|Perl}}== |
=={{header|Perl}}== |
||
< |
<syntaxhighlight lang="perl">sub luhn_test |
||
{ |
{ |
||
my @rev = reverse split //,$_[0]; |
my @rev = reverse split //,$_[0]; |
||
Line 968: | Line 4,755: | ||
return ($sum1+$sum2) % 10 == 0; |
return ($sum1+$sum2) % 10 == 0; |
||
} |
} |
||
print |
print luhn_test('49927398716'); |
||
print |
print luhn_test('49927398717'); |
||
print |
print luhn_test('1234567812345678'); |
||
print |
print luhn_test('1234567812345670');</syntaxhighlight> |
||
Or using map( ) and a precomputed array: |
Or using map( ) and a precomputed array: |
||
< |
<syntaxhighlight lang="perl">sub luhn { |
||
my (@n,$i,$sum) = split //, reverse $_[0]; |
my (@n,$i,$sum) = split //, reverse $_[0]; |
||
my @a = map {int(2*$_ / 10) + (2*$_ % 10)} (0..9); |
my @a = map {int(2*$_ / 10) + (2*$_ % 10)} (0..9); |
||
Line 984: | Line 4,771: | ||
# Test and display |
# Test and display |
||
map {print luhn($_), ": $_\n"} |
map {print luhn($_), ": $_\n"} |
||
qw(49927398716 49927398717 1234567812345678 1234567812345670);</ |
qw(49927398716 49927398717 1234567812345678 1234567812345670);</syntaxhighlight> |
||
{{out}} |
|||
Output: |
|||
<pre>1: 49927398716 |
<pre>1: 49927398716 |
||
0: 49927398717 |
0: 49927398717 |
||
Line 992: | Line 4,779: | ||
1: 1234567812345670</pre> |
1: 1234567812345670</pre> |
||
=={{header| |
=={{header|Phix}}== |
||
<!--<syntaxhighlight lang="phix">(phixonline)--> |
|||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">Luhn</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">st</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">d</span> |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">st</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #000000;">d</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">st</span><span style="color: #0000FF;">[-</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]-</span><span style="color: #008000;">'0'</span> |
|||
<span style="color: #000000;">s</span> <span style="color: #0000FF;">+=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">mod</span><span style="color: #0000FF;">(</span><span style="color: #000000;">i</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2</span><span style="color: #0000FF;">)?</span><span style="color: #000000;">d</span><span style="color: #0000FF;">,</span><span style="color: #000000;">d</span><span style="color: #0000FF;">*</span><span style="color: #000000;">2</span><span style="color: #0000FF;">-(</span><span style="color: #000000;">d</span><span style="color: #0000FF;">></span><span style="color: #000000;">4</span><span style="color: #0000FF;">)*</span><span style="color: #000000;">9</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #7060A8;">remainder</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">,</span><span style="color: #000000;">10</span><span style="color: #0000FF;">)=</span><span style="color: #000000;">0</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">test</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%20s : %t\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">s</span><span style="color: #0000FF;">,</span><span style="color: #000000;">Luhn</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)})</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
<span style="color: #7060A8;">papply</span><span style="color: #0000FF;">({</span><span style="color: #008000;">"49927398716"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"49927398717"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"1234567812345678"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"1234567812345670"</span><span style="color: #0000FF;">},</span><span style="color: #000000;">test</span><span style="color: #0000FF;">)</span> |
|||
<!--</syntaxhighlight>--> |
|||
{{out}} |
|||
<pre> |
|||
49927398716 : true |
|||
49927398717 : false |
|||
1234567812345678 : false |
|||
1234567812345670 : true |
|||
</pre> |
|||
=={{header|PHP}}== |
|||
{{works with|Rakudo Star|2010.08}} |
|||
{{trans|C}} |
|||
<syntaxhighlight lang="php">$numbers = "49927398716 49927398717 1234567812345678 1234567812345670"; |
|||
foreach (split(' ', $numbers) as $n) |
|||
echo "$n is ", luhnTest($n) ? 'valid' : 'not valid', '</br>'; |
|||
function luhnTest($num) { |
|||
Here we make use of <tt>comb</tt>, which splits into individual characters, and the sequence operator <tt>...</tt>, which can intuit an even or odd sequence from the first two values. |
|||
$len = strlen($num); |
|||
The <tt>[+]</tt> is a reduction metaoperator; with <tt>+</tt> it just sums the list of values. |
|||
for ($i = $len-1; $i >= 0; $i--) { |
|||
<tt>%%</tt> is the divisible-by operator. |
|||
$ord = ord($num[$i]); |
|||
<lang perl6>sub luhn-test ($cc-number --> Bool) { |
|||
if (($len - 1) & $i) { |
|||
my @digits = $cc-number.comb.reverse; |
|||
$sum += $ord; |
|||
my $s1 = [+] @digits[0,2...@digits.end]; |
|||
} else { |
|||
my $s2 = [+] @digits[1,3...@digits.end].map({[+] ($^a * 2).comb}); |
|||
$sum += $ord / 5 + (2 * $ord) % 10; |
|||
} |
|||
} |
|||
return $sum % 10 == 0; |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716 is valid |
|||
49927398717 is not valid |
|||
1234567812345678 is not valid |
|||
1234567812345670 is valid</pre> |
|||
And a more concise example using PHP core methods: |
|||
return ($s1 + $s2) %% 10; |
|||
<syntaxhighlight lang="php">function luhn_test($num) { |
|||
}</lang> |
|||
$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) |
|||
And we can test it like this: |
|||
echo "$n is ", luhn_test($n) ? 'valid' : 'not valid', "</br>\n";</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716 is valid |
|||
49927398717 is not valid |
|||
1234567812345678 is not valid |
|||
1234567812345670 is valid |
|||
</pre> |
|||
=={{header|Picat}}== |
|||
<lang perl6>use Test; |
|||
<syntaxhighlight lang="picat">go => |
|||
Nums = ["49927398716","49927398717","1234567812345678","1234567812345670"], |
|||
foreach (N in Nums) |
|||
println([N, isluhn10(N)]) |
|||
end, |
|||
nl. |
|||
% |
|||
my @cc-numbers = |
|||
% isluhn10(num) returns 1 is valid, else 0 |
|||
'49927398716' => True, |
|||
% |
|||
'49927398717' => False, |
|||
% Assumption: input num is a string. |
|||
'1234567812345678' => False, |
|||
% |
|||
'1234567812345670' => True; |
|||
isluhn10(Num) = V => |
|||
X = [I : I in Num.reverse()] ++ [""], |
|||
Digits = "0246813579", |
|||
M = new_map([(I.to_string()=Digits[I+1]) : I in 0..9]), |
|||
V1 = sum([X[I].to_integer() + M.get2(X[I+1].to_string(),0) : I in 1..2..Num.length]), |
|||
V := cond(V1 mod 10 == 0, 1, 0). |
|||
% A variant of Map.get with conversions |
|||
plan @cc-numbers.elems; |
|||
get2(M, Key, Default)=V => |
|||
if M.has_key(Key) then V= M.get(Key).to_integer() else V=Default end.</syntaxhighlight> |
|||
{{out}} |
|||
for @cc-numbers».kv -> $cc, $expected-result { |
|||
<pre>[49927398716,1] |
|||
is luhn-test($cc), $expected-result, |
|||
[49927398717,0] |
|||
"$cc {$expected-result ?? 'passes' !! 'does not pass'} the Luhn test."; |
|||
[1234567812345678,0] |
|||
}</lang> |
|||
[1234567812345670,1]</pre> |
|||
Output: |
|||
<pre>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.</pre> |
|||
=={{header|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> |
|||
=={{header|PicoLisp}}== |
=={{header|PicoLisp}}== |
||
< |
<syntaxhighlight lang="picolisp">(de luhn (Num) # 'Num' may be a number or a string |
||
(=0 |
(=0 |
||
(% |
(% |
||
Line 1,059: | Line 4,887: | ||
(flip (chop Num)) |
(flip (chop Num)) |
||
'(T NIL .) ) |
'(T NIL .) ) |
||
10 ) ) )</ |
10 ) ) )</syntaxhighlight> |
||
{{out}} |
|||
Output: |
|||
<pre>: (mapcar luhn (49927398716 49927398717 1234567812345678 1234567812345670)) |
<pre>: (mapcar luhn (49927398716 49927398717 1234567812345678 1234567812345670)) |
||
-> (0 NIL NIL 0)</pre> |
-> (0 NIL NIL 0)</pre> |
||
=={{header|PL/I}}== |
=={{header|PL/I}}== |
||
<syntaxhighlight lang="pli">test: procedure options (main); |
|||
<lang PL/I> |
|||
test: procedure options (main); |
|||
declare (cardnumber, rcn) character (20) varying; |
declare (cardnumber, rcn) character (20) varying; |
||
Line 1,089: | Line 4,916: | ||
put skip edit (cardnumber, ' does not pass the Luhn test' )(a); |
put skip edit (cardnumber, ' does not pass the Luhn test' )(a); |
||
put skip list (s1 + s2); |
put skip list (s1 + s2); |
||
end test; |
end test;</syntaxhighlight> |
||
</lang> |
|||
{{out}} |
|||
Output: |
|||
< |
<pre> |
||
49927398716 passes the Luhn test |
49927398716 passes the Luhn test |
||
70 |
70 |
||
Line 1,101: | Line 4,928: | ||
1234567812345670 passes the Luhn test |
1234567812345670 passes the Luhn test |
||
60 |
60 |
||
</ |
</pre> |
||
Comment: it isn't necessary to reverse the string |
|||
to perform the test. |
in order to perform the test. |
||
=={{header|PL/M}}== |
|||
<syntaxhighlight lang="pli">100H: |
|||
BDOS: PROCEDURE(F,A); DECLARE F BYTE, A ADDRESS; GO TO 5; END BDOS; |
|||
EXIT: PROCEDURE; GO TO 0; END EXIT; |
|||
PRINT: PROCEDURE(S); DECLARE S ADDRESS; CALL BDOS(9,S); END PRINT; |
|||
LUHN: PROCEDURE(NUM) BYTE; |
|||
DECLARE MAP DATA (0, 2, 4, 6, 8, 1, 3, 5, 7, 9); |
|||
DECLARE (START, NUM, MASK, TOTAL) ADDRESS; |
|||
DECLARE (CHR BASED NUM, DGT) BYTE; |
|||
START = NUM; |
|||
DO WHILE CHR <> '$'; NUM = NUM + 1; END; |
|||
MASK = NUM := NUM - 1; |
|||
TOTAL = 0; |
|||
DO WHILE NUM >= START; |
|||
DGT = CHR - '0'; |
|||
IF NUM XOR MASK THEN DGT = MAP(DGT); |
|||
TOTAL = TOTAL + DGT; |
|||
NUM = NUM - 1; |
|||
END; |
|||
RETURN TOTAL MOD 10 = 0; |
|||
END LUHN; |
|||
DECLARE TEST (4) ADDRESS, I BYTE; |
|||
TEST(0) = .'49927398716$'; |
|||
TEST(1) = .'49927398717$'; |
|||
TEST(2) = .'1234567812345678$'; |
|||
TEST(3) = .'1234567812345670$'; |
|||
DO I=0 TO LAST(TEST); |
|||
CALL PRINT(TEST(I)); |
|||
CALL PRINT(.': $'); |
|||
IF LUHN(TEST(I)) |
|||
THEN CALL PRINT(.'PASS$'); |
|||
ELSE CALL PRINT(.'FAIL$'); |
|||
CALL PRINT(.(13,10,'$')); |
|||
END; |
|||
CALL EXIT; |
|||
EOF</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716: PASS |
|||
49927398717: FAIL |
|||
1234567812345678: FAIL |
|||
1234567812345670: PASS</pre> |
|||
=={{header|PL/SQL}}== |
|||
<syntaxhighlight lang="plsql">FUNCTION algoLuhn ( p_numeroVerif VARCHAR2 ) |
|||
RETURN NUMBER |
|||
IS |
|||
i NUMBER; |
|||
v_NBi SMALLINT; |
|||
v_retour SMALLINT; |
|||
v_somme NUMBER := 0; |
|||
v_nbCar NUMBER; |
|||
BEGIN |
|||
v_nbCar := LENGTH(p_numeroVerif); |
|||
FOR i IN 1..v_nbCar |
|||
LOOP |
|||
v_NBi := TO_NUMBER(SUBSTR(p_numeroVerif,v_nbCar+1-i,1)); |
|||
v_somme := v_somme |
|||
+ MOD(i,2) * v_NBi |
|||
+ MOD(i+1,2) * SIGN(-SIGN(v_Nbi-4)+1) * (2*v_NBi) |
|||
+ MOD(i+1,2) * SIGN( SIGN(v_Nbi-5)+1) * (2*v_NBi-9); |
|||
END LOOP; |
|||
v_retour := SIGN(MOD(v_somme,10)); |
|||
RETURN v_retour; |
|||
EXCEPTION |
|||
WHEN OTHERS |
|||
THEN |
|||
RETURN 1; |
|||
END algoLuhn;</syntaxhighlight> |
|||
=={{header|Plain English}}== |
|||
<syntaxhighlight lang="plainenglish">To run: |
|||
Start up. |
|||
Test whether "49927398716" will pass the luhn test. |
|||
Test whether "49927398717" will pass the luhn test. |
|||
Test whether "1234567812345678" will pass the luhn test. |
|||
Test whether "1234567812345670" will pass the luhn test. |
|||
Wait for the escape key. |
|||
Shut down. |
|||
To test whether a string will pass the luhn test: |
|||
Write the string then ": " on the console without advancing. |
|||
If the string will pass the luhn test, write "valid" on the console; exit. |
|||
Write "invalid" on the console. |
|||
A sum is a number. |
|||
To decide if a string will pass the luhn test: |
|||
If the string is blank, say no. |
|||
Privatize the string. |
|||
Reverse the string. |
|||
Put 0 into a first sum. |
|||
Put 0 into a second sum. |
|||
Put 1 into a count. |
|||
Slap a rider on the string. |
|||
Loop. |
|||
If the rider's source is blank, break. |
|||
Put the rider's source's first's target into a byte. |
|||
If the byte is not any digit, say no. |
|||
Convert the byte to a number. |
|||
If the count is odd, add the number to the first sum; bump the count; bump the rider; repeat. |
|||
Double the number. |
|||
If the number is greater than 9, digital root the number. |
|||
Add the number to the second sum. |
|||
Bump the count. Bump the rider. |
|||
Repeat. |
|||
If the first sum plus the second sum is evenly divisible by 10, say yes. |
|||
Say no. |
|||
To digital root a number: |
|||
Divide the number by 10 giving a quotient and a remainder. |
|||
Put the quotient plus the remainder into the number. |
|||
To convert a byte to a number: |
|||
If the byte is any digit, put the byte minus 48 into the number.</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
49927398716: valid |
|||
49927398717: invalid |
|||
1234567812345678: invalid |
|||
1234567812345670: valid |
|||
</pre> |
|||
=={{header|PowerBASIC}}== |
|||
{{trans|Visual Basic}} |
|||
<syntaxhighlight lang="powerbasic">#COMPILE EXE |
|||
#DIM ALL |
|||
#COMPILER PBCC 6 |
|||
FUNCTION LuhnCheckPassed(BYVAL dgts AS STRING) AS INTEGER |
|||
LOCAL i, s, s1 AS DWORD |
|||
dgts = STRREVERSE$(dgts) |
|||
FOR i = 1 TO LEN(dgts) STEP 2 |
|||
s += VAL(MID$(dgts, i, 1)) |
|||
NEXT i |
|||
FOR i = 2 TO LEN(dgts) STEP 2 |
|||
s1 = 2 * VAL(MID$(dgts, i, 1)) |
|||
IF s1 >= 10 THEN |
|||
s -= 9 |
|||
END IF |
|||
s += s1 |
|||
NEXT i |
|||
FUNCTION = NOT ISTRUE (s MOD 10) |
|||
END FUNCTION |
|||
FUNCTION PBMAIN () AS LONG |
|||
' this test is expected to pass: |
|||
CON.PRINT IIF$(LuhnCheckPassed("49927398716"), "passed", "failed") |
|||
' this test is expected to fail: |
|||
CON.PRINT IIF$(LuhnCheckPassed("49927398717"), "passed", "failed") |
|||
' this test is expected to fail: |
|||
CON.PRINT IIF$(LuhnCheckPassed("1234567812345678"), "passed", "failed") |
|||
' this test is expected to pass: |
|||
CON.PRINT IIF$(LuhnCheckPassed("1234567812345670"), "passed", "failed") |
|||
END FUNCTION</syntaxhighlight> |
|||
{{out}} |
|||
<pre>passed |
|||
failed |
|||
failed |
|||
passed</pre> |
|||
=={{header|PowerShell}}== |
|||
<syntaxhighlight lang="powershell"> |
|||
function Test-LuhnNumber |
|||
{ |
|||
<# |
|||
.SYNOPSIS |
|||
Tests validity of credit card numbers. |
|||
.DESCRIPTION |
|||
Tests validity of credit card numbers using the Luhn test. |
|||
.PARAMETER Number |
|||
The number must be 11 or 16 digits. |
|||
.EXAMPLE |
|||
Test-LuhnNumber 49927398716 |
|||
.EXAMPLE |
|||
[int64[]]$numbers = 49927398716, 49927398717, 1234567812345678, 1234567812345670 |
|||
C:\PS>$numbers | ForEach-Object { |
|||
"{0,-17}: {1}" -f $_,"$(if(Test-LuhnNumber $_) {'Is valid.'} else {'Is not valid.'})" |
|||
} |
|||
#> |
|||
[CmdletBinding()] |
|||
[OutputType([bool])] |
|||
Param |
|||
( |
|||
[Parameter(Mandatory=$true, |
|||
Position=0)] |
|||
[ValidateScript({$_.Length -eq 11 -or $_.Length -eq 16})] |
|||
[ValidatePattern("^\d+$")] |
|||
[string] |
|||
$Number |
|||
) |
|||
$digits = ([Regex]::Matches($Number,'.','RightToLeft')).Value |
|||
$digits | |
|||
ForEach-Object ` |
|||
-Begin {$i = 1} ` |
|||
-Process {if ($i++ % 2) {$_}} | |
|||
ForEach-Object ` |
|||
-Begin {$sumOdds = 0} ` |
|||
-Process {$sumOdds += [Char]::GetNumericValue($_)} |
|||
$digits | |
|||
ForEach-Object ` |
|||
-Begin {$i = 0} ` |
|||
-Process {if ($i++ % 2) {$_}} | |
|||
ForEach-Object ` |
|||
-Process {[Char]::GetNumericValue($_) * 2} | |
|||
ForEach-Object ` |
|||
-Begin {$sumEvens = 0} ` |
|||
-Process { |
|||
$_number = $_.ToString() |
|||
if ($_number.Length -eq 1) |
|||
{ |
|||
$sumEvens += [Char]::GetNumericValue($_number) |
|||
} |
|||
elseif ($_number.Length -eq 2) |
|||
{ |
|||
$sumEvens += [Char]::GetNumericValue($_number[0]) + [Char]::GetNumericValue($_number[1]) |
|||
} |
|||
} |
|||
($sumOdds + $sumEvens).ToString()[-1] -eq "0" |
|||
} |
|||
</syntaxhighlight> |
|||
<syntaxhighlight lang="powershell"> |
|||
Test-LuhnNumber 49927398716 |
|||
</syntaxhighlight> |
|||
{{Out}} |
|||
<pre> |
|||
True |
|||
</pre> |
|||
<syntaxhighlight lang="powershell"> |
|||
49927398716, 49927398717, 1234567812345678, 1234567812345670 | ForEach-Object { |
|||
"{0,-17}: {1}" -f $_,"$(if(Test-LuhnNumber $_) {'Is valid.'} else {'Is not valid.'})" |
|||
} |
|||
</syntaxhighlight> |
|||
{{Out}} |
|||
<pre> |
|||
49927398716 : Is valid. |
|||
49927398717 : Is not valid. |
|||
1234567812345678 : Is not valid. |
|||
1234567812345670 : Is valid. |
|||
</pre> |
|||
=={{header|PureBasic}}== |
=={{header|PureBasic}}== |
||
< |
<syntaxhighlight lang="purebasic">DataSection |
||
Sample: |
Sample: |
||
Data.s "49927398716" |
Data.s "49927398716" |
||
Line 1,160: | Line 5,242: | ||
Input() |
Input() |
||
CloseConsole() |
CloseConsole() |
||
EndIf</ |
EndIf</syntaxhighlight> |
||
{{out}} |
|||
Sample output: |
|||
<pre>49927398716 is valid |
<pre>49927398716 is valid |
||
49927398717 is not valid |
49927398717 is not valid |
||
Line 1,168: | Line 5,250: | ||
=={{header|Python}}== |
=={{header|Python}}== |
||
===Functional=== |
|||
The divmod in the function below conveniently splits a number into its two digits ready for summing: |
|||
The [http://docs.python.org/py3k/library/functions.html#divmod divmod] in the function below conveniently splits a number into its two digits ready for summing: |
|||
<lang python>>>> def luhn(n): |
|||
<syntaxhighlight lang="python">>>> def luhn(n): |
|||
r = [int(ch) for ch in str(n)][::-1] |
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 |
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): |
>>> for n in (49927398716, 49927398717, 1234567812345678, 1234567812345670): |
||
print(n, luhn(n))</ |
print(n, luhn(n))</syntaxhighlight> |
||
{{out}} |
|||
'''Sample output:''' |
|||
<pre>49927398716 True |
<pre>49927398716 True |
||
49927398717 False |
49927398717 False |
||
1234567812345678 False |
1234567812345678 False |
||
1234567812345670 True |
1234567812345670 True</pre> |
||
</pre> |
|||
Or, using itertools ''cycle'' with map and reduce: |
|||
{{Works with|Python|3.7}} |
|||
<syntaxhighlight lang="python">'''Luhn test of credit card numbers''' |
|||
from operator import add, mul |
|||
from functools import reduce |
|||
from itertools import cycle |
|||
# luhn :: Integer -> Bool |
|||
def luhn(n): |
|||
'''True if n is a valid Luhn credit card number.''' |
|||
def divMod10Sum(a, x): |
|||
return a + add(*divmod(x, 10)) |
|||
return 0 == reduce( |
|||
divMod10Sum, |
|||
map( |
|||
mul, |
|||
cycle([1, 2]), |
|||
map(int, reversed(str(n))) |
|||
), |
|||
0 |
|||
) % 10 |
|||
# ---------------------------TEST--------------------------- |
|||
# main :: IO () |
|||
def main(): |
|||
'''Tests''' |
|||
print(list( |
|||
map(luhn, [ |
|||
49927398716, 49927398717, |
|||
1234567812345678, 1234567812345670 |
|||
]) |
|||
)) |
|||
if __name__ == '__main__': |
|||
main()</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>[True, False, False, True]</pre> |
|||
Or alternatively, we can prune out some of the imports, define the Luhn predicate over strings rather than integers, |
|||
and cycle lambdas rather than integers. |
|||
<syntaxhighlight lang="python">'''Luhn test of credit card numbers''' |
|||
from itertools import cycle |
|||
# luhn :: String -> Bool |
|||
def luhn(k): |
|||
'''True if k is a valid Luhn credit card number string |
|||
''' |
|||
def asDigits(s): |
|||
return (int(c) for c in s) |
|||
return 0 == sum(map( |
|||
lambda f, x: f(x), |
|||
cycle([ |
|||
lambda n: n, |
|||
lambda n: sum(asDigits(str(2 * n))) |
|||
]), |
|||
asDigits(reversed(k)) |
|||
)) % 10 |
|||
# ------------------------- TEST ------------------------- |
|||
# main :: IO () |
|||
def main(): |
|||
'''Tests''' |
|||
print('\n'.join([ |
|||
repr((x, luhn(x))) for x in [ |
|||
"49927398716", |
|||
"49927398717", |
|||
"1234567812345678", |
|||
"1234567812345670" |
|||
] |
|||
])) |
|||
if __name__ == '__main__': |
|||
main() |
|||
</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>('49927398716', True) |
|||
('49927398717', False) |
|||
('1234567812345678', False) |
|||
('1234567812345670', True)</pre> |
|||
===Procedural=== |
|||
Without usingsum() and divmod() functions: |
|||
<syntaxhighlight lang="python">>>> def vérifLuhn(ch): |
|||
sum = 0 |
|||
chParity = len(ch) % 2 |
|||
for i in range (len(ch)-1, -1, -1): |
|||
j = int(ch[i]) |
|||
if ((i + 1) % 2 != chParity): |
|||
j = j * 2 |
|||
if (j > 9): |
|||
j = j - 9 |
|||
sum = sum + j |
|||
print("value calculated = ", str(sum)) |
|||
return sum % 10 == 0 |
|||
for n in (49927398716, 49927398717, 1234567812345678, 1234567812345670): |
|||
print (str(n)+" =>", vérifLuhn(str(n))) |
|||
</syntaxhighlight> |
|||
=={{header|Q}}== |
|||
<syntaxhighlight lang="q">sd:{s:0; while[x<>0; s+:x mod 10; x:floor x%10]; s} / Sum digits of x |
|||
luhn:{ |
|||
r:reverse string x; / Reversed credit card number |
|||
o:("I"$) each r[2*til ceiling (count r) % 2]; / Odd-indexed numbers |
|||
e:("I"$) each r[1+2*til floor (count r) % 2]; / Even-indexed numbers |
|||
0=(sum o,sd each e*2) mod 10 / Return 1b if checksum ends in 0; 0b otherwise |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>q)luhn each 49927398716 49927398717 1234567812345678 1234567812345670 |
|||
1001b</pre> |
|||
=={{header|Quackery}}== |
|||
<syntaxhighlight lang="quackery"> [ 1 & ] is odd ( n --> b ) |
|||
[ [] swap |
|||
[ 10 /mod |
|||
rot join swap |
|||
dup 0 = until ] |
|||
drop ] is digits ( n --> [ ) |
|||
[ [] [] rot |
|||
dup size odd dip |
|||
[ witheach |
|||
[ nested join |
|||
swap ] ] |
|||
not if swap ] is unzip ( [ --> [ [ ) |
|||
[ [] swap |
|||
witheach |
|||
[ 2 * |
|||
10 /mod + |
|||
join ] ] is 2*digitsum ( [ --> [ ) |
|||
[ behead swap |
|||
witheach + ] is sum ( [ --> n ) |
|||
[ digits |
|||
reverse |
|||
unzip |
|||
2*digitsum |
|||
sum |
|||
swap sum |
|||
+ 10 mod 0 = ] is luhn ( n --> b ) |
|||
' [ 49927398716 49927398717 |
|||
1234567812345678 1234567812345670 ] |
|||
witheach |
|||
[ dup echo |
|||
luhn iff |
|||
[ say " valid" ] |
|||
else |
|||
[ say " invalid" ] |
|||
cr ]</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716 valid |
|||
49927398717 invalid |
|||
1234567812345678 invalid |
|||
1234567812345670 valid</pre> |
|||
=={{header|R}}== |
=={{header|R}}== |
||
<syntaxhighlight lang="rsplus"> |
|||
<lang R>luhnTest <- function(cc){ |
|||
is.luhn <- function(cc){ |
|||
# Reverse the digits, convert to numeric vector |
|||
numbers <- as.numeric(rev(unlist(strsplit(cc,"")))) |
|||
(sum(numbers[seq(1,length(numbers),by=2)]) + sum({numbers[seq(2,length(numbers),by=2)]*2 ->.; .%%10 +.%/%10})) %% 10 == 0 |
|||
} |
|||
sapply(c("49927398716","49927398717","1234567812345678","1234567812345670"),is.luhn) |
|||
s1 <- 0 |
|||
s2 <- 0 |
|||
</syntaxhighlight> |
|||
for (index in 1:length(cc2)){ |
|||
{{out}} |
|||
if (index %% 2 == 1){ |
|||
<pre>sapply(c("49927398716","49927398717","1234567812345678","1234567812345670"),is.luhn) |
|||
s1 <- sum(s1, cc2[index]) |
|||
49927398716 49927398717 1234567812345678 1234567812345670 |
|||
} else if (cc2[index] >= 5){ |
|||
TRUE FALSE FALSE TRUE </pre> |
|||
s2 <- sum(s2, (cc2[index]*2 - 9)) |
|||
} else { |
|||
=={{header|Racket}}== |
|||
s2 <- sum(s2, (cc2[index]*2)) |
|||
} |
|||
<syntaxhighlight lang="racket"> |
|||
} |
|||
#lang racket |
|||
return ((s1 + s2) %% 10 == 0) |
|||
(define (luhn-test n) |
|||
(let loop ([n n] [odd? #t] [s 0]) |
|||
(if (zero? n) |
|||
(zero? (modulo s 10)) |
|||
(let*-values ([(q r) (quotient/remainder n 10)] |
|||
[(rq rr) (quotient/remainder (* (if odd? 1 2) r) 10)]) |
|||
(loop q (not odd?) (+ s rq rr)))))) |
|||
(map luhn-test '(49927398716 49927398717 1234567812345678 1234567812345670)) |
|||
;; -> '(#t #f #f #t) |
|||
</syntaxhighlight> |
|||
=={{header|Raku}}== |
|||
(formerly Perl 6) |
|||
{{Works with|rakudo|2015-11-29}} |
|||
Here we make use of <tt>comb</tt>, which splits into individual characters, |
|||
and the sequence operator <tt>...</tt>, which can intuit an even or odd sequence from the first two values. |
|||
<tt>%%</tt> is the divisible-by operator. |
|||
<!-- |
|||
NOTE FOR EDITORS: |
|||
This function is also referenced by [[Validate_International_Securities_Identification_Number#Raku]]. |
|||
If you rename it or change its behavior, make sure to update that task as well. |
|||
--> |
|||
<syntaxhighlight lang="raku" line>sub luhn-test ($number --> Bool) { |
|||
my @digits = $number.comb.reverse; |
|||
my $sum = @digits[0,2...*].sum |
|||
+ @digits[1,3...*].map({ |($_ * 2).comb }).sum; |
|||
return $sum %% 10; |
|||
} |
} |
||
</lang> |
|||
# And we can test it like this: |
|||
Sample Output |
|||
<lang> |
|||
sapply(c(49927398716, 49927398717, 1234567812345678, 1234567812345670), luhnTest) |
|||
[1] TRUE FALSE FALSE TRUE |
|||
</lang> |
|||
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."; |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>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.</pre> |
|||
=={{header|Refal}}== |
|||
<syntaxhighlight lang="refal">$ENTRY Go { |
|||
= <Test '49927398716'> |
|||
<Test '49927398717'> |
|||
<Test '1234567812345678'> |
|||
<Test '1234567812345670'>; |
|||
}; |
|||
Test { |
|||
e.Digits = <Prout e.Digits ': ' <Luhn e.Digits>>; |
|||
}; |
|||
Luhn { |
|||
(s.Sum) e.Digits s.Even s.Odd, |
|||
<Mul 2 <Numb s.Even>>: s.Even2, |
|||
<Divmod s.Even2 10>: (s.EvenD1) s.EvenD2, |
|||
<+ s.EvenD1 s.EvenD2>: s.EvenV, |
|||
<+ <Numb s.Odd> s.EvenV>: s.Step |
|||
= <Luhn (<+ s.Sum s.Step>) e.Digits>; |
|||
(s.Sum) s.Odd = <Luhn (<+ s.Sum <Numb s.Odd>>)>; |
|||
(s.Sum), <Divmod s.Sum 10>: (s.Rest) s.Last, |
|||
s.Last: { |
|||
0 = Valid; |
|||
s.X = Invalid; |
|||
}; |
|||
e.Digits = <Luhn (0) e.Digits>; |
|||
};</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716: Valid |
|||
49927398717: Invalid |
|||
1234567812345678: Invalid |
|||
1234567812345670: Valid</pre> |
|||
=={{header|REXX}}== |
=={{header|REXX}}== |
||
===version 1=== |
|||
<lang REXX> |
|||
/*REXX program |
<syntaxhighlight lang="rexx">/*REXX program validates credit card numbers using the Luhn algorithm. */ |
||
#.=; #.1= 49927398716 /*the 1st sample credit card number. */ |
|||
#.2= 49927398717 /* " 2nd " " " " */ |
|||
#.3= 1234567812345678 /* " 3rd " " " " */ |
|||
#.4= 1234567812345670 /* " 4th " " " " */ |
|||
do k=1 while #.k\=='' /*validate all the credit card numbers.*/ |
|||
say right( Luhn(#.k), 9) ' the Luhn test, credit card number: ' #.k |
|||
end /*k*/ /* [↑] function returns passed│flunked.*/ |
|||
exit /*stick a fork in it, we're all done. */ |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
Luhn: procedure; parse arg x; $= 0 /*get credit card number; zero $ sum. */ |
|||
y= reverse( left(0, length(x) // 2)x) /*add leading zero if needed, reverse. */ |
|||
do j=1 to length(y)-1 by 2; _= substr(y, j+1, 1) * 2 |
|||
$= $ + substr(y, j, 1) + left(_, 1) + substr(_, 2, 1, 0) /* ◄────────┐*/ |
|||
end /*j*/ /*sum odd and even decimal digits ►────┘*/ |
|||
return word('passed flunked',1+($//10==0)) /*$ ending in zero? Then the # passed.*/</syntaxhighlight> |
|||
{{out|output|text= when using the (internal) default inputs:}} |
|||
<pre> |
|||
flunked the Luhn test, credit card number: 49927398716 |
|||
passed the Luhn test, credit card number: 49927398717 |
|||
passed the Luhn test, credit card number: 1234567812345678 |
|||
flunked the Luhn test, credit card number: 1234567812345670 |
|||
</pre> |
|||
===Version 2=== |
|||
cc.=0 /*define default value of zero for CCs.*/ |
|||
<syntaxhighlight lang="rexx">/* Rexx *************************************************** |
|||
cc.1='49927398716' /*sample credit card number one. */ |
|||
* 09.04.2013 Walter Pachl |
|||
cc.2='49927398717' /*sample credit card number two. */ |
|||
* Implements the task's description in a rather concise way |
|||
cc.3='1234567812345678' /*sample credit card number three. */ |
|||
* Instead of reverting the ccn work it backwards |
|||
cc.4='1234567812345670' /*sample credit card number four. */ |
|||
**********************************************************/ |
|||
numeric digits 20 |
|||
push 49927398716 |
|||
do k=1 while cc.k\==0 |
|||
push 49927398717 |
|||
r=checksum(cc.k) |
|||
push 1234567812345678 |
|||
if r==0 then say right(cc.k,20) ' passed the Luhn test.' |
|||
push 1234567812345670 |
|||
else say right(cc.k,20) " flunked the Luhn test!" |
|||
end |
|||
do while queued() > 0 |
|||
parse pull ccnum |
|||
if luhn(ccnum) then ln = 'passed' |
|||
else ln = 'failed' |
|||
say right(ccnum, 20) ln |
|||
end |
|||
return |
|||
exit |
exit |
||
luhn: |
|||
/*-----Luhn algorith checksum for credit card numbers-----*/ |
|||
Parse Arg ccn /* credit card number */ |
|||
checksum: procedure; parse arg t |
|||
sum=0 /* initialize test sum */ |
|||
even=0 /* even indicator */ |
|||
t=reverse(t) |
|||
Do i=length(ccn) To 1 By -1 /* process all digits */ |
|||
s=0 |
|||
c=substr(ccn,i,1) /* pick one digit at a time */ |
|||
If even Then Do /* even numbered digit */ |
|||
c=c*2 /* double it */ |
|||
If c>=10 Then /* 10, 12, 14, 16, 18 */ |
|||
c=c-9 /* Sum of the two digits */ |
|||
End /* end of even numbered */ |
|||
even=\even /* flip even indicator */ |
|||
sum=sum+c /* add into test sum */ |
|||
End |
|||
Return right(sum,1)=0 /* ok if last digit is 0 */ </syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
1234567812345670 passed |
|||
1234567812345678 failed |
|||
49927398717 failed |
|||
49927398716 passed |
|||
</pre> |
|||
=={{header|Ring}}== |
|||
do j=1 to length(t)-1 by 2 |
|||
<syntaxhighlight lang="ring">decimals(0) |
|||
q=2*substr(t,j+1,1) |
|||
if q>9 then q=left(q,1)+right(q,1) |
|||
test = ["49927398716", "49927398717", "1234567812345678", "1234567812345670"] |
|||
s=s+substr(t,j,1)+q |
|||
for n = 1 to len(test) |
|||
end |
|||
see test[n] + " -> " + cardtest(test[n]) + nl |
|||
next |
|||
func cardtest(numstr) |
|||
revstring = revstr(numstr) |
|||
s1 = revodd(revstring) |
|||
s2 = reveven(revstring) |
|||
s3 =right(string(s1+s2), 1) |
|||
if s3 = "0" |
|||
return "Valid" |
|||
else |
|||
return "Invalid" |
|||
ok |
|||
func revstr(str) |
|||
strnew = "" |
|||
for nr = len(str) to 1 step -1 |
|||
strnew = strnew + str[nr] |
|||
next |
|||
return strnew |
|||
func revodd(str) |
|||
strnew = "" |
|||
for nr = 1 to len(str) step 2 |
|||
strnew = strnew + str[nr] |
|||
next |
|||
sumodd = 0 |
|||
for p = 1 to len(strnew) |
|||
sumodd = sumodd + number(strnew[p]) |
|||
next |
|||
return sumodd |
|||
func reveven(str) |
|||
return s//10\==0 |
|||
strnew = "" |
|||
</lang> |
|||
for nr = 2 to len(str) step 2 |
|||
strnew = strnew + str[nr] |
|||
next |
|||
lsteven = [] |
|||
for p = 1 to len(strnew) |
|||
add(lsteven, string(2*number(strnew[p]))) |
|||
next |
|||
arreven = list(len(lsteven)) |
|||
for q = 1 to len(lsteven) |
|||
sum = 0 |
|||
for w = 1 to len(lsteven[q]) |
|||
sum = sum + lsteven[q][w] |
|||
next |
|||
arreven[q] = sum |
|||
next |
|||
sumarr = 0 |
|||
for x = 1 to len(arreven) |
|||
sumarr = sumarr + arreven[x] |
|||
next |
|||
return sumarr |
|||
</syntaxhighlight> |
|||
Output: |
Output: |
||
<pre> |
|||
<pre style="height:30ex;overflow:scroll"> |
|||
49927398716 -> Valid |
|||
49927398717 -> Invalid |
|||
1234567812345678 -> Invalid |
|||
1234567812345670 -> Valid |
|||
</pre> |
|||
=={{header|RPL}}== |
|||
Card numbers shall be entered as strings to avoid any rounding error when testing long ones. |
|||
{{works with|RPL|HP48-C}} |
|||
{| class="wikitable" |
|||
! RPL code |
|||
! Comment |
|||
|- |
|||
| |
|||
« 0 → card even |
|||
« 0 |
|||
card SIZE 1 '''FOR''' j |
|||
card j DUP SUB OBJ→ |
|||
'''IF''' even '''THEN''' |
|||
DUP + 10 MOD LASTARG / IP + '''END''' |
|||
+ 1 'even' STO- |
|||
-1 '''STEP''' |
|||
10 MOD NOT |
|||
» » '<span style="color:blue">LUHN?</span>' STO |
|||
| |
|||
<span style="color:blue">LUHN?</span> ''( "card_number" -- boolean ) '' |
|||
sum = 0 |
|||
loop for j=n to 1 |
|||
digit = card[j] |
|||
if even digit |
|||
multiply it by 2 and add digits |
|||
sum += digit ; reverse parity flag |
|||
return not(sum mod 10) |
|||
|} |
|||
{ "49927398716" "49927398717" "1234567812345678" "1234567812345670" } 1 « <span style="color:blue">LUHN?</span> » DOLIST |
|||
{{out}} |
|||
<pre> |
|||
1: { 1 0 0 1 } |
|||
</pre> |
</pre> |
||
=={{header|Ruby}}== |
=={{header|Ruby}}== |
||
< |
<syntaxhighlight lang="ruby"> def luhn_valid?(str) |
||
str.scan(/\d/).reverse #using str.to_i.digits fails for cases with leading zeros |
|||
s1 = s2 = 0 |
|||
.each_slice(2) |
|||
.sum { |i, k = 0| i.to_i + ((k.to_i)*2).digits.sum } |
|||
s1 += odd.to_i |
|||
.modulo(10).zero? |
|||
end |
|||
["49927398716", "49927398717", "1234567812345678", "1234567812345670"].map{ |i| luhn_valid?(i) } |
|||
double = even.to_i * 2 |
|||
</syntaxhighlight> |
|||
double -= 9 if double >= 10 |
|||
s2 += double |
|||
{{out}} |
|||
end |
|||
<pre>[true, false, false, true]</pre> |
|||
(s1 + s2) % 10 == 0 |
|||
Simpler Alternative |
|||
<syntaxhighlight lang="ruby">def luhn_valid?(n) # Card values can be numbers or strings |
|||
d2sum = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9] |
|||
sum, num = 0, n.to_i |
|||
num.digits.each_with_index { |digit, i| sum += i.even? ? digit : d2sum[digit] } |
|||
sum % 10 == 0 |
|||
end |
end |
||
[49927398716, 49927398717, 1234567812345678, 1234567812345670] |
cards = [49927398716, "49927398717", 1234567812345678, "1234567812345670"] |
||
cards.each{ |i| puts "#{i}: #{luhn_valid?(i)}" }</syntaxhighlight> |
|||
p [n, luhn(n)] |
|||
end</lang> |
|||
{{out}} |
|||
outputs |
|||
<pre> |
<pre>49927398716: true |
||
49927398717: false |
|||
1234567812345678: false |
|||
1234567812345670: true</pre> |
|||
=={{header| |
=={{header|Run BASIC}}== |
||
<syntaxhighlight lang="runbasic">card$(1) = "49927398716" |
|||
Same method as used in C code |
|||
card$(2) = "49927398717" |
|||
<lang scala> |
|||
card$(3) = "1234567812345678" |
|||
def luhnTest(number: String): Boolean = { |
|||
card$(4) = "1234567812345670" |
|||
var odd = true |
|||
var sum = 0 |
|||
for i = 1 to 4 |
|||
for (int <- number.reverse.map{ i => i.toString.toInt }) { |
|||
print card$(i);" ";luhn$(card$(i)) |
|||
if (odd) |
|||
next i |
|||
sum = sum + int |
|||
else |
|||
sum = sum + (int * 2 % 10) + (int / 5) |
|||
FUNCTION luhn$(card$) |
|||
odd = !odd |
|||
lc = len(card$) |
|||
for i = lc to 1 step -1 |
|||
digit = val(mid$(card$,i,1)) |
|||
if ((lc -i) mod 2) = 0 then chkSum = chkSum + digit else chkSum = chkSum + int(digit * 2.2) |
|||
next i |
|||
if chkSum mod 10 = 0 then luhn$ = "True" else luhn$ = "False" |
|||
end function</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716 True |
|||
49927398717 False |
|||
1234567812345678 False |
|||
1234567812345670 True</pre> |
|||
=={{header|Rust}}== |
|||
<syntaxhighlight lang="rust">extern crate luhn_test_of_credit_card_numbers; |
|||
use luhn_test_of_credit_card_numbers::luhn_test; |
|||
fn validate_isin(isin: &str) -> bool { |
|||
if !isin.chars().all(|x| x.is_alphanumeric()) || isin.len() != 12 { |
|||
return false; |
|||
} |
|||
if !isin[..2].chars().all(|x| x.is_alphabetic()) |
|||
|| !isin[2..12].chars().all(|x| x.is_alphanumeric()) |
|||
|| !isin.chars().last().unwrap().is_numeric() |
|||
{ |
|||
return false; |
|||
} |
} |
||
let bytes = isin.as_bytes(); |
|||
let s2 = bytes |
|||
.iter() |
|||
.flat_map(|&c| { |
|||
if c.is_ascii_digit() { |
|||
vec![c] |
|||
} else { |
|||
(c + 10 - ('A' as u8)).to_string().into_bytes() |
|||
} |
|||
}) |
|||
.collect::<Vec<u8>>(); |
|||
let string = std::str::from_utf8(&s2).unwrap(); |
|||
let number = string.parse::<u64>().unwrap(); |
|||
return luhn_test(number as u64); |
|||
} |
} |
||
#[cfg(test)] |
|||
println(luhnTest("49927398716")) |
|||
mod tests { |
|||
println(luhnTest("49927398717")) |
|||
use super::validate_isin; |
|||
println(luhnTest("1234567812345678")) |
|||
println(luhnTest("1234567812345670")) |
|||
</lang> |
|||
#[test] |
|||
Sample output |
|||
fn test_validate_isin() { |
|||
<pre> |
|||
assert_eq!(validate_isin("US0378331005"), true); |
|||
true |
|||
assert_eq!(validate_isin("US0373831005"), false); |
|||
false |
|||
assert_eq!(validate_isin("U50378331005"), false); |
|||
false |
|||
assert_eq!(validate_isin("US03378331005"), false); |
|||
true |
|||
assert_eq!(validate_isin("AU0000XVGZA3"), true); |
|||
</pre> |
|||
assert_eq!(validate_isin("AU0000VXGZA3"), true); |
|||
assert_eq!(validate_isin("FR0000988040"), true); |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716: true |
|||
49927398717: false |
|||
1234567812345678: false |
|||
1234567812345670: true</pre> |
|||
=={{header|Scala}}== |
|||
{{libheader|Scala}} |
|||
===Functional style=== |
|||
<syntaxhighlight lang="scala">object Luhn { |
|||
private def parse(s: String): Seq[Int] = s.map{c => |
|||
assert(c.isDigit) |
|||
c.asDigit |
|||
} |
|||
def checksum(digits: Seq[Int]): Int = { |
|||
digits.reverse.zipWithIndex.foldLeft(0){case (sum,(digit,i))=> |
|||
if (i%2 == 0) sum + digit |
|||
else sum + (digit*2)/10 + (digit*2)%10 |
|||
} % 10 |
|||
} |
|||
def validate(digits: Seq[Int]): Boolean = checksum(digits) == 0 |
|||
def checksum(number: String): Int = checksum(parse(number)) |
|||
def validate(number: String): Boolean = validate(parse(number)) |
|||
} |
|||
object LuhnTest extends App { |
|||
Seq(("49927398716", true), |
|||
("49927398717", false), |
|||
("1234567812345678", false), |
|||
("1234567812345670", true) |
|||
).foreach { case (n, expected) => |
|||
println(s"$n ${Luhn.validate(n)}") |
|||
assert(Luhn.validate(n) == expected) |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716 true |
|||
49927398717 false |
|||
1234567812345678 false |
|||
1234567812345670 true</pre> |
|||
===Imperative style=== |
|||
<syntaxhighlight lang="scala"> def luhnTest1(number: String): Boolean = { |
|||
var (odd, sum) = (true, 0) |
|||
for (int <- number.reverse.map { _.toString.toShort }) { |
|||
if (odd) sum += int |
|||
else sum += (int * 2 % 10) + (int / 5) |
|||
odd = !odd |
|||
} |
|||
sum % 10 == 0 |
|||
}</syntaxhighlight> |
|||
=={{header|Scheme}}== |
=={{header|Scheme}}== |
||
< |
<syntaxhighlight lang="scheme">(define luhn |
||
(define luhn |
|||
(lambda (n) |
(lambda (n) |
||
(let loop ((number n) |
(let loop ((number n) |
||
Line 1,323: | Line 5,898: | ||
(remainder number 10) |
(remainder number 10) |
||
(let ((part (* 2 (remainder number 10)))) |
(let ((part (* 2 (remainder number 10)))) |
||
(+ (remainder part 10) (quotient part 10)))))))))) |
(+ (remainder part 10) (quotient part 10))))))))))</syntaxhighlight> |
||
{{out}} |
|||
</lang> |
|||
<pre> |
<pre> |
||
(map luhn '(49927398716 49927398717 1234567812345678 1234567812345670)) |
(map luhn '(49927398716 49927398717 1234567812345678 1234567812345670)) |
||
Line 1,332: | Line 5,906: | ||
=={{header|sed}}== |
=={{header|sed}}== |
||
< |
<syntaxhighlight lang="sed"># Split number into double evens and odds |
||
s/.*/&: / |
s/.*/&: / |
||
: split |
: split |
||
Line 1,359: | Line 5,933: | ||
/0:/!a\ |
/0:/!a\ |
||
Fail |
Fail |
||
d</ |
d</syntaxhighlight> |
||
{{out}} |
|||
sample Output |
|||
<pre> |
<pre> |
||
$ sed -f luhn.sed <<! |
$ sed -f luhn.sed <<! |
||
Line 1,375: | Line 5,949: | ||
</pre> |
</pre> |
||
=={{header| |
=={{header|Seed7}}== |
||
<syntaxhighlight lang="seed7">$ include "seed7_05.s7i"; |
|||
const func boolean: luhnTest (in string: cardNumber) is func |
|||
Using a precomputed array. |
|||
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 |
|||
<lang SNOBOL4> define('luhn(n)a,d,i,j,sum') :(luhn_end) |
|||
local |
|||
luhn n = reverse(n); a = array('0:9') |
|||
var string: cardNumber is ""; |
|||
ln1 a<i> = (2 * i / 10) + remdr(2 * i,10) |
|||
begin |
|||
i = lt(i,9) i + 1 :s(ln1) |
|||
for cardNumber range [] ("49927398716", "49927398717", "1234567812345678", "1234567812345670") do |
|||
ln2 n len(1) . d = :f(ln3) |
|||
writeln(cardNumber <& ": " <& luhnTest(cardNumber)); |
|||
d = ne(remdr(j,2),0) a<d>; j = j + 1 |
|||
end for; |
|||
sum = sum + d :(ln2) |
|||
end func;</syntaxhighlight> |
|||
ln3 luhn = 0; luhn = eq(remdr(sum,10),0) 1 :(return) |
|||
{{out}} |
|||
<pre> |
|||
49927398716: TRUE |
|||
49927398717: FALSE |
|||
1234567812345678: FALSE |
|||
1234567812345670: TRUE |
|||
</pre> |
|||
=={{header|SenseTalk}}== |
|||
<syntaxhighlight lang="sensetalk">function LuhnCheck ccNum |
|||
put length of ccNum into numDigits |
|||
put the last character of ccNum into total |
|||
put numDigits modulo 2 into parity |
|||
repeat for each character of the first numDigits - 1 characters of ccNum |
|||
put it into digit |
|||
if (the counter - 1) modulo 2 equals parity |
|||
multiply digit by 2 |
|||
end if |
|||
if digit is greater than 9 |
|||
subtract 9 from digit |
|||
end if |
|||
add digit to total |
|||
end repeat |
|||
return total is divisible by 10 |
|||
end LuhnCheck</syntaxhighlight> |
|||
<syntaxhighlight lang="sensetalk">repeat for each item of (49927398716, 49927398717, 1234567812345678, 1234567812345670) |
|||
put it && LuhnCheck(it) |
|||
end repeat</syntaxhighlight> |
|||
=={{header|SequenceL}}== |
|||
<syntaxhighlight lang="sequencel"> |
|||
main(args(2)) := |
|||
sum(luhnTest(asciiToInt(args[1]) - asciiToInt('0'))) mod 10 = 0; |
|||
s2Mapping := [0,2,4,6,8,1,3,5,7,9]; |
|||
luhnTest(x(1))[i] := |
|||
x[i] when i mod 2 = size(x) mod 2 else |
|||
s2Mapping[x[i] + 1]; |
|||
</syntaxhighlight> |
|||
=={{header|SETL}}== |
|||
<syntaxhighlight lang="setl">program luhn_test; |
|||
tests := [ |
|||
49927398716, |
|||
49927398717, |
|||
1234567812345678, |
|||
1234567812345670 |
|||
]; |
|||
loop for test in tests do |
|||
print(test, if luhn test then "pass" else "fail" end); |
|||
end loop; |
|||
op luhn(n); |
|||
fac := 2; |
|||
digits := [val d * (fac := 3-fac) : d in reverse str n]; |
|||
return 0 = +/[d - if d>9 then 9 else 0 end : d in digits] mod 10; |
|||
end op; |
|||
end program;</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716 pass |
|||
49927398717 fail |
|||
1234567812345678 fail |
|||
1234567812345670 pass</pre> |
|||
=={{header|Shen}}== |
|||
<syntaxhighlight lang="shen"> |
|||
(define mapi |
|||
_ _ [] -> [] |
|||
F N [X | Xs] -> [(F N X) | (mapi F (+ N 1) Xs)]) |
|||
(define double |
|||
X -> (let Y (* 2 X) (if (> Y 9) (- Y 9) Y))) |
|||
(define luhn? |
|||
Number -> |
|||
(let Exploded (explode Number) |
|||
Digits (map (/. H (- (string->n H) 48)) Exploded) |
|||
Reversed (reverse Digits) |
|||
Doubled (mapi (/. N X (if (= 1 (shen.mod N 2)) (double X) X)) 0 Reversed) |
|||
Summed (sum Doubled) |
|||
Modded (shen.mod Summed 10) |
|||
(= 0 Modded))) |
|||
"Expected: [true false false true]" |
|||
(map (function luhn?) ["49927398716" "49927398717" "1234567812345678" "1234567812345670"]) |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
mapi |
|||
transform-x |
|||
luhn? |
|||
"Expected: [true false false true]" |
|||
[true false false true] |
|||
run time: 0.014999985694885254 secs |
|||
loaded |
|||
</pre> |
|||
=={{header|Sidef}}== |
|||
<syntaxhighlight lang="ruby">func luhn (n) { |
|||
static a = {|j| (2*j // 10) + (2*j % 10) }.map(^10) |
|||
var checksum = n.digits.map_kv {|i,j| |
|||
i.is_odd ? a[j] : j |
|||
}.sum |
|||
checksum % 10 == 0 |
|||
} |
|||
for n in [49927398716, 49927398717, 1234567812345678, 1234567812345670] { |
|||
say [n, luhn(n)] |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>[49927398716, true] |
|||
[49927398717, false] |
|||
[1234567812345678, false] |
|||
[1234567812345670, true]</pre> |
|||
=={{header|SNOBOL4}}== |
|||
Using a precomputed array. |
|||
<syntaxhighlight lang="snobol4"> define('luhn(n)a,d,i,j,sum') :(luhn_end) |
|||
luhn n = reverse(n); a = array('0:9') |
|||
ln1 a<i> = (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 |
luhn_end |
||
ok = array('0:1') |
|||
ok<0> = 'FAIL' |
|||
ok<1> = 'OK' |
|||
n = '49927398717'; eval(test) |
|||
n = '1234567812345678'; eval(test) |
|||
n = '1234567812345670'; eval(test) |
|||
end</lang> |
|||
* Test and display |
|||
Output: |
|||
define('test(n)') :(test_end) |
|||
<pre>1: 49927398716 |
|||
test output = n ': ' ok<luhn(n)> :(return) |
|||
0: 49927398717 |
|||
test_end |
|||
0: 1234567812345678 |
|||
1: 1234567812345670</pre> |
|||
test('49927398716') |
|||
test('49927398717') |
|||
test('1234567812345678') |
|||
test('1234567812345670') |
|||
end |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716: OK |
|||
49927398717: FAIL |
|||
1234567812345678: FAIL |
|||
1234567812345670: OK</pre> |
|||
=={{header|SparForte}}== |
|||
As a structured script. |
|||
<syntaxhighlight lang="ada">#!/usr/local/bin/spar |
|||
pragma annotate( summary, "luhn test of credit card numbers" ) |
|||
@( description, "The Luhn test is used by some credit card companies to " ) |
|||
@( description, "distinguish valid credit card numbers from what could be a random selection of digits." ) |
|||
@( see_also, "https://rosettacode.org/wiki/Luhn_test_of_credit_card_number" ) |
|||
@( author, "Ken O. Burtch" ); |
|||
pragma license( unrestricted ); |
|||
pragma restriction( no_external_commands ); |
|||
procedure luhn is |
|||
bad_digit : exception; |
|||
-- return true if the card number passes the luhn test |
|||
function is_luhn( card_number : string) return boolean is |
|||
card_num_len : constant natural := strings.length( card_number ); |
|||
checksum: natural := 0; |
|||
ch : character; |
|||
begin |
|||
for i in reverse 1..card_num_len loop |
|||
ch := strings.element( card_number, i ); |
|||
if strings.is_digit( ch ) then |
|||
declare |
|||
ord : constant natural := numerics.pos(ch); |
|||
begin |
|||
if ((card_num_len-1) and (i-1) ) /= 0 then |
|||
checksum := @ + ord; |
|||
else |
|||
checksum := @ + numerics.floor(ord / 5) + ((2*ord) mod 10); |
|||
end if; |
|||
end; |
|||
else |
|||
raise bad_digit; |
|||
end if; |
|||
end loop; |
|||
return checksum mod 10 = 0; |
|||
end is_luhn; |
|||
-- check a credit card and display the result |
|||
procedure check_card( card_number : string ) is |
|||
begin |
|||
put( card_number ) |
|||
@( ": " ) |
|||
@( is_luhn( card_number ) ); |
|||
new_line; |
|||
end check_card; |
|||
begin |
|||
check_card("49927398716"); |
|||
check_card("49927398717"); |
|||
check_card("1234567812345678"); |
|||
check_card("1234567812345670"); |
|||
end luhn;</syntaxhighlight> |
|||
=={{header|SPARK}}== |
=={{header|SPARK}}== |
||
Line 1,409: | Line 6,206: | ||
A final test has been added which passes as valid unless there is an explicit test for all digits. |
A final test has been added which passes as valid unless there is an explicit test for all digits. |
||
< |
<syntaxhighlight lang="ada">with Spark_IO; |
||
--# inherit Spark_IO; |
--# inherit Spark_IO; |
||
--# main_program; |
--# main_program; |
||
Line 1,468: | Line 6,265: | ||
Do_Test("1234567812345670"); |
Do_Test("1234567812345670"); |
||
Do_Test("123456781234567D"); |
Do_Test("123456781234567D"); |
||
end Luhn;</ |
end Luhn;</syntaxhighlight> |
||
{{out}} |
|||
Output: |
|||
<pre>49927398716 is valid. |
<pre>49927398716 is valid. |
||
49927398717 is not valid. |
49927398717 is not valid. |
||
Line 1,475: | Line 6,272: | ||
1234567812345670 is valid. |
1234567812345670 is valid. |
||
123456781234567D is not valid.</pre> |
123456781234567D is not valid.</pre> |
||
=={{header|SQL PL}}== |
|||
{{works with|Db2 LUW}} |
|||
With SQL PL: |
|||
<syntaxhighlight lang="sql pl"> |
|||
--#SET TERMINATOR @ |
|||
SET SERVEROUTPUT ON @ |
|||
CREATE OR REPLACE FUNCTION LUHN_TEST ( |
|||
IN NUMBER VARCHAR(24) |
|||
) RETURNS SMALLINT |
|||
--) RETURNS BOOLEAN |
|||
BEGIN |
|||
DECLARE TYPE CARD_NUMBER AS VARCHAR(1) ARRAY [24]; |
|||
DECLARE LENGTH SMALLINT; |
|||
DECLARE REVERSE CARD_NUMBER; |
|||
DECLARE I SMALLINT; |
|||
DECLARE POS SMALLINT; |
|||
DECLARE S1 SMALLINT; |
|||
DECLARE S2 SMALLINT; |
|||
DECLARE TEMP SMALLINT; |
|||
DECLARE RET SMALLINT; |
|||
--DECLARE RET BOOLEAN; |
|||
DECLARE INVALID_CHAR CONDITION FOR SQLSTATE 'LUHN1'; |
|||
-- Reverse the order of the digits in the number. |
|||
SET LENGTH = LENGTH(NUMBER); |
|||
SET I = 1; |
|||
WHILE (I <= LENGTH) DO |
|||
SET POS = LENGTH - I + 1; |
|||
SET REVERSE[POS] = SUBSTR(NUMBER, I, 1); |
|||
IF (ASCII(REVERSE[POS]) < 48 OR 57 < ASCII(REVERSE[POS])) THEN |
|||
SIGNAL INVALID_CHAR SET MESSAGE_TEXT = 'Invalid character, not a digit'; |
|||
END IF; |
|||
SET I = I + 1; |
|||
END WHILE; |
|||
-- Take the first, third, ... and every other odd digit in the reversed digits and sum them to form the partial sum s1 |
|||
SET S1 = 0; |
|||
SET I = 1; |
|||
WHILE (I <= LENGTH) DO |
|||
IF (MOD(I, 2) = 1) THEN |
|||
SET S1 = S1 + REVERSE[I]; |
|||
END IF; |
|||
-- CALL DBMS_OUTPUT.PUT_LINE('I ' || I || ', S1 ' || S1 || ', val ' || REVERSE[I]); |
|||
SET I = I + 1; |
|||
END WHILE; |
|||
-- Taking the second, fourth ... and every other even digit in the reversed digits: |
|||
SET S2 = 0; |
|||
SET TEMP = 0; |
|||
SET I = 1; |
|||
WHILE (I <= LENGTH) DO |
|||
IF (MOD(I, 2) = 0) THEN |
|||
-- Multiply each digit by two and sum the digits if the answer is greater than nine to form partial sums for the even digits |
|||
SET TEMP = REVERSE[I] * 2; |
|||
IF (TEMP > 9) THEN |
|||
SET TEMP = (TEMP / 10) + (MOD(TEMP, 10)); |
|||
END IF; |
|||
-- Sum the partial sums of the even digits to form s2 |
|||
SET S2 = S2 + TEMP; |
|||
END IF; |
|||
-- CALL DBMS_OUTPUT.PUT_LINE('I ' || I || ', S2 ' || S2 || ', TEMP ' || TEMP || ' val ' || REVERSE[I]); |
|||
SET I = I + 1; |
|||
END WHILE; |
|||
-- 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. |
|||
SET RET = 1; |
|||
--SET RET = FALSE; |
|||
SET TEMP = S1 + S2; |
|||
IF (MOD(TEMP, 10) = 0) THEN |
|||
SET RET = 0; |
|||
--SET RET = TRUE; |
|||
CALL DBMS_OUTPUT.PUT_LINE('It is a valid number ' || S1 || '+' || S2 || '=' || TEMP); |
|||
ELSE |
|||
CALL DBMS_OUTPUT.PUT_LINE('It is NOT a valid number ' || S1 || '+' || S2 || '=' || TEMP); |
|||
END IF; |
|||
RETURN RET; |
|||
END |
|||
@ |
|||
</syntaxhighlight> |
|||
Output: |
|||
<pre> |
|||
db2 -td@ |
|||
db2 => SET SERVEROUTPUT ON @ |
|||
DB20000I The SET SERVEROUTPUT command completed successfully. |
|||
db2 => CREATE OR REPLACE FUNCTION VALIDATE_CREDIT_CARD_NUMBER ( |
|||
... |
|||
db2 (cont.) => END @ |
|||
DB20000I The SQL command completed successfully. |
|||
db2 => values VALIDATE_CREDIT_CARD_NUMBER(49927398716)@ |
|||
1 |
|||
------ |
|||
0 |
|||
1 record(s) selected. |
|||
It is a valid number 42+28=70 |
|||
db2 => VALUES VALIDATE_CREDIT_CARD_NUMBER(49927398717)@ |
|||
1 |
|||
------ |
|||
1 |
|||
1 record(s) selected. |
|||
It is NOT a valid number 43+28=71 |
|||
db2 => VALUES VALIDATE_CREDIT_CARD_NUMBER(1234567812345678)@ |
|||
1 |
|||
------ |
|||
1 |
|||
1 record(s) selected. |
|||
It is NOT a valid number 40+28=68 |
|||
db2 => VALUES VALIDATE_CREDIT_CARD_NUMBER(1234567812345670)@ |
|||
1 |
|||
------ |
|||
0 |
|||
1 record(s) selected. |
|||
It is a valid number 32+28=60 |
|||
</pre> |
|||
=={{header|Standard ML}}== |
|||
<syntaxhighlight lang="sml">local |
|||
fun revDigits 0 = [] |
|||
| revDigits n = (n mod 10) :: revDigits (n div 10) |
|||
fun digitSum n = if n > 9 then digitSum (n div 10 + n mod 10) |
|||
else n |
|||
fun luhn_sum [] = 0 |
|||
| luhn_sum [d] = d |
|||
| luhn_sum (d::d'::ds) = d + digitSum (2*d') + luhn_sum ds |
|||
in |
|||
fun luhn_test n = luhn_sum (revDigits n) mod 10 = 0 |
|||
val res = map luhn_test [49927398716, 49927398717, 1234567812345678, 1234567812345670]; |
|||
end; |
|||
(* |
|||
[opening file "luhn.sml"] |
|||
> val luhn_test = fn : int -> bool |
|||
val res = [true, false, false, true] : bool list |
|||
[closing file "luhn.sml"] |
|||
*)</syntaxhighlight> |
|||
=={{header|Swift}}== |
|||
<syntaxhighlight lang="swift">func luhn(_ number: String) -> Bool { |
|||
return number.reversed().enumerated().map({ |
|||
let digit = Int(String($0.element))! |
|||
let even = $0.offset % 2 == 0 |
|||
return even ? digit : digit == 9 ? 9 : digit * 2 % 9 |
|||
}).reduce(0, +) % 10 == 0 |
|||
} |
|||
luhn("49927398716") // true |
|||
luhn("49927398717") // false</syntaxhighlight> |
|||
=={{header|Tcl}}== |
=={{header|Tcl}}== |
||
Based on an algorithmic encoding for the test on Wikipedia. |
Based on an algorithmic encoding for the test on Wikipedia. |
||
< |
<syntaxhighlight lang="tcl">package require Tcl 8.5 |
||
proc luhn digitString { |
proc luhn digitString { |
||
if {[regexp {[^0-9]} $digitString]} {error "not a number"} |
if {[regexp {[^0-9]} $digitString]} {error "not a number"} |
||
Line 1,490: | Line 6,451: | ||
} |
} |
||
return [expr {($sum % 10) == 0}] |
return [expr {($sum % 10) == 0}] |
||
}</ |
}</syntaxhighlight> |
||
Driver: |
Driver: |
||
< |
<syntaxhighlight lang="tcl">foreach testNumber { |
||
49927398716 |
49927398716 |
||
49927398717 |
49927398717 |
||
Line 1,500: | Line 6,461: | ||
puts [format "%s is %s" $testNumber \ |
puts [format "%s is %s" $testNumber \ |
||
[lindex {"NOT valid" "valid"} [luhn $testNumber]]] |
[lindex {"NOT valid" "valid"} [luhn $testNumber]]] |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
|||
Output: |
|||
<pre> |
<pre> |
||
49927398716 is valid |
49927398716 is valid |
||
Line 1,508: | Line 6,469: | ||
1234567812345670 is valid |
1234567812345670 is valid |
||
</pre> |
</pre> |
||
=={{header|Terraform}}== |
|||
<syntaxhighlight lang="hcl">variable number { |
|||
type = "string" |
|||
} |
|||
locals { |
|||
digits = reverse(split("", var.number)) |
|||
count = length(local.digits) |
|||
odds = [for i in range(local.count): local.digits[i] if i%2==0] |
|||
evens = [for i in range(local.count): local.digits[i] if i%2==1] |
|||
s1 = length(flatten([for d in local.odds: range(d)])) |
|||
doubles = [for d in local.evens: d * 2] |
|||
partials = [for d in local.doubles: d < 10 ? d : floor(d/10)+d%10] |
|||
s2 = length(flatten([for p in local.partials: range(p)])) |
|||
check = (local.s1 + local.s2) % 10 |
|||
} |
|||
output "valid" { |
|||
value = local.check == 0 |
|||
}</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>$ terraform apply |
|||
var.number |
|||
Enter a value: 49927398716 |
|||
Apply complete! Resources: 0 added, 0 changed, 0 destroyed. |
|||
Outputs: |
|||
valid = true |
|||
$ TF_VAR_number=49927398717 terraform apply >/dev/null; terraform output valid |
|||
false |
|||
$ TF_VAR_number=1234567812345678 terraform apply >/dev/null; terraform output valid |
|||
false |
|||
$ TF_VAR_number=1234567812345670 terraform apply >/dev/null; terraform output valid |
|||
true</pre> |
|||
=={{header|TI-83 BASIC}}== |
|||
<syntaxhighlight lang="ti83b">PROGRAM:LUHN |
|||
:Disp "ENTER NUMBER" |
|||
:Input Str1 |
|||
:0→S |
|||
:0→E |
|||
:For(I,length(Str1),1,-1) |
|||
:inString("0123456789",sub(Str1,I,1))–1→X |
|||
:If X<0 |
|||
:Goto BA |
|||
:If E≠0 |
|||
:Then |
|||
:2X→X |
|||
:If X>9 |
|||
:X–9→X |
|||
:End |
|||
:X+S→S |
|||
:not(E)→E |
|||
:End |
|||
:If fPart(S/10)=0 |
|||
:Then |
|||
:Disp "GOOD CARD" |
|||
:Else |
|||
:Lbl BA |
|||
:Disp "BAD CARD" |
|||
:End |
|||
</syntaxhighlight> |
|||
=={{header|Transact-SQL}}== |
|||
<syntaxhighlight lang="transact-sql"> |
|||
CREATE FUNCTION dbo._CreditCardNumCheck( @strCCNum VarChar(40) ) |
|||
RETURNS VarChar(7) |
|||
AS |
|||
BEGIN |
|||
DECLARE @string VarChar(40) = REVERSE(@strCCNum); -- usage: set once, never changed |
|||
DECLARE @strS2Values VarChar(10) = '0246813579'; -- constant: maps digits to their S2 summed values |
|||
DECLARE @table TABLE (ID INT, Value INT, S_Value INT); -- ID=digit position. S_Value is used for SUM(). |
|||
DECLARE @p INT = 0; -- loop counter: position in string |
|||
-- Convert the reversed string's digits into rows in a table variable, S_Values to be updated afterwards |
|||
WHILE @p < LEN(@string) |
|||
BEGIN |
|||
SET @p = @p+1; |
|||
INSERT INTO @table (ID,Value,S_Value) VALUES (@p, CONVERT(INT,SUBSTRING(@string,@p,1)), 0); |
|||
END |
|||
-- Update S_Value column : the digit's value to be summed (for even-positioned digits this is mapped via @strS2Values) |
|||
UPDATE @table SET S_Value = CASE WHEN ID % 2 = 1 THEN Value ELSE CONVERT(INT,SUBSTRING(@strS2Values,Value+1,1)) END |
|||
-- If the SUM of S_Values ends in 0 (modulo 10 = 0) then the CC Number is valid |
|||
RETURN CASE WHEN (SELECT SUM(S_Value) FROM @table) % 10 = 0 THEN 'Valid' ELSE 'Invalid' END |
|||
END |
|||
</syntaxhighlight> |
|||
=={{header|TUSCRIPT}}== |
=={{header|TUSCRIPT}}== |
||
< |
<syntaxhighlight lang="tuscript">$$ MODE TUSCRIPT |
||
$$ MODE TUSCRIPT |
|||
MODE DATA |
MODE DATA |
||
$$ SET cardnumbers=* |
$$ SET cardnumbers=* |
||
Line 1,551: | Line 6,601: | ||
PRINT c,"false" |
PRINT c,"false" |
||
ENDIF |
ENDIF |
||
ENDLOOP |
ENDLOOP</syntaxhighlight> |
||
{{out}} |
|||
</lang> |
|||
Output: |
|||
<pre> |
<pre> |
||
49927398716 true |
49927398716 true |
||
Line 1,560: | Line 6,609: | ||
1234567812345670 true |
1234567812345670 true |
||
</pre> |
</pre> |
||
=={{header|TXR}}== |
|||
<syntaxhighlight 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)</syntaxhighlight> |
|||
<pre>$ txr luhn.txr luhn.txt |
|||
49927398716 -> good |
|||
49927398717 -> bad |
|||
1234567812345678 -> bad |
|||
1234567812345670 -> good</pre> |
|||
=={{header|Uiua}}== |
|||
<syntaxhighlight lang="uiua"> |
|||
Luhn ← =0◿10+⊃(/+⊢|/+∵(⍥(-9)>9.×2)⊡1)⍉⬚0↯∞_2⇌ |
|||
T ← {"49927398716" |
|||
"49927398717" |
|||
"1234567812345678" |
|||
"1234567812345670"} |
|||
≡◇(Luhn ≡⋕) T |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
[1 0 0 1] |
|||
</pre> |
|||
=={{header|UNIX Shell}}== |
|||
{{works with|bash}} |
|||
{{works with|ksh}} |
|||
<syntaxhighlight lang="bash">function luhn { |
|||
typeset n p s t=('0123456789' '0516273849') |
|||
while ((-n<${#1})); do |
|||
p="${t[n--%2]%${1:n:1}*}" |
|||
((s+=${#p})) |
|||
done |
|||
((s%10)) |
|||
} |
|||
for c in 49927398716 49927398717 1234567812345678 1234567812345670; do |
|||
if luhn $c; then |
|||
echo $c is invalid |
|||
else |
|||
echo $c is valid |
|||
fi |
|||
done</syntaxhighlight> |
|||
Notes: |
|||
* The parameter expansion hack (p=${t…%${1:n:1}};…${#p}…) is an interesting way of converting a set of characters to ordinals. It's highly extensible to larger character sets (e.g. for ISBN and Code 39 checksums). |
|||
* Invalid characters are effectively treated as 0s. This is actually useful sometimes for ignoring alphabetic prefixes. |
|||
* When attempting to understand the function, remember that n is negative, so it indexes from the end of the input string. |
|||
{{out}} |
|||
<pre>49927398716 is valid |
|||
49927398717 is invalid |
|||
1234567812345678 is invalid |
|||
1234567812345670 is valid</pre> |
|||
=={{header|Ursala}}== |
=={{header|Ursala}}== |
||
< |
<syntaxhighlight lang="ursala">#import std |
||
#import nat |
#import nat |
||
luhn = %nP; %np*hxiNCNCS; not remainder\10+ //sum:-0@DrlrHK32 ~&iK27K28TK25 iota10</ |
luhn = %nP; %np*hxiNCNCS; not remainder\10+ //sum:-0@DrlrHK32 ~&iK27K28TK25 iota10</syntaxhighlight> |
||
Some notes on this solution: |
Some notes on this solution: |
||
Line 1,580: | Line 6,698: | ||
* The output from the function is tested for divisibility by 10 with <code>remainder\10</code>, with the result negated so that zero values map to true and non-zero to false. |
* The output from the function is tested for divisibility by 10 with <code>remainder\10</code>, with the result negated so that zero values map to true and non-zero to false. |
||
usage: |
usage: |
||
<lang>#cast %bL |
<syntaxhighlight lang="ursala">#cast %bL |
||
test = luhn* <49927398716,49927398717,1234567812345678,1234567812345670></ |
test = luhn* <49927398716,49927398717,1234567812345678,1234567812345670></syntaxhighlight> |
||
{{out}} |
|||
output: |
|||
<pre> |
<pre> |
||
<true,false,false,true> |
<true,false,false,true> |
||
</pre> |
|||
=={{header|VBA}}== |
|||
<syntaxhighlight lang="vb"> |
|||
Option Explicit |
|||
Sub Main() |
|||
Debug.Print "Number 49927398716 is "; Luhn("49927398716") |
|||
Debug.Print "Number 49927398717 is "; Luhn("49927398717") |
|||
Debug.Print "Number 1234567812345678 is "; Luhn("1234567812345678") |
|||
Debug.Print "Number 1234567812345670 is "; Luhn("1234567812345670") |
|||
End Sub |
|||
Private Function Luhn(Nb As String) As String |
|||
Dim t$, i&, Summ&, s& |
|||
t = StrReverse(Nb) |
|||
For i = 1 To Len(t) Step 2 |
|||
Summ = Summ + CInt(Mid(t, i, 1)) |
|||
Next i |
|||
For i = 2 To Len(t) Step 2 |
|||
s = 2 * (CInt(Mid(t, i, 1))) |
|||
If s >= 10 Then |
|||
Summ = Summ - 9 |
|||
End If |
|||
Summ = Summ + s |
|||
Next i |
|||
If Summ Mod 10 = 0 Then |
|||
Luhn = "valid" |
|||
Else |
|||
Luhn = "invalid" |
|||
End If |
|||
End Function</syntaxhighlight> |
|||
{{out}} |
|||
<pre>Number 49927398716 is valid |
|||
Number 49927398717 is invalid |
|||
Number 1234567812345678 is invalid |
|||
Number 1234567812345670 is valid</pre> |
|||
=={{header|VBScript}}== |
|||
<syntaxhighlight lang="vbscript">Function Luhn_Test(cc) |
|||
cc = RevString(cc) |
|||
s1 = 0 |
|||
s2 = 0 |
|||
For i = 1 To Len(cc) |
|||
If i Mod 2 > 0 Then |
|||
s1 = s1 + CInt(Mid(cc,i,1)) |
|||
Else |
|||
tmp = CInt(Mid(cc,i,1))*2 |
|||
If tmp < 10 Then |
|||
s2 = s2 + tmp |
|||
Else |
|||
s2 = s2 + CInt(Right(CStr(tmp),1)) + 1 |
|||
End If |
|||
End If |
|||
Next |
|||
If Right(CStr(s1 + s2),1) = "0" Then |
|||
Luhn_Test = "Valid" |
|||
Else |
|||
Luhn_Test = "Invalid" |
|||
End If |
|||
End Function |
|||
Function RevString(s) |
|||
For i = Len(s) To 1 Step -1 |
|||
RevString = RevString & Mid(s,i,1) |
|||
Next |
|||
End Function |
|||
WScript.Echo "49927398716 is " & Luhn_Test("49927398716") |
|||
WScript.Echo "49927398717 is " & Luhn_Test("49927398717") |
|||
WScript.Echo "1234567812345678 is " & Luhn_Test("1234567812345678") |
|||
WScript.Echo "1234567812345670 is " & Luhn_Test("1234567812345670")</syntaxhighlight> |
|||
{{out}} |
|||
<pre>49927398716 is Valid |
|||
49927398717 is Invalid |
|||
1234567812345678 is Invalid |
|||
1234567812345670 is Valid</pre> |
|||
=={{header|Visual Basic}}== |
|||
{{works with|Visual Basic|VB6 Standard}} |
|||
<syntaxhighlight lang="vb">Public Function LuhnCheckPassed(ByVal dgts As String) As Boolean |
|||
Dim i As Long, s As Long, s1 As Long |
|||
dgts = VBA.StrReverse(dgts) |
|||
For i = 1 To Len(dgts) Step 2 |
|||
s = s + CInt(Mid$(dgts, i, 1)) |
|||
Next i |
|||
For i = 2 To Len(dgts) Step 2 |
|||
s1 = 2 * (CInt(Mid$(dgts, i, 1))) |
|||
If s1 >= 10 Then |
|||
s = s - 9 |
|||
End If |
|||
s = s + s1 |
|||
Next i |
|||
LuhnCheckPassed = Not CBool(s Mod 10) |
|||
End Function</syntaxhighlight> |
|||
Test: |
|||
<syntaxhighlight lang="vb">Sub Main() |
|||
Debug.Assert LuhnCheckPassed("49927398716") |
|||
Debug.Assert Not LuhnCheckPassed("49927398717") |
|||
Debug.Assert Not LuhnCheckPassed("1234567812345678") |
|||
Debug.Assert LuhnCheckPassed("1234567812345670") |
|||
End Sub</syntaxhighlight> |
|||
=={{header|Visual Basic .NET}}== |
|||
<syntaxhighlight lang="visual basic .net"> |
|||
Imports System.Linq |
|||
Function ValidLuhn(value As String) |
|||
Return value.Select(Function(c, i) (AscW(c) - 48) << ((value.Length - i - 1) And 1)).Sum(Function(n) If(n > 9, n - 9, n)) Mod 10 = 0 |
|||
End Function |
|||
Sub Main() |
|||
Console.WriteLine(ValidLuhn("49927398716")) |
|||
Console.WriteLine(ValidLuhn("49927398717")) |
|||
Console.WriteLine(ValidLuhn("1234567812345678")) |
|||
Console.WriteLine(ValidLuhn("1234567812345670")) |
|||
End Sub |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
True |
|||
False |
|||
False |
|||
True |
|||
</pre> |
|||
=={{header|V (Vlang)}}== |
|||
{{trans|go}} |
|||
<syntaxhighlight lang="v (vlang)">const ( |
|||
input = '49927398716 |
|||
49927398717 |
|||
1234567812345678 |
|||
1234567812345670' |
|||
t = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9] |
|||
) |
|||
fn luhn(s string) bool { |
|||
odd := s.len & 1 |
|||
mut sum := 0 |
|||
for i, c in s.split('') { |
|||
if c < '0' || c > '9' { |
|||
return false |
|||
} |
|||
if i&1 == odd { |
|||
sum += t[c.int()-'0'.int()] |
|||
} else { |
|||
sum += c.int() - '0'.int() |
|||
} |
|||
} |
|||
return sum%10 == 0 |
|||
} |
|||
fn main() { |
|||
for s in input.split("\n") { |
|||
println('$s ${luhn(s)}') |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
49927398716 true |
|||
49927398717 false |
|||
1234567812345678 false |
|||
1234567812345670 true |
|||
</pre> |
|||
=={{header|Wren}}== |
|||
{{libheader|Wren-fmt}} |
|||
{{libheader|Wren-iterate}} |
|||
<syntaxhighlight lang="wren">import "./fmt" for Fmt |
|||
import "./iterate" for Stepped |
|||
var luhn = Fn.new { |s| |
|||
// reverse digits |
|||
s = s[-1..0] |
|||
// sum the odd digits |
|||
var s1 = Stepped.new(s, 2).reduce(0) { |sum, d| sum + d.bytes[0] - 48 } |
|||
// sum two times the even digits |
|||
var s2 = Stepped.new(s[1..-1], 2).reduce(0) { |sum, d| |
|||
var d2 = (d.bytes[0] - 48) * 2 |
|||
return sum + ((d2 > 9) ? d2%10 + 1 : d2) |
|||
} |
|||
// check if s1 + s2 ends in zero |
|||
return (s1 + s2)%10 == 0 |
|||
} |
|||
var tests = [ "49927398716", "49927398717", "1234567812345678", "1234567812345670"] |
|||
for (test in tests) { |
|||
var ans = (luhn.call(test)) ? "pass" : "fail" |
|||
Fmt.print("$-16s -> $s", test, ans) |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
49927398716 -> pass |
|||
49927398717 -> fail |
|||
1234567812345678 -> fail |
|||
1234567812345670 -> pass |
|||
</pre> |
|||
=={{header|Xojo}}== |
|||
<syntaxhighlight lang="xojo">Public Function Modulus10(digits As String) as String |
|||
// |
|||
// Confirm the digits are really, well, digits |
|||
// |
|||
dim validator as new RegEx |
|||
validator.SearchPattern = "\A\d+\z" |
|||
if validator.Search( digits ) is nil then |
|||
// |
|||
// Raise an exception or something |
|||
// |
|||
end if |
|||
static doublingTable() as string = array( "0", "2", "4", "6", "8", "1", "3", "5", "7", "9" ) |
|||
dim digitArr() as string = digits.Split( "" ) |
|||
for i as integer = digitArr.Ubound downto 0 step 2 |
|||
digitArr( i ) = doublingTable( digitArr( i ).Val ) |
|||
next |
|||
dim sum as integer |
|||
for each digit as string in digitArr |
|||
sum = sum + digit.Val |
|||
next |
|||
dim check as integer = ( sum * 9 ) mod 10 |
|||
return str( check ) |
|||
End Function |
|||
Public Function ValidateMod10(digits As String) as Boolean |
|||
dim checkDigit as string = digits.Right( 1 ) |
|||
digits = digits.Left( digits.Len - 1 ) |
|||
return Modulus10( digits ) = checkDigit |
|||
End Function |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
ValididateMod10( "49927398716" ) = True |
|||
ValididateMod10( "49927398717" ) = False |
|||
ValididateMod10( "1234567812345678" ) = False |
|||
ValididateMod10( "1234567812345670" ) = True |
|||
</pre> |
|||
=={{header|XPL0}}== |
|||
<syntaxhighlight lang="xpl0">string 0; \use zero-terminated strings |
|||
func Valid(Str); \Return 'true' if digits in Str pass Luhn test |
|||
char Str; |
|||
int Len, Sum, I, Dig; |
|||
[Len:= 0; \find length of Str |
|||
while Str(Len) do Len:= Len+1; |
|||
Sum:= 0; \sum even and odd digits |
|||
for I:= 0 to Len-1 do \(no need to reverse) |
|||
[if (I xor Len) and 1 then |
|||
Sum:= Sum + Str(I) - ^0 |
|||
else [Dig:= Str(I) - ^0; |
|||
Dig:= Dig*2; |
|||
Sum:= Sum + Dig/10 + rem(0); |
|||
]; |
|||
]; |
|||
return rem(Sum/10) = 0; |
|||
]; |
|||
int Luhn, N; |
|||
[Luhn:= ["49927398716", |
|||
"49927398717", |
|||
"1234567812345678", |
|||
"1234567812345670"]; |
|||
for N:= 0 to 4-1 do |
|||
[Text(0, Luhn(N)); |
|||
Text(0, if Valid(Luhn(N)) |
|||
then " is valid" |
|||
else " is not valid"); |
|||
CrLf(0); |
|||
]; |
|||
]</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
49927398716 is valid |
|||
49927398717 is not valid |
|||
1234567812345678 is not valid |
|||
1234567812345670 is valid |
|||
</pre> |
|||
=={{header|zkl}}== |
|||
<syntaxhighlight lang="zkl">fcn luhnTest(n){ |
|||
0 == (n.split().reverse().reduce(fcn(s,n,clk){ |
|||
s + if(clk.next()) n else 2*n%10 + n/5 },0,Walker.cycle(1,0)) %10) |
|||
}</syntaxhighlight> |
|||
<syntaxhighlight lang="zkl">T(49927398716,49927398717,1234567812345678,1234567812345670) |
|||
.apply(luhnTest).println();</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
L(True,False,False,True) |
|||
</pre> |
|||
=={{header|ZX Spectrum Basic}}== |
|||
<syntaxhighlight 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 |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
49927398716 VALID! |
|||
49927398717 INVALID! |
|||
1234567812345678 INVALID! |
|||
1234567812345670 VALID! |
|||
</pre> |
</pre> |
Latest revision as of 14:52, 19 June 2024
You are encouraged to solve this task according to the task description, using any language you may know.
The Luhn test is used by some credit card companies to distinguish valid credit card numbers from what could be a random selection of digits.
Those companies using credit card numbers that can be validated by the Luhn test have numbers that pass the following test:
- Reverse the order of the digits in the number.
- Take the first, third, ... and every other odd digit in the reversed digits and sum them to form the partial sum s1
- Taking the second, fourth ... and every other even digit in the reversed digits:
- Multiply each digit by two and sum the digits if the answer is greater than nine to form partial sums for the even digits
- Sum the partial sums of the even digits to form s2
- If s1 + s2 ends in zero then the original number is in the form of a valid credit card number as verified by the Luhn test.
For example, if the 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
- Task
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
- Related tasks
11l
F luhn(n)
V ch = String(n)
V sum = 0
V chParity = ch.len % 2
L(i) (ch.len-1 .. 0).step(-1)
V j = Int(ch[i])
I (i + 1) % 2 != chParity
j *= 2
I j > 9
j -= 9
sum += j
R sum % 10 == 0
L(n) (49927398716,
49927398717,
1234567812345678,
1234567812345670)
print(luhn(n))
- Output:
1B 0B 0B 1B
360 Assembly
For maximum compatibility, this program uses only the basic instruction set (S/360) and an ASSIST macro (XPRNT) to keep the code as short as possible.
* Luhn test of credit card numbers 22/05/2016
LUHNTEST CSECT
USING LUHNTEST,R13 base register
B 72(R15) skip savearea
DC 17F'0' savearea
STM R14,R12,12(R13) prolog
ST R13,4(R15) "
ST R15,8(R13) "
LR R13,R15 "
LA R9,T @t(k)
LA R8,N for n
LOOPK EQU * for k=1 to n
LR R4,R9 @t(k),@s[1]
LA R6,1 from i=1
LA R7,M to m
LOOPI1 CR R6,R7 for i=1 to m
BH ELOOPI1 leave i
CLI 0(R4),C' ' if mid(s,i,1)=" "
BNE ITERI1 then
BCTR R6,0 i-1
ST R6,L l=i-1
B ELOOPI1 exit for
* end if
ITERI1 LA R4,1(R4) next @s[i]
LA R6,1(R6) i=i+1
B LOOPI1 next i
ELOOPI1 EQU * out of loop i
MVC W,BLANK w=" "
LA R4,W iw=@w
LR R5,R9 is=@s
A R5,L is=@s+l
BCTR R5,0 is=s+l-1
L R6,L i=l
LA R7,1 to 1
LOOPI2 CR R6,R7 for i=l to 1 by -1
BL ELOOPI2 leave i
MVC 0(1,R4),0(R5) mid(w,iw,1)=mid(s,is,1)
LA R4,1(R4) iw=iw+1
BCTR R5,0 is=is-1
BCTR R6,0 i=i-1
B LOOPI2 next i
ELOOPI2 EQU * out of loop i
LA R11,0 s1=0
LA R12,0 s2=0
LA R6,1 i=1
L R7,L to l
LOOPI3 CR R6,R7 for i=1 to l
BH ELOOPI3 leave i
LA R2,W-1 @w-1
AR R2,R6 w[i]
MVC CI,0(R2) ci=mid(w,i,1)
NI CI,X'0F' zap upper half byte
LR R4,R6 i
SRDA R4,32 >>32
D R4,=F'2' i/2
LTR R4,R4 if mod(i,2)>0
BNH NOTMOD then
XR R2,R2 clear
IC R2,CI z=cint(mid(w,i,1))
AR R11,R2 s1=s1+cint(mid(w,i,1))
B EIFMOD else
NOTMOD XR R2,R2 clear
IC R2,CI cint(mid(w,i,1))
SLA R2,1 *2
ST R2,Z z=cint(mid(w,i,1))*2
C R2,=F'10' if z<10
BNL GE10 then
A R12,Z s2=s2+z
B EIF10 else
GE10 L R2,Z z
CVD R2,PL8 binary to packed
UNPK CL16,PL8 packed to zoned
OI CL16+15,X'F0' zoned to char (zap sign)
MVC X(1),CL16+15 x=right(cstr(z),1)
NI X,X'0F' zap upper half byte
XR R2,R2 r2=0
IC R2,X r2=cint(right(cstr(z),1))
AR R12,R2 s2=s2+r2
LA R12,1(R12) s2=s2+cint(right(cstr(z),1))+1
EIF10 EQU * end if
EIFMOD EQU * end if
LA R6,1(R6) i=i+1
B LOOPI3 next i
ELOOPI3 EQU * out of loop i
LR R1,R11 s1
AR R1,R12 s1+s2
CVD R1,PL8 binary to packed
UNPK CL16,PL8 packed to zoned
CLI CL16+15,X'C0' if right(cstr(s1+s2),1)="0"
BNE NOTZERO then
MVC R,=CL8'Valid' r="Valid"
B ECLI else
NOTZERO MVC R,=CL8'Invalid' r="Invalid"
ECLI EQU * end if
MVC PG(M),0(R9) t(k)
MVC PG+M+1(L'R),R r
XPRNT PG,L'PG print buffer
LA R9,M(R9) at=at+m
BCT R8,LOOPK next k
L R13,4(0,R13) epilog
LM R14,R12,12(R13) "
XR R15,R15 "
BR R14 exit
N EQU (TEND-T)/L'T
M EQU 20
T DC CL(M)'49927398716 '
DC CL(M)'49927398717 '
DC CL(M)'1234567812345678 '
DC CL(M)'1234567812345670 '
TEND DS 0C
W DS CL(M)
BLANK DC CL(M)' '
L DS F
Z DS F
PL8 DS PL8
CL16 DS CL16
CI DS C
X DS C
R DS CL8
PG DC CL80' ' buffer
YREGS
END LUHNTEST
- Output:
49927398716 Valid 49927398717 Invalid 1234567812345678 Invalid 1234567812345670 Valid
8080 Assembly
org 100h
jmp demo
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Check if the 0-terminated string at HL passes the Luhn test.
;;; Returns with carry clear if the string passes, carry set
;;; if the string fails.
luhn: mvi b,0 ; Counter
mov d,b ; D = S1+S2 (we don't need to keep them separate)
lscan: mov a,m ; Get byte
inx h ; Increment pointer
inr b ; Increment counter
ana a ; Is it 0?
jnz lscan ; If not, try next byte
dcx h ; Go to the byte before the 0
dcx h
dcr b ; Decrement counter
rz ; If 0, the string was empty, return.
lloop: mvi c,'0' ; ASCII zero
mov a,d ; Add odd digit to the total
add m
sub c ; Subtract ASCII zero
mov d,a
dcr b ; If last digit, we're done
jz ldone
dcx h ; Go back one byte
mov a,m ; Get even digit
sub c ; Subtract ASCII zero
add a ; Multiply by two
mvi c,9 ; 10-1, compensate for extra subtraction loop
ldiv: inr c ; Find two digits using trial subtraction
sui 10
jnc ldiv
add c ; Add the possible second digit in
add d ; Add it to the total
mov d,a
dcx h ; Go back one byte
dcr b ; Done yet?
jnz lloop
ldone: mov a,d ; See if total is divisible by 10
mvi b,10
lchk: sub b ; Trial subtraction, subtract 10
rz ; If zero, it is divisible, return (carry clear)
rc ; If carry, it is not divisible, return (carry set)
jmp lchk
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Run the routine on the argument given on the CP/M command line
demo: lxi h,80h ; Zero-terminate the command line argument
mov a,m
add l
mov l,a
inr l
mvi m,0
mvi l,82h ; Run the 'luhn' subroutine
call luhn
mvi c,9
lxi d,pass ; Carry clear = print 'Pass'
jnc 5
lxi d,fail ; Carry set = print 'Fail'
jmp 5
pass: db 'Pass$'
fail: db 'Fail$'
- Output:
A>luhn 49927398716 Pass A>luhn 49927398717 Fail A>luhn 1234567812345678 Fail A>luhn 1234567812345670 Pass
8086 Assembly
bits 16
cpu 8086
org 100h
section .text
jmp demo
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Check whether the 0-terminated string at DS:SI passes the Luhn
;;; test. Returns with carry clear if the string passes, carry
;;; set if the string fails.
luhn: push es ; Keep original ES, and set ES=DS so SCASB can be used.
push ds ; "REP DS:SCASB" is a bad idea, because the 286 has a
pop es ; bug where it "forgets" the 2nd prefix if interrupted!
mov di,si ; DI = pointer
xor ax,ax ; Zero to test against
xor bl,bl ; BL = S1+S2
mov dx,0A30h ; DH = 10 (divisor), DL = '0' (ASCII zero)
xor cx,cx ; Set counter to 65535
dec cx
cld ; Seek forwards
repnz scasb ; Find zero
dec di ; SCASB goes one byte too far
xchg si,di ; SI = pointer, DI = end (or rather, beginning)
mov cx,si ; CX = counter
sub cx,di
jcxz .done ; Empty string = stop
dec si ; We don't need the zero itself
std ; Seek backwards
.loop: lodsb ; Get number in odd position
sub al,dl ; Subtract ASCII zero
add bl,al ; Add to total
dec cx ; One fewer character
jz .done ; No more characters = stop
lodsb ; Get number in even position
sub al,dl ; Subtract ASCII zero
add al,al ; Multiply by two
xor ah,ah ; AX = AL
div dh ; Divide by 10; AL=quotient, AH=remainder
add al,ah ; Add the two "digits" together
add bl,al ; Add to total
loop .loop ; Decrement CX and loop
.done: xor ah,ah ; Divide total by 10
mov al,bl
div dh
and ah,ah ; If remainder 0, then return with carry clear
jz .out
stc ; Set carry (remainder wasn't 0, the test failed)
.out: cld ; Clean up: clear direction flag,
pop es ; and restore ES.
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Run the 'luhn' routine on the argument given on the MS-DOS
;;; command line.
demo: mov si,80h ; Zero-terminate the argument
xor bh,bh
mov bl,[si]
mov [si+bx+1],bh
inc si
inc si
call luhn ; Call the routine
mov ah,9
mov dx,pass ; If carry is clear, print 'Pass'
jnc print
mov dx,fail ; Otherwise, print 'fail'
print: int 21h
ret
section .data
pass: db 'Pass$'
fail: db 'Fail$'
- Output:
C:\>luhn86 49927398716 Pass C:\>luhn86 49927398717 Fail C:\>luhn86 1234567812345678 Fail C:\>luhn86 1234567812345670 Pass
8th
\ Adapted from the C version:
: remap \ n1 -- n2
[0,2,4,6,8,1,3,5,7,9]
swap caseof ;
: luhn \ s -- f
0 swap
s:rev
(
'0 n:-
swap 2 n:mod if remap then
n:+
) s:each
10 n:mod not ;
: test-luhn \ s --
dup . space
luhn if "OK" else "FAIL" then . cr ;
"49927398716" test-luhn
"49927398717" test-luhn
"1234567812345678" test-luhn
"1234567812345670" test-luhn
bye
- Output:
49927398716 OK 49927398717 FAIL 1234567812345678 FAIL 1234567812345670 OK
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.
ACL2
(include-book "arithmetic-3/top" :dir :system)
(defun digits (n)
(if (zp n)
nil
(cons (mod n 10)
(digits (floor n 10)))))
(defun sum (xs)
(if (endp xs)
0
(+ (first xs)
(sum (rest xs)))))
(defun double-and-sum-digits (xs)
(if (endp xs)
nil
(cons (sum (digits (* 2 (first xs))))
(double-and-sum-digits (rest xs)))))
(defun dmx (xs)
(if (endp (rest xs))
(mv xs nil)
(mv-let (odds evens)
(dmx (rest (rest xs)))
(mv (cons (first xs) odds)
(cons (second xs) evens)))))
(defun luhn (n)
(mv-let (odds evens)
(dmx (digits n))
(= (mod (+ (sum odds)
(sum (double-and-sum-digits evens)))
10)
0)))
- Output:
> (luhn 49927398716) T > (luhn 49927398717) NIL > (luhn 1234567812345678) NIL > (luhn 1234567812345670) T
Action!
PROC ReverseDigits(CHAR ARRAY n,rev)
BYTE i,j
i=n(0)
WHILE i>0 AND n(i)='0
DO
i==-1
OD
j=1
WHILE i>0
DO
rev(j)=n(i)
j==+1 i==-1
OD
rev(0)=j-1
RETURN
BYTE FUNC SumOddDigits(CHAR ARRAY n)
BYTE sum,i
sum=0
FOR i=1 TO n(0) STEP 2
DO
sum==+ValB(n(i))
OD
RETURN(sum)
BYTE FUNC SumEvenDigitsMultiplied(CHAR ARRAY n)
BYTE sum,i,v
sum=0
FOR i=2 TO n(0) STEP 2
DO
v=ValB(n(i))*2
IF v>9 THEN v==-9 FI
sum==+v
OD
RETURN(sum)
BYTE FUNC Luhn(CHAR ARRAY n)
CHAR ARRAY rev(20)
BYTE s1,s2
ReverseDigits(n,rev)
s1=SumOddDigits(rev)
s2=SumEvenDigitsMultiplied(rev)
IF (s1+s2) MOD 10=0 THEN
RETURN(1)
FI
RETURN(0)
PROC Test(CHAR ARRAY n)
PrintF("%S is ",n)
IF Luhn(n) THEN
PrintE("valid")
ELSE
PrintE("invalid")
FI
RETURN
PROC Main()
Test("49927398716")
Test("49927398717")
Test("1234567812345678")
Test("1234567812345670")
RETURN
- Output:
Screenshot from Atari 8-bit computer
49927398716 is valid 49927398717 is invalid 1234567812345678 is invalid 1234567812345670 is valid
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"));
Ada
with Ada.Text_IO;
use Ada.Text_IO;
procedure Luhn is
function Luhn_Test (Number: String) return Boolean is
Sum : Natural := 0;
Odd : Boolean := True;
Digit: Natural range 0 .. 9;
begin
for p in reverse Number'Range loop
Digit := Integer'Value (Number (p..p));
if Odd then
Sum := Sum + Digit;
else
Sum := Sum + (Digit*2 mod 10) + (Digit / 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;
- Output:
TRUE FALSE FALSE TRUE
ALGOL 68
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
)
- Output:
49927398716: T 49927398717: F 1234567812345678: F 1234567812345670: T
ALGOL W
Separate source so the LuhnTest procedure can be used in other tasks, e.g.: Validate International Securities Identification Number
% returns true if ccNumber passes the Luhn test, false otherwise %
% as Algol W has fixed length strings, the length of the number %
% must be specified in ccLength %
logical procedure LuhnTest ( string(32) value ccNumber
; integer value ccLength
) ;
begin
integer checkSum;
logical oddDigit, isValid;
checkSum := 0;
isValid := oddDigit := true;
for cPos := ccLength step -1 until 1 do begin
integer digit;
digit := decode( ccNumber( cPos - 1 // 1 ) ) - decode( "0" );
if digit < 0 or digit > 9 then isValid := false
else if oddDigit
then checkSum := checkSum + digit
else checkSum := checkSum + ( case digit + 1 of ( 0, 2, 4, 6, 8
, 1, 3, 5, 7, 9
)
);
oddDigit := not oddDigit
end for_cPos ;
isValid and ( ( checkSum rem 10 ) = 0 )
end LuhnTest
Use the above to test the LuhnTest procedure:
begin
% external procedure that returns true if ccNumber passes the Luhn test, false otherwise %
logical procedure LuhnTest ( string(32) value ccNumber
; integer value ccLength
) ; algol "LUHN" ;
% task test cases %
procedure testLuhnTest ( string(32) value ccNumber
; integer value ccLength
) ;
write( s_w := 0, ccNumber, if LuhnTest( ccNumber, ccLength ) then " is valid" else " is invalid" );
testLuhnTest( "49927398716", 11 );
testLuhnTest( "49927398717", 11 );
testLuhnTest( "1234567812345678", 16 );
testLuhnTest( "1234567812345670", 16 )
end.
- Output:
49927398716 is valid 49927398717 is invalid 1234567812345678 is invalid 1234567812345670 is valid
APL
LuhnTest←{
digits←⍎¨⍵ ⍝ Characters to digits
doubled←2∘×@(⌽⍤~1 0⍴⍨≢)⊢digits ⍝ Double every other digit
partial←-∘9@(9∘<)⊢doubled ⍝ Subtract 9 is equivalent to sum of digits for the domain 10≤x≤19
0=10|+/partial ⍝ Valid if sum is a multiple of 10
}
- Output:
LuhnTest¨'49927398716' '49927398717' '1234567812345678' '1234567812345670' 1 0 0 1
∇ 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
∇
- Output:
LuhnTest¨ 49927398716 49927398717 1234567812345678 1234567812345670 1 0 0 1
AppleScript
Functional
-- luhn :: String -> Bool
on luhn(s)
-- True if the digit string represents
-- a valid Luhn credit card number.
script divMod10Sum
on |λ|(a, x)
a + x div 10 + x mod 10
end |λ|
end script
0 = foldl(divMod10Sum, 0, ¬
zipWith(my mul, ¬
map(my int, reverse of (characters of s)), ¬
cycle({1, 2}))) mod 10
end luhn
--------------------------- TEST ---------------------------
on run
map(luhn, ¬
{"49927398716", "49927398717", ¬
"1234567812345678", "1234567812345670"})
--> {true, false, false, true}
end run
---------------- REUSABLE GENERIC FUNCTIONS ----------------
-- cycle :: [a] -> Generator [a]
on cycle(xs)
script
property lng : 1 + (length of xs)
property i : missing value
on |λ|()
if missing value is i then
set i to 1
else
set nxt to (1 + i) mod lng
if 0 = ((1 + i) mod lng) then
set i to 1
else
set i to nxt
end if
end if
return item i of xs
end |λ|
end script
end cycle
-- foldl :: (a -> b -> a) -> a -> [b] -> a
on foldl(f, startValue, xs)
tell mReturn(f)
set v to startValue
set lng to length of xs
repeat with i from 1 to lng
set v to |λ|(v, item i of xs, i, xs)
end repeat
return v
end tell
end foldl
-- int :: String -> Int
on int(s)
s as integer
end int
-- length :: [a] -> Int
on |length|(xs)
set c to class of xs
if list is c or string is c then
length of xs
else
(2 ^ 29 - 1) -- (maxInt - simple proxy for non-finite)
end if
end |length|
-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
-- 2nd class handler function lifted into 1st class script wrapper.
if script is class of f then
f
else
script
property |λ| : f
end script
end if
end mReturn
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
-- The list obtained by applying f
-- to each element of xs.
tell mReturn(f)
set lng to length of xs
set lst to {}
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs, i, xs)
end repeat
return lst
end tell
end map
-- min :: Ord a => a -> a -> a
on min(x, y)
if y < x then
y
else
x
end if
end min
-- mul (*) :: Num a => a -> a -> a
on mul(a, b)
a * b
end mul
-- take :: Int -> [a] -> [a]
-- take :: Int -> String -> String
on take(n, xs)
set c to class of xs
if list is c then
if 0 < n then
items 1 thru min(n, length of xs) of xs
else
{}
end if
else if string is c then
if 0 < n then
text 1 thru min(n, length of xs) of xs
else
""
end if
else if script is c then
set ys to {}
repeat with i from 1 to n
set v to |λ|() of xs
if missing value is v then
return ys
else
set end of ys to v
end if
end repeat
return ys
else
missing value
end if
end take
-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
on zipWith(f, xs, ys)
set lng to min(|length|(xs), |length|(ys))
if 1 > lng then return {}
set xs_ to take(lng, xs) -- Allow for non-finite
set ys_ to take(lng, ys) -- generators like cycle etc
set lst to {}
tell mReturn(f)
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs_, item i of ys_)
end repeat
return lst
end tell
end zipWith
- Output:
{true, false, false, true}
Straightforward
on luhnTest(n)
-- Accept only text input.
if (n's class is not text) then return false
-- Edit out any spaces or dashes.
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to {space, "-"}
set n to n's text items
set AppleScript's text item delimiters to ""
set n to n as text
set AppleScript's text item delimiters to astid
-- Check that what's left is numeric.
try
n as number
on error
return false
end try
-- Do the calculation two digits at a time, starting at the end of the text and working back.
set sum to 0
repeat with i from ((count n) - 1) to 1 by -2
set n2 to (text i thru (i + 1) of n) as integer
tell n2 div 10 mod 10 * 2 to set sum to sum + it div 10 + it mod 10 + n2 mod 10
end repeat
-- If there's an odd digit left over, add that in too.
if (i is 2) then set sum to sum + (character 1 of n)
return (sum mod 10 is 0)
end luhnTest
-- Test code:
set testResults to {}
repeat with testNumber in {"49927398716", "49927398717", "1234567812345678", "1234567812345670"}
set end of testResults to {testNumber:testNumber's contents, valid:luhnTest(testNumber)}
end repeat
return testResults
- Output:
{{testNumber:"49927398716", valid:true}, {testNumber:"49927398717", valid:false}, {testNumber:"1234567812345678", valid:false}, {testNumber:"1234567812345670", valid:true}}
ARM Assembly
.text
.global _start
_start:
ldr r0, =example_numbers
bl test_number
add r1, r0, #1
bl length
add r0, r1, r0
bl test_number
add r1, r0, #1
bl length
add r0, r1, r0
bl test_number
add r1, r0, #1
bl length
add r0, r1, r0
bl test_number
mov r0, #0
mov r7, #1
swi 0
test_number:
push {r0, lr}
bl print_string
bl luhn_test
cmp r0, #1
ldreq r0, =valid_message
ldrne r0, =invalid_message
bl print_string
pop {r0, lr}
mov pc, lr
print_string:
push {r0-r7, lr}
mov r1, r0 @ string to print
bl length
mov r2, r0 @ length of string
mov r0, #1 @ write to stdout
mov r7, #4 @ SYS_WRITE
swi 0 @ call system interupt
pop {r0-r7, lr}
mov pc, lr
@ r0 address of credit card number string
@ returns result in r0
luhn_test:
push {r1-r7, lr}
mov r1, r0
bl isNumerical @ check if string is a number
cmp r0, #1
bne .luhn_test_end @ exit if not number
mov r0, r1
ldr r1, =reversed_string @ address to store reversed string
bl reverse @ reverse string
push {r0}
bl length @ get length of string
mov r4, r0 @ store string length in r4
pop {r0}
mov r2, #0 @ string index
mov r6, #0 @ sum of odd digits
mov r7, #0 @ sum of even digits
.loadNext:
ldrb r3, [r1, r2] @ load byte into r3
sub r3, #'0' @ convert letter to digit
and r5, r2, #1 @ test if index is even or odd
cmp r5, #0
beq .odd_digit
bne .even_digit
.odd_digit:
add r6, r3 @ add digit to sum if odd
b .continue @ skip next step
.even_digit:
lsl r3, #1 @ multiply digit by 2
cmp r3, #10 @ sum digits
subge r3, #10 @ get digit in 1s place
addge r3, #1 @ add 1 for the 10s place
add r7, r3 @ add digit sum to the total
.continue:
add r2, #1 @ increment digit index
cmp r2, r4 @ check if at end of string
blt .loadNext
add r0, r6, r7 @ add even and odd sum
mov r3, r0 @ copy sum to r3
ldr r1, =429496730 @ (2^32-1)/10
sub r0, r0, r0, lsr #30 @ divide by 10
umull r2, r0, r1, r0
mov r1, #10
mul r0, r1 @ multiply the r0 by 10 to see if divisible
cmp r0, r3 @ compare with the original value in r3
.luhn_test_end:
movne r0, #0 @ return false if invalid card number
moveq r0, #1 @ return true if valid card number
pop {r1-r7, lr}
mov pc, lr
length:
push {r1-r2, lr}
mov r2, r0 @ start of string address
.loop:
ldrb r1, [r2], #1 @ load byte from address r2 and increment
cmp r1, #0 @ check for end of string
bne .loop @ load next byte if not 0
sub r0, r2, r0 @ subtract end of string address from start
sub r0, #1 @ end of line from count
pop {r1-r2, lr}
mov pc, lr
@ reverses a string
@ r0 address of string to reverse
@ r1 address to store reversed string
reverse:
push {r0-r5, lr}
push {r0, lr}
bl length @ get length of string to reverse
mov r3, r0 @ backword index
pop {r0, lr}
mov r4, #0 @ fowrard index
.reverse_next:
sub r3, #1 @ decrement backword index
ldrb r5, [r0, r3] @ load byte from original string at index
strb r5, [r1, r4] @ copy byte to reversed string
add r4, #1 @ increment fowrard index
cmp r3, #0 @ check if any characters are left
bge .reverse_next
mov r5, #0
strb r5, [r1, r4] @ write null byte to terminate reversed string
pop {r0-r5, lr}
mov pc, lr
isNumerical:
push {r1, lr}
.isNumerical_checkNext:
ldrb r1, [r0], #1
cmp r1, #0
beq .isNumerical_true
cmp r1, #'0'
blt .isNumerical_false
cmp r1, #'9'
bgt .isNumerical_false
b .isNumerical_checkNext
.isNumerical_false:
mov r0, #0
b .isNumerical_end
.isNumerical_true:
mov r0, #1
.isNumerical_end:
pop {r1, lr}
mov pc, lr
.data
valid_message:
.asciz " valid card number\n"
invalid_message:
.asciz " invalid card number\n"
reversed_string:
.space 32
example_numbers:
.asciz "49927398716"
.asciz "49927398717"
.asciz "1234567812345678"
.asciz "1234567812345670"
Arturo
digits: function [n][
res: new []
while -> n > 0 [
'res ++ n % 10
n: n / 10
]
res
]
luhn?: function [n][
s1: new 0
s2: new 0
loop.with: 'i digits n 'd [
if? even? i -> 's1 + d
else [
'd * 2
if d > 9 -> 'd - 9
's2 + d
]
]
zero? (s1 + s2) % 10
]
print luhn? 49927398716
print luhn? 49927398717
print luhn? 1234567812345678
print luhn? 1234567812345670
- Output:
true false false true
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)
}
AutoIt
Global $avarray[4] = [49927398716, 49927398717, 1234567812345678, 1234567812345670]
For $i = 0 To 3
checkLuhn($avarray[$i])
Next
Func checkLuhn($number)
$sum = 0
$numDigits = StringSplit($number, "")
For $i = $numDigits[0] - 1 To 1 Step -2
$numDigits[$i] = $numDigits[$i] * 2
If $numDigits[$i] >= 10 Then $numDigits[$i] -= 9
Next
For $i = 1 To $numDigits[0]
$sum += $numDigits[$i]
Next
If StringRight($sum, 1) = "0" Then
ConsoleWrite("Luhn-Check (" & $number & ") : True" & @CRLF)
Return True
Else
ConsoleWrite("Luhn-Check (" & $number & ") : False" & @CRLF)
Return False
EndIf
EndFunc ;==>checkLuhn
- Output:
Luhn-Check (49927398716) : True Luhn-Check (49927398717) : False Luhn-Check (1234567812345678) : False Luhn-Check (1234567812345670) : True
AWK
#!/usr/bin/awk -f
BEGIN {
A[1] = 49927398716;
A[2] = 49927398717;
A[3] = 1234567812345678;
A[4] = 1234567812345670;
A[5] = "01234567897";
A[6] = "01234567890";
A[7] = "00000000000";
for (k in A) print "isLuhn("A[k]"): ",isLuhn(A[k]);
}
function isLuhn(cardno) {
s = 0;
m = "0246813579";
n = length(cardno);
for (k = n; 0 < k; k -= 2) {
s += substr(cardno, k, 1);
}
for (k = n-1; 0 < k; k -= 2) {
s += substr(m, substr(cardno, k, 1)+1, 1);
}
return ((s%10)==0);
}
- Output:
isLuhn(1234567812345670): 1 isLuhn(01234567897): 1 isLuhn(01234567890): 0 isLuhn(00000000000): 1 isLuhn(49927398716): 1 isLuhn(49927398717): 0 isLuhn(1234567812345678): 0
Bash
#!/bin/bash
function luhn_validate # <numeric-string>
{
num=$1
shift 1
len=${#num}
is_odd=1
sum=0
for((t = len - 1; t >= 0; --t)) {
digit=${num:$t:1}
if [[ $is_odd -eq 1 ]]; then
sum=$(( sum + $digit ))
else
sum=$(( $sum + ( $digit != 9 ? ( ( 2 * $digit ) % 9 ) : 9 ) ))
fi
is_odd=$(( ! $is_odd ))
}
# NOTE: returning exit status of 0 on success
return $(( 0 != ( $sum % 10 ) ))
}
function print_result # <numeric-string>
{
if luhn_validate "$1"; then
echo "$1 is valid"
else
echo "$1 is not valid"
fi
}
print_result "49927398716"
print_result "49927398717"
print_result "1234567812345678"
print_result "1234567812345670"
- Output:
49927398716 is valid 49927398717 is not valid 1234567812345678 is not valid 1234567812345670 is valid
BASIC
BASIC256
dim card$(5)
card$[1]="49927398716"
card$[2]="49927398717"
card$[3]="1234567812345678"
card$[4]="1234567812345670"
for test = 1 to 4
odd = True
sum = 0
for n = length(card$[test]) to 1 step -1
num = int(mid(card$[test],n,1))
if odd then
sum += num
odd = False
else
num *= 2
if num <= 9 then
sum += num
else
sum += int(left(string(num),1)) + int(right(string(num),1))
end if
odd = True
end if
next
if sum mod 10 = 0 then
print card$[test], "True"
else
print card$[test], "False"
end if
next test
Chipmunk Basic
100 cls
110 rem Luhn test
120 dim card$(5)
130 card$(1) = "49927398716"
140 card$(2) = "49927398717"
150 card$(3) = "1234567812345678"
160 card$(4) = "1234567812345670"
170 for test = 1 to 4
180 odd = true
190 sum = 0
200 for n = len(card$(test)) to 1 step -1
210 num = val(mid$(card$(test),n,1))
220 if odd then
230 sum = sum+num
240 odd = false
250 else
260 num = num*2
270 if num <= 9 then
280 sum = sum+num
290 else
300 sum = sum+val(left$(str$(num),1))+val(right$(str$(num),1))
310 endif
320 odd = true
330 endif
340 next
350 if sum mod 10 = 0 then
360 print card$(test),"True"
370 else
380 print card$(test),"False"
390 endif
400 next test
IS-BASIC
100 PROGRAM "CredCard.bas"
110 DO
120 PRINT :PRINT "Credit card number:":INPUT PROMPT ">":CCN$
130 IF CCN$="" THEN EXIT DO
140 IF LUHN(TRIM$(CCN$)) THEN
150 PRINT "Card number is valid."
160 ELSE
170 SET #102:INK 3:PRINT "Card number is invalid.":SET #102:INK 1
180 END IF
190 LOOP
200 DEF LUHN(CCN$)
210 LET L=LEN(CCN$):LET S=0
220 FOR I=1 TO L
230 LET N=VAL(CCN$(L-I+1))
240 IF I BAND 1 THEN
250 LET S=S+N
260 ELSE
270 LET N=N*2:LET S=S+MOD(N,10)+INT(N/10)
280 END IF
290 NEXT
300 LET LUHN=MOD(S,10)=0
310 END DEF
320 DEF TRIM$(S$)
330 LET T$=""
340 FOR I=1 TO LEN(S$)
350 IF S$(I)>CHR$(47) AND S$(I)<CHR$(58) THEN LET T$=T$&S$(I)
360 NEXT
370 LET TRIM$=T$
380 END DEF
Output:
Credit card number: >49927398716 Card number is valid. Credit card number: >49927398717 Card number is invalid. Credit card number: >1234 5678 1234 5678 Card number is invalid. Credit card number: >1234 5678 1234 5670 Card number is valid.
QBasic
CONST True = -1: False = NOT True
DIM card$(5)
card$(1) = "49927398716"
card$(2) = "49927398717"
card$(3) = "1234567812345678"
card$(4) = "1234567812345670"
FOR test = 1 TO 4
odd = True
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 = False
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 = True
END IF
NEXT
IF sum MOD 10 = 0 THEN
PRINT card$(test), "True"
ELSE
PRINT card$(test), "False"
END IF
NEXT test
True BASIC
LET true = -1
LET false = 0
DIM card$(5)
LET card$(1) = "49927398716"
LET card$(2) = "49927398717"
LET card$(3) = "1234567812345678"
LET card$(4) = "1234567812345670"
FOR test = 1 TO 4
LET odd = true
LET sum = 0
FOR n = LEN(card$(test)) TO 1 STEP -1
LET num = VAL((card$(test))[n:n+1-1])
IF odd<>0 THEN
LET sum = sum + num
LET odd = false
ELSE
LET num = num*2
IF num <= 9 THEN
LET sum = sum + num
ELSE
LET sum = sum + VAL((STR$(num))[1:1]) + VAL((STR$(num))[LEN(STR$(num))-1+1:maxnum])
END IF
LET odd = true
END IF
NEXT n
IF remainder(round(sum),10) = 0 THEN PRINT card$(test), "True" ELSE PRINT card$(test), "False"
NEXT test
END
uBasic/4tH
Print " 49927398716", Show (Iif(FUNC(_Luhn ("49927398716")), "ok", "fail"))
Print " 49927398717", Show (Iif(FUNC(_Luhn ("49927398717")), "ok", "fail"))
Print "1234567812345678", Show (Iif(FUNC(_Luhn ("1234567812345678")), "ok", "fail"))
Print "1234567812345670", Show (Iif(FUNC(_Luhn ("1234567812345670")), "ok", "fail"))
End
_Luhn
Param (1)
Local (4)
c@ = 1 : d@ = 0
For b@ = Len(a@)-1 To 0 Step -1
e@ = Peek(a@, b@) - Ord("0")
d@ = d@ + Iif (c@, e@, e@+e@-9*(e@>4))
c@ = c@ = 0
Next
Return ((d@ % 10) = 0)
- Output:
49927398716 ok 49927398717 fail 1234567812345678 fail 1234567812345670 ok 0 OK, 0:333
Yabasic
dim card$(5)
card$(1)="49927398716"
card$(2)="49927398717"
card$(3)="1234567812345678"
card$(4)="1234567812345670"
for test = 1 to 4
odd = true
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 = false
else
num = num * 2
if num <= 9 then
sum = sum + num
else
sum = sum + val(left$(str$(num),1)) + val(right$(str$(num),1))
fi
odd = true
fi
next
if mod(sum, 10) = 0 then
print card$(test), chr$(9), "True"
else
print card$(test), chr$(9), "False"
fi
next test
Batch File
This simple implementation does not reverse the numbers. Instead, it counts from right to left.
@echo off
setlocal enabledelayedexpansion
call :luhn 49927398716
call :luhn 49927398717
call :luhn 1234567812345678
call :luhn 1234567812345670
exit /b 0
:luhn
set "input=%1"
set "cnt=0"
set "s1=0"
set "s2=0"
:digit_loop
set /a "cnt-=1"
set /a "isOdd=(-%cnt%)%%2"
if !isodd! equ 1 (
set /a "s1+=!input:~%cnt%,1!"
) else (
set /a "twice=!input:~%cnt%,1!*2"
if !twice! geq 10 (
set /a "s2+=!twice:~0,1!+!twice:~1,1!"
) else (
set /a "s2+=!twice!"
)
)
if "!input:~%cnt%!"=="!input!" (
set /a "sum=(!s1!+!s2!)%%10"
if !sum! equ 0 (echo !input! is valid.) else (echo !input! is not valid.)
goto :EOF
)
goto digit_loop
- Output:
>luhn.bat 49927398716 is valid. 49927398717 is not valid. 1234567812345678 is not valid. 1234567812345670 is valid. >
BBC BASIC
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
bc
/* Return 1 if number passes Luhn test, else 0 */
define l(n) {
auto m, o, s, x
o = scale
scale = 0
m = 1
while (n > 0) {
x = (n % 10) * m
if (x > 9) x -= 9
s += x
m = 3 - m
n /= 10
}
s %= 10
scale = o
if (s) return(0)
return(1)
}
l(49927398716)
l(49927398717)
l(1234567812345678)
l(1234567812345670)
- Output:
1 0 0 1
BCPL
get "libhdr"
let luhn(s) = valof
$( let sum=0 and fac=1
for i = s%0 to 1 by -1
$( unless '0' <= s%i <= '9' resultis false
sum := sum + fac*(s%i - '0') rem 10 + fac*(s%i - '0')/10
fac := 3 - fac
$)
resultis sum rem 10 = 0
$)
let show(s) be
writef("%S: %S*N", s, luhn(s) -> "pass", "fail")
let start() be
$( show("49927398716")
show("49927398717")
show("1234567812345678")
show("1234567812345670")
$)
- Output:
49927398716: pass 49927398717: fail 1234567812345678: fail 1234567812345670: pass
Befunge
v 1 >$0 v v <
>&:19+`|v < >v 5 6 7 8
^ \ <>09p19p>09g+09p:|>2*:19+%19g+19p19+/19g+19p:|
2 3 4 > v
v"invalid"<10 9
|%+91+g91g90<
v "valid"<
>:#,_@
11
The labelled points (1 to 11) are: 1. Read in input until number greater than 10, 2. Reverse the order, 3. Set accumulators to 0, 4. Add odd number to accumulator, 5. Mod even number with 10, 6. Add this digit to accumulator, 7. Integer divide number by 10, 8. Add this digit to accumulator, 9. Add odd and even accumulators, 10. Mod this accumulator with 10, 11. Print result.
The code requires input be separated by spaces and ended with a number greater than 10 to exit the reading loop. This could be done by reading characters and ending at a new line, but this way is much simpler.
Inputs:
4 9 9 2 7 3 9 8 7 1 6 99 4 9 9 2 7 3 9 8 7 1 7 99 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 99 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 0 99
- Output:
valid invalid invalid valid
BQN
Luhn ← (0=10|⊢)∘(+´(10|⊢)+⊢≥10˙)∘(⊢×≠⥊1‿2˙)∘(⌽•Fmt-'0'˙)
(⍉⊢≍Luhn¨) ⟨49927398716,49927398717,1234567812345678,1234567812345670⟩
- Output:
┌─ ╵ 49927398716 1 49927398717 0 1234567812345678 0 1234567812345670 1 ┘
Bracmat
( luhn
= sum odd even
. 0:?sum
& rev$!arg:?arg
& whl
' ( @( !arg
: %?odd
( %?even ?arg
| :?arg&0:?even
)
)
& !odd+mod$(2*!even.10)+div$(!even.5)+!sum:?sum
)
& mod$(!sum.10):0
)
& ( test
=
. out
$ (!arg ":" (luhn$!arg&true|false))
)
& test$49927398716
& test$49927398717
& test$1234567812345678
& test$1234567812345670
& ;
- Output:
49927398716 : true 49927398717 : false 1234567812345678 : false 1234567812345670 : true
Brainf***
>>>>>>>>>+>,----------[ READ CHARACTERS UNTIL \N AND
>++++++++[<----->-]<++>>>>>>+>,----------] SUBTRACT ASCII 0 FROM EACH
<-<<<<<<< GO TO LAST DIGIT
[ WHILE THERE ARE DIGITS
>>>>>>>>[-<<<<<<<+>>>>>>>]<<<<<<<< ADD RUNNING TOTAL TO ODD DGT
<<<<<<< GO TO EVEN DIGIT
[ IF THERE IS ONE
>[->++<]>[-<+>] MUL BY TWO
>>>++++++++++<<<<[ DIVMOD BY TEN
>>>[-]+>-[<-<]<[<<] DECR DIVISOR
>>[ IF ZERO
>++++++++++>+<<-]<<< SET TO TEN; INCR QUOTIENT
-] DECR DIVIDEND UNTIL ZERO
++++++++++>>>>[-<<<<->>>>] CALCULATE REMAINDER
>[-<<<<<+>>>>>] ADD QUOTIENT TO IT
>>[-<<<<<<<+>>>>>>>]<<<<<<<< THEN ADD RUNNING TOTAL
<<<<< ZERO BEFORE NEXT ODD DIGIT
]<< GO TO NEXT ODD DIGIT
]
>>>>>>>-[+>>-]> GO TO TOTAL
>>>>++++++++++<<<<[ MODULO TEN
>>>[-]+>-[<-<]<[<<] DECR DIVISOR
>>[>++++++++++<-] IF ZERO SET BACK TO TEN
<<<-] DECR DIVIDEND UNTIL ZERO
>>>++++++++++>[-<->]< REMAINDER: TEN MINUS DIVISOR
<<<<<<++++++++++[-> VALUES FOR ASCII OUTPUT
+++++++++++> 110
++++++++++> 100
++++++++> 80
+++++++<<<<] 70
>>>>>+> GO BACK TO REMAINDER
[<<.<<---.<-----.+++.<] IF NOT ZERO FAIL
<[<<.<---.<+++++..<] IF ZERO PASS
++++++++++. NEWLINE
- Output:
$ echo 49927398716 | beef luhn.bf Pass $ echo 49927398717 | beef luhn.bf Fail $ echo 1234567812345678 | beef luhn.bf Fail $ echo 1234567812345670 | beef luhn.bf Pass
Bruijn
:import std/Combinator .
:import std/Math .
:import std/List .
luhn number→list → reverse → check → (\mod (+10)) → zero?
check y [[[[0 [[[6 \5 (4 + (5 odd even)) 1]]] 1]]]] k (+0)
odd 2
even digit-sum (2 ⋅ (+2))
:test (luhn (+61789372994)) ([[1]])
:test (luhn (+49927398716)) ([[1]])
:test (luhn (+49927398717)) ([[0]])
:test (luhn (+1234567812345678)) ([[0]])
:test (luhn (+1234567812345670)) ([[1]])
Burlesque
tt "Remove whitespace"vv
pe "Eval to number"vv
<- "Reverse digits"vv
XX "Split number into digits"vv
{
{ "Odd digits"vv
2EN
}
{ "Even digits"vv
2en
{
2.* "Double"vv
^^ 9.> "<test>=Duplicate greater than 9"vv
{
XX++ "Sum digits"vv
}if "If <test>"vv
}m[ "For each even digit"vv
}
}M- "Cool map. Create array of each branch applied to argument."vv
{++}m[ "Sum each block (odd & even)"vv
++ "Sum these"vv
[- "Last digit"vv
0== "Equal to zero"vv
Q "Pretty print"vv
- Output:
49927398716 1 49927398717 0 1234567812345678 0 1234567812345670 1
C
#include <string.h>
#include <stdio.h>
int luhn(const char* cc)
{
const int m[] = {0,2,4,6,8,1,3,5,7,9}; // mapping for rule 3
int i, odd = 1, sum = 0;
for (i = strlen(cc); i--; odd = !odd) {
int digit = cc[i] - '0';
sum += odd ? digit : m[digit];
}
return sum % 10 == 0;
}
int main()
{
const char* cc[] = {
"49927398716",
"49927398717",
"1234567812345678",
"1234567812345670",
0
};
int i;
for (i = 0; cc[i]; i++)
printf("%16s\t%s\n", cc[i], luhn(cc[i]) ? "ok" : "not ok");
return 0;
}
- Output:
49927398716 ok 49927398717 not ok 1234567812345678 not ok 1234567812345670 ok
C#
The LuhnCheck method takes an array of integers because values in memory will be integer-aligned.
public static class Luhn
{
public static bool LuhnCheck(this string cardNumber)
{
return LuhnCheck(cardNumber.Select(c => c - '0').ToArray());
}
private static bool LuhnCheck(this int[] digits)
{
return GetCheckValue(digits) == 0;
}
private static int GetCheckValue(int[] digits)
{
return digits.Select((d, i) => i % 2 == digits.Length % 2 ? ((2 * d) % 10) + d / 5 : d).Sum() % 10;
}
}
public static class TestProgram
{
public static void Main()
{
long[] testNumbers = {49927398716, 49927398717, 1234567812345678, 1234567812345670};
foreach (var testNumber in testNumbers)
Console.WriteLine("{0} is {1}valid", testNumber, testNumber.ToString().LuhnCheck() ? "" : "not ");
}
}
49927398716 is valid 49927398717 is not valid 1234567812345678 is not valid 1234567812345670 is valid
Note that the original implementation, which follows, is flawed because it assumes that n is a number which, when represented as a string, has an even number of characters. Granted, the brief is for Credit Card Numbers which are all, at the time of writing, an even number of digits.
using System;
using System.Linq;
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);
}
public static bool luhnLinq(long n)
{
string s = n.ToString();
return s.Select((c, i) => (c - '0') << ((s.Length - i - 1) & 1)).Sum(n => n > 9 ? n - 9 : n) % 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);
}
}
}
}
49927398716 is valid 49927398717 is not valid 1234567812345678 is not valid 1234567812345670 is valid
A solution without using LINQ, works for all versions of .NET.
using System;
namespace Luhn_Test
{
public static class Extensions
{
public static string Reverse(this string s )
{
char[] charArray = s.ToCharArray();
Array.Reverse( charArray );
return new string( charArray );
}
}
class Program
{
public static bool Luhn(long x)
{
long s1=0;
long s2=0;
bool STATE=x%10!=0; // If it ends with zero, we want the order to be the other way around
x=long.Parse(x.ToString().Reverse());
while (x!=0)
{
s1+=STATE?x%10:0;
s2+=STATE?0:((x%10)*2>9)?(((x%10)*2/10)+((x%10)*2)%10):((x%10)*2);
STATE=!STATE; //Switch state
x/=10; //Cut the last digit and continue
}
return ((s1+s2)%10==0); //Check if it ends with zero, if so, return true, otherwise,false.
}
public static void Main(string[] args)
{
long[] ks = {1234567812345670, 49927398717, 1234567812345678 ,1234567812345670 };
foreach (long k in ks)
{
Console.WriteLine("{0} is {1} Valid.",k,Luhn(k)?"":"Not");
}
Start:
try {
Console.WriteLine("Enter your credit:");
long x=long.Parse(Console.ReadLine());
Console.WriteLine("{0} Valid.",Luhn(x)?"":"Not");
goto Start;
}
catch (FormatException)
{
goto Start;
}
}
}
}
1234567812345670 is Valid. 49927398717 is Not Valid. 1234567812345678 is Not Valid. 49927398716 is Valid.
A solution optimized for readability:
using System;
using System.Linq;
public class CreditCardLogic
{
static Func<char, int> charToInt = c => c - '0';
static Func<int, int> doubleDigit = n => (n * 2).ToString().Select(charToInt).Sum();
static Func<int, bool> isOddIndex = index => index % 2 == 0;
public static bool LuhnCheck(string creditCardNumber)
{
var checkSum = creditCardNumber
.Select(charToInt)
.Reverse()
.Select((digit, index) => isOddIndex(index) ? digit : doubleDigit(digit))
.Sum();
return checkSum % 10 == 0;
}
}
Extremely compact version uses Europa rtl library https://github.com/CodeAlkemist/Europa-rtl
using System;
using EuropaRTL.Utilities;
public static partial class Algoritmhs
{
public static bool CheckLuhn(long n)
{
int s1 = n.Shatter(true).Subset(2).Arithmetic('+');
int s2 = n.Shatter(true).Subset(1, -1, 2).ArithmeticRA('*', 2).ShatterAndSum().Arithmetic('+');
return (s1 + s2) % 10 == 0 ? true : false;
}
}
class Program
{
static void Main(string[] args)
{
long[] ll = {
49927398716,
49927398717,
1234567812345678,
1234567812345670
};
foreach (var item in ll)
{
item.ToString().WriteLine();
Algoritmhs.CheckLuhn(item).ToString().WriteLine();
}
Console.ReadKey();
}
}
49927398716 True 49927398717 False 1234567812345678 False 1234567812345670 False
C++
#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;
}
C++11
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool luhn( const string& id)
{
static const int m[10] = {0,2,4,6,8,1,3,5,7,9}; // mapping for rule 3
bool is_odd_dgt = false;
auto lambda = [&](int a, char c) {return a + ((is_odd_dgt = !is_odd_dgt) ? c-'0' : m[c-'0']);};
int s = std::accumulate(id.rbegin(), id.rend(), 0, lambda);
return 0 == s%10;
}
int main( )
{
auto t_cases = {"49927398716", "49927398717", "1234567812345678", "1234567812345670"};
auto print = [](const string & s) {cout << s << ": " << luhn(s) << endl;};
for_each(t_cases.begin(), t_cases.end(), print);
return 0;
}
It is also possible to achieve a compile-time version using metaprogramming.
#include <iostream>
#include <type_traits>
template<size_t I, int... Args>
struct find_impl;
template<int A, int... Args>
struct find_impl<0, A, Args...> {
using type = std::integral_constant<int, A>;
};
template<int A, int B, int... Args>
struct find_impl<0, A, B, Args...> {
using type = std::integral_constant<int, A>;
};
template<size_t I, int A, int B, int... Args>
struct find_impl<I, A, B, Args...> {
using type = typename find_impl<I-1, B, Args...>::type;
};
namespace detail {
template<typename, typename>
struct append_sequence
{};
template<typename T, typename... Ts>
struct append_sequence<T, std::tuple<Ts...>> {
using type = std::tuple<Ts..., T>;
};
template<typename... Ts>
struct reverse_sequence {
using type = std::tuple<>;
};
template<typename T, typename... Ts>
struct reverse_sequence<T, Ts...> {
using type = typename append_sequence<
T,
typename reverse_sequence<Ts...>::type
>::type;
};
}
template<size_t I>
using rule3 = typename find_impl<I, 0, 2, 4, 6, 8, 1, 3, 5, 7, 9>::type;
template<int A, char C, bool dgt>
struct calc
: std::integral_constant<int, A + C - '0'>
{};
template<int A, char C>
struct calc<A, C, false>
: std::integral_constant<int, A + rule3<C - '0'>::type::value>
{};
template<typename Acc, bool Dgt, char...>
struct luhn_impl;
template<typename Acc, bool Dgt, char A, char... Args>
struct luhn_impl<Acc, Dgt, A, Args...> {
using type = typename calc<Acc::value, A, Dgt>::type;
};
template<typename Acc, bool Dgt, char A, char B, char... Args>
struct luhn_impl<Acc, Dgt, A, B, Args...> {
using type =
typename luhn_impl<typename calc<Acc::value, A, Dgt>::type, !Dgt, B, Args...>::type;
};
template<typename>
struct luhn;
template<typename... Args>
struct luhn<std::tuple<Args...>> {
using type = typename luhn_impl<std::integral_constant<int, 0>, true, Args::value...>::type;
constexpr static bool result = (type::value % 10) == 0;
};
template<char... Args>
bool operator "" _luhn() {
return luhn<typename detail::reverse_sequence<std::integral_constant<char, Args>...>::type>::result;
}
int main() {
std::cout << std::boolalpha;
std::cout << 49927398716_luhn << std::endl;
std::cout << 49927398717_luhn << std::endl;
std::cout << 1234567812345678_luhn << std::endl;
std::cout << 1234567812345670_luhn << std::endl;
return 0;
}
true false false true
Caché ObjectScript
Class Utils.Check [ Abstract ]
{
ClassMethod Luhn(x As %String) As %Boolean
{
// https://www.simple-talk.com/sql/t-sql-programming/calculating-and-verifying-check-digits-in-t-sql/
SET x=$TRANSLATE(x," "), cd=$EXTRACT(x,*)
SET x=$REVERSE($EXTRACT(x,1,*-1)), t=0
FOR i=1:1:$LENGTH(x) {
SET n=$EXTRACT(x,i)
IF i#2 SET n=n*2 IF $LENGTH(n)>1 SET n=$EXTRACT(n,1)+$EXTRACT(n,2)
SET t=t+n
}
QUIT cd=((t*9)#10)
}
}
- Examples:
USER>For { Read ccn Quit:ccn="" Write ": "_##class(Utils.Check).Luhn(ccn), ! } 49927398716: 1 49927398717: 0 1234567812345678: 0 1234567812345670: 1 USER>
Ceylon
shared void run() {
value numbers = "49927398716
49927398717
1234567812345678
1234567812345670";
for(number in numbers.lines) {
print("``number`` passes? ``luhn(number)``");
}
}
shared Boolean luhn(String number) {
value digits = number
.reversed
.map(Character.string)
.map(Integer.parse)
.narrow<Integer>();
value s1 = sum { 0, *digits.by(2) };
value s2 = sum {
0,
*digits
.skip(1)
.by(2)
.map(curry(times<Integer>)(2))
.map((Integer element) => element / 10 + element % 10)
};
return (s1 + s2) % 10 == 0;
}
Clojure
(defn luhn? [cc]
(let [factors (cycle [1 2])
numbers (map #(Character/digit % 10) cc)
sum (reduce + (map #(+ (quot % 10) (mod % 10))
(map * (reverse numbers) factors)))]
(zero? (mod sum 10))))
(doseq [n ["49927398716" "49927398717" "1234567812345678" "1234567812345670"]]
(println (luhn? n)))
CLU
luhn = proc (num: string) returns (bool) signals (bad_format)
total: int := 0
even: bool := true
for i: int in int$from_to_by(string$size(num), 1, -1) do
digit: int := int$parse(string$c2s(num[i])) resignal bad_format
even := ~even
if even then
digit := 2 * digit
if digit >= 10 then digit := digit//10 + 1 end
end
total := total + digit
end
return(total // 10 = 0)
end luhn
start_up = proc ()
po: stream := stream$primary_output()
tests: sequence[string] := sequence[string]$
["49927398716", "49927398717", "1234567812345678", "1234567812345670"]
for test: string in sequence[string]$elements(tests) do
stream$puts(po, test || ": ")
if luhn(test)
then stream$putl(po, "pass")
else stream$putl(po, "fail")
end
end
end start_up
- Output:
49927398716: pass 49927398717: fail 1234567812345678: fail 1234567812345670: pass
COBOL
IDENTIFICATION DIVISION.
PROGRAM-ID. LUHNTEST.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
data division.
WORKING-STORAGE SECTION.
01 inp-card.
03 inp-card-ch pic x(01) occurs 20 times.
01 ws-result pic 9(01).
88 pass-luhn-test value 0.
PROCEDURE DIVISION.
move "49927398716" to inp-card
perform test-card
move "49927398717" to inp-card
perform test-card
move "1234567812345678" to inp-card
perform test-card
move "1234567812345670" to inp-card
perform test-card
stop run
.
test-card.
call "LUHN" using inp-card, ws-result
if pass-luhn-test
display "input=" inp-card "pass"
else
display "input=" inp-card "fail"
.
END PROGRAM LUHNTEST.
IDENTIFICATION DIVISION.
PROGRAM-ID. LUHN.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 maxlen pic 9(02) comp value 16.
01 inplen pic 9(02) comp value 0.
01 i pic 9(02) comp value 0.
01 j pic 9(02) comp value 0.
01 l pic 9(02) comp value 0.
01 dw pic 9(02) comp value 0.
01 ws-total pic 9(03) comp value 0.
01 ws-prod pic 99.
01 filler redefines ws-prod.
03 ws-prod-tens pic 9.
03 ws-prod-units pic 9.
01 ws-card.
03 filler occurs 16 times depending on maxlen.
05 ws-card-ch pic x(01).
05 ws-card-digit redefines ws-card-ch pic 9(01).
LINKAGE SECTION.
01 inp-card.
03 inp-card-ch pic x(01) occurs 20 times.
01 ws-result pic 9(01).
88 pass-luhn-test value 0.
PROCEDURE DIVISION using inp-card, ws-result.
perform varying i from 1 by +1
until i > maxlen
or inp-card-ch (i) = space
end-perform
compute l = i - 1
compute inplen = l
perform varying j from 1 by +1
until j > inplen
if l < 1
move "0" to ws-card-ch (j)
else
move inp-card-ch (l) to ws-card-ch (j)
compute l = l - 1
end-if
end-perform
move 0 to ws-total
perform varying i from 1 by +1
until i > inplen
compute dw = 2 - (i - 2 * function integer (i / 2))
compute ws-prod = ws-card-digit (i) * dw
compute ws-total = ws-total
+ ws-prod-tens
+ ws-prod-units
end-perform
compute ws-result = ws-total - 10 * function integer (ws-total / 10)
goback
.
END PROGRAM LUHN.
- Output:
input=49927398716 pass input=49927398717 fail input=1234567812345678 fail input=1234567812345670 pass
Comal
0010 FUNC luhn(s$) CLOSED
0020 total#:=0
0030 even#:=TRUE
0040 FOR i#:=LEN(s$) TO 1 STEP -1 DO
0050 digit#:=VAL(s$(i#))
0060 even#:=NOT even#
0070 IF even# THEN digit#:=(2*digit#) DIV 10+(2*digit#) MOD 10
0080 total#:+digit#
0090 ENDFOR i#
0100 RETURN total# MOD 10=0
0110 ENDFUNC luhn
0120 //
0130 PROC test(s$)
0140 PRINT s$,": ",
0150 IF luhn(s$) THEN
0160 PRINT "pass"
0170 ELSE
0180 PRINT "fail"
0190 ENDIF
0200 ENDPROC test
0210 //
0220 test("49927398716")
0230 test("49927398717")
0240 test("1234567812345678")
0250 test("1234567812345670")
0260 END
- Output:
49927398716: pass 49927398717: fail 1234567812345678: fail 1234567812345670: pass
Common 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))))))
Another version, using Maciej Pasternacki's reader macros for function composition and currying in the curly package:
(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)))
Cowgol
include "cowgol.coh";
# Given a string containing the digits of a credit card number,
# see if it passes the Luhn test.
sub luhn(card: [uint8]): (ok: uint8) is
# Scan ahead to last digit, counting digits
var n: uint8 := 0;
while [card] != 0 loop
n := n + 1;
card := @next card;
end loop;
var sum: uint8 := 0;
while n > 0 loop
# odd digit is simply added
card := @prev card;
n := n - 1;
sum := sum + ([card] - '0');
# if uneven amount of digits, stop
if n == 0 then break; end if;
# even digit
card := @prev card;
n := n - 1;
var digit := [card] - '0';
# it is good to avoid unnecessary multiplication/
# division, since 8-bit processors and microcontrollers
# don't tend to have that in hardware
if digit < 5 then
sum := sum + digit + digit;
else
digit := digit - 5;
sum := sum + digit + digit + 1;
end if;
end loop;
# there is no boolean type, comparisons only work
# in conditionals; this is the only way to return
# a status
if sum % 10 == 0 then
ok := 1;
else
ok := 0;
end if;
end sub;
# Test and print
sub test(card: [uint8]) is
var msg: [uint8][] := {"Fail", "Pass"};
print(card);
print(": ");
print(msg[luhn(card)]);
print_nl();
end sub;
test("49927398716");
test("49927398717");
test("1234567812345678");
test("1234567812345670");
- Output:
49927398716: Pass 49927398717: Fail 1234567812345678: Fail 1234567812345670: Pass
Crystal
def luhn_valid?(n) # Card values can be numbers or strings
d2sum = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
sum, n = 0, n.to_u64
while n > 0; sum += n%10; n //= 10; sum += d2sum[n%10]; n //= 10 end
sum % 10 == 0
end
cards = [49927398716, "49927398717", 1234567812345678, "1234567812345670"]
cards.each{ |i| puts "#{i}: #{luhn_valid?(i)}" }
- Output:
49927398716: true 49927398717: false 1234567812345678: false 1234567812345670: true
D
Functional Version
import std.algorithm, std.range, std.string;
enum luhnTest = (in string n) pure /*nothrow*/ @safe /*@nogc*/ =>
retro(n)
.zip(only(1, 2).cycle)
.map!(p => (p[0] - '0') * p[1])
.map!(d => d / 10 + d % 10)
.sum % 10 == 0;
void main() {
assert("49927398716 49927398717 1234567812345678 1234567812345670"
.split.map!luhnTest.equal([true, false, false, true]));
}
More Imperative Version
import std.algorithm;
bool luhnTest(in string num) @safe pure nothrow @nogc {
uint sum;
foreach_reverse (immutable i, immutable n; num) {
immutable uint ord = n - '\u0030';
sum += ((num.length - i) & 1) ? ord : ord / 5 + (2 * ord) % 10;
}
return sum % 10 == 0;
}
void main() {
immutable data = ["49927398716",
"49927398717",
"1234567812345678",
"1234567812345670"];
assert(data.map!luhnTest.equal([true, false, false, true]));
}
Stronger Statically Typed Version
This version uses more precise types.
import std.stdio;
struct Interval(T) {
immutable T a, b;
this(in T a_, in T b_) pure nothrow @nogc {
this.a = a_;
this.b = b_;
}
bool opBinaryRight(string op="in")(in T x)
const pure nothrow @nogc {
return x >= a && x <= b;
}
pure nothrow @safe @nogc const invariant {
assert(a <= b);
}
}
Interval!T interval(T)(in T a, in T b) pure nothrow @nogc {
return Interval!T(a, b);
}
bool luhnTest(in string num) pure nothrow @nogc
in {
assert(num.length <= 20);
} body {
int sum = 0;
bool od = true;
bool ok = true;
immutable int numLen = num.length;
foreach_reverse (immutable 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 (immutable n; ["49927398716", "49927398717",
"1234567812345678", "1234567812345670",
"123456781234567D"])
writefln("%s is %svalid", n, luhnTest(n) ? "" : "not ");
}
Delphi
Uses an array to handle the situation where multiply a term by 2, creates a two digit number. The array automatically handles adding each digit of the resulting two digit number. Also, avoids reversing the number by traversing the array backwards
{Test data arrays}
const Num1: array [0..10] of byte = (4,9,9,2,7,3,9,8,7,1,6);
const Num2: array [0..10] of byte = (4,9,9,2,7,3,9,8,7,1,7);
const Num3: array [0..15] of byte = (1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8);
const Num4: array [0..15] of byte = (1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,0);
{Simplifies cases where we have to sum a two digit number}
const DigitSum: array [0..18] of byte = (0,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9);
function ValidateCreditCard(CardNum: array of byte): boolean;
{Validate a Credit Card number}
var I,J,Len,Sum,Sum1,Sum2: integer;
var Rev: array of byte;
begin
Sum1:=0; Sum2:=0;
Len:=High(CardNum);
for I:=Len downto 0 do
if ((I-Len) and 1)=0 then Sum1:=Sum1 + CardNum[I]
else Sum2:=Sum2 + DigitSum[CardNum[I]*2];
Sum:=Sum1+Sum2;
Result:=(Sum mod 10)=0;
end;
function CardNumberToStr(CardNum: array of byte): string;
{Convert card number to a string}
var I: integer;
begin
Result:='';
for I:=0 to High(CardNum) do
Result:=Result+IntToStr(CardNum[I]);
end;
procedure TestDisplayNumber(Memo: TMemo; Num: array of byte);
{Test a credit card number and display results}
var S: string;
begin
S:=CardNumberToStr(Num);
if ValidateCreditCard(Num) then S:=S+': Valid'
else S:=S+': Not Valid';
Memo.Lines.Add(S);
end;
procedure TestCreditCardNums(Memo: TMemo);
{Test all credit card numbers}
begin
TestDisplayNumber(Memo,Num1);
TestDisplayNumber(Memo,Num2);
TestDisplayNumber(Memo,Num3);
TestDisplayNumber(Memo,Num4);
end;
- Output:
49927398716: Valid 49927398717: Not Valid 1234567812345678: Not Valid 1234567812345670: Valid
Draco
proc nonrec luhn(*char num) bool:
[10] byte map = (0, 2, 4, 6, 8, 1, 3, 5, 7, 9);
byte total, digit;
*char start;
bool even;
start := num;
total := 0;
even := true;
while num* /= '\e' do num := num + 1 od;
while
num := num - 1;
num >= start
do
digit := num* - '0';
even := not even;
if even then digit := map[digit] fi;
total := total + digit
od;
total % 10 = 0
corp
proc nonrec test(*char num) void:
writeln(num, ": ", if luhn(num) then "pass" else "fail" fi)
corp
proc nonrec main() void:
test("49927398716");
test("49927398717");
test("1234567812345678");
test("1234567812345670")
corp
- Output:
49927398716: pass 49927398717: fail 1234567812345678: fail 1234567812345670: pass
EasyLang
func luhn cc$ .
for i = len cc$ downto 1
odd = 1 - odd
dig = number substr cc$ i 1
if odd = 0
dig = 2 * dig
if dig >= 10
dig -= 9
.
.
sum += dig
.
return if sum mod 10 = 0
.
cc$[] = [ "49927398716" "49927398717" "1234567812345678" "1234567812345670" ]
for cc$ in cc$[]
write cc$ & " "
if luhn cc$ = 1
print "is valid"
else
print "is not valid"
.
.
- Output:
49927398716 is valid 49927398717 is not valid 1234567812345678 is not valid 1234567812345670 is valid
EchoLisp
;; value for 'even' numbers
(define (even-val n) (if (> n 4) (+ n n -9) (+ n n)))
;;Luhn test
;; input : a string of decimal digits
;; output #t or #f
(define (valid nums (odd #f ))
(let ((nums (map string->number (reverse (string->list nums)))))
(= 0 (modulo
(for/sum ((n nums)) (set! odd (not odd)) (if odd n (even-val n)))
10))))
(valid "49927398716") → #t
(valid "1234567812345670") → #t
(valid "1234567812345678") → #f
(valid "49927398717") → #f
Elixir
defmodule Luhn do
def valid?(cc) when is_binary(cc), do: String.to_integer(cc) |> valid?
def valid?(cc) when is_integer(cc) do
0 == Integer.digits(cc)
|> Enum.reverse
|> Enum.chunk(2, 2, [0])
|> Enum.reduce(0, fn([odd, even], sum) -> Enum.sum([sum, odd | Integer.digits(even*2)]) end)
|> rem(10)
end
end
numbers = ~w(49927398716 49927398717 1234567812345678 1234567812345670)
for n <- numbers, do: IO.puts "#{n}: #{Luhn.valid?(n)}"
- Output:
49927398716: true 49927398717: false 1234567812345678: false 1234567812345670: true
Emacs Lisp
(require 'seq)
(defun luhn (str)
"Check if STR is a valid credit card number using the Luhn algorithm."
(if (string-match-p "[^0-9]" str)
(error "String contains invalid character")
(let ((digit-list (reverse (mapcar #'(lambda (x) (- x 48))
(string-to-list str)))))
(zerop
(mod (apply #'+ (seq-map-indexed
(lambda (elt idx)
(if (not (zerop (% idx 2)))
(if (> (* 2 elt) 9)
(- (* 2 elt) 9)
(* 2 elt))
elt))
digit-list))
10)))))
(mapcar #'luhn '("49927398716" "49927398717" "1234567812345678" "1234567812345670"))
- Output:
(t nil nil t)
Erlang
-module(luhn_test).
-export( [credit_card/1, task/0] ).
luhn_sum([Odd, Even |Rest]) when Even >= 5 ->
Odd + 2 * Even - 10 + 1 + luhn_sum(Rest);
luhn_sum([Odd, Even |Rest]) ->
Odd + 2 * Even + luhn_sum(Rest);
luhn_sum([Odd]) ->
Odd;
luhn_sum([]) ->
0.
check( Sum ) when (Sum rem 10) =:= 0 -> valid;
check( _Sum ) -> invalid.
credit_card(Digits) ->
check(luhn_sum(lists:map(fun(D) -> D-$0 end, lists:reverse(Digits)))).
task() ->
Numbers = ["49927398716", "49927398717", "1234567812345678", "1234567812345670"],
[io:fwrite("~s: ~p~n", [X, credit_card(X)]) || X <- Numbers].
- Output:
16> luhn_test:task(). 49927398716: valid 49927398717: invalid 1234567812345678: invalid 1234567812345670: valid
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
- Output:
49927398716 = 1 49927398717 = 0 1234567812345678 = 0 1234567812345670 = 1
Excel
LAMBDA
Binding the name luhnChecked to the following lambda expression in the Name Manager of the Excel WorkBook:
(See LAMBDA: The ultimate Excel worksheet function)
luhnChecked
=LAMBDA(s,
LET(
ns, REVERSECOLS(VALUE(CHARSROW(s))),
ixs, SEQUENCE(1, COLUMNS(ns), 1, 1),
0 = MOD(SUM(
FILTER(ns, 0 <> MOD(ixs, 2))
) + (
LAMBDA(n,
DIGITSUM(
CONCAT(TEXT(2 * n, "0"))
)
)(
FILTER(ns, 0 = MOD(ixs, 2))
)
),
10
)
)
)
and also assuming the following generic bindings in the Name Manager for the WorkBook:
CHARSROW
=LAMBDA(s,
MID(s,
SEQUENCE(1, LEN(s), 1, 1),
1
)
)
DIGITSUM
=LAMBDA(s,
SUM(VALUE(
MID(s,
SEQUENCE(LEN(s), 1, 1, 1),
1
)
))
)
REVERSECOLS
=LAMBDA(xs,
LET(
n, COLUMNS(xs),
SORTBY(
xs,
SEQUENCE(1, n, n, -1)
)
)
)
- Output:
fx | =luhnChecked(A2) | ||
---|---|---|---|
A | B | ||
1 | Digit strings | Luhn result | |
2 | 49927398716 | TRUE | |
3 | 49927398717 | FALSE | |
4 | 1234567812345678 | FALSE | |
5 | 1234567812345670 | TRUE |
F#
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
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 = ;
- Output:
( scratchpad ) 49927398716 luhn? . t ( scratchpad ) 49927398717 luhn? . f ( scratchpad ) 1234567812345678 luhn? . f ( scratchpad ) 1234567812345670 luhn? . t
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
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
FreeBASIC
' version 05-07-2015
' compile with: fbc -s console
#Ifndef TRUE ' define true and false for older freebasic versions
#Define FALSE 0
#Define TRUE Not FALSE
#EndIf
Function luhntest(cardnr As String) As Integer
cardnr = Trim(cardnr) ' we don't want spaces
Dim As String reverse_nr = cardnr
Dim As Integer i, j, s1, s2, l = Len(cardnr) - 1
' reverse string
For i = 0 To l
reverse_nr[i] = cardnr[l - i]
Next
' sum odd numbers
For i = 0 To l Step 2
s1 = s1 + (reverse_nr[i] - Asc("0"))
Next
' sum even numbers
For i = 1 To l Step 2
j = reverse_nr[i] - Asc("0")
j = j * 2
If j > 9 Then j = j Mod 10 + 1
s2 = s2 + j
Next
If (s1 + s2) Mod 10 = 0 Then
Return TRUE
Else
Return FALSE
End If
End Function
' ------=< MAIN >=------
Dim As String input_nr(1 To ...) = {"49927398716", "49927398717",_
"1234567812345678", "1234567812345670"}
Dim As Integer a
Print "Task test number 49927398716 should be TRUE, report back as ";
Print IIf(luhntest("49927398716" ) = TRUE, "TRUE", "FALSE")
Print : Print
Print "test card nr:"
For a = 1 To UBound(input_nr)
Print input_nr(a); " = "; IIf(luhntest(input_nr(a)) = TRUE, "TRUE", "FALSE")
Next
' empty keyboard buffer
While InKey <> "" : Wend
Print : Print "hit any key to end program"
Sleep
End
- Output:
Task test number 49927398716 should be TRUE, report back as TRUE test card nr: 49927398716 = TRUE 49927398717 = FALSE 1234567812345678 = FALSE 1234567812345670 = TRUE
Free Pascal
see also: 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.
- Output:
49927398716: TRUE 49927398717: FALSE 1234567812345678: FALSE 1234567812345670: TRUE
FunL
def luhn_checksum( card_number ) =
def digits_of( n ) = [int(d) | d <- n.toString()]
digits = digits_of( card_number ).reverse()
odd_digits = digits(0:digits.length():2)
even_digits = digits(1:digits.length():2)
(sum( odd_digits ) + sum( sum(digits_of(d*2)) | d <- even_digits )) mod 10
def is_luhn_valid( card_number ) = luhn_checksum( card_number ) == 0
for n <- [49927398716, 49927398717, 1234567812345678, 1234567812345670]
println( n + ' is ' + (if is_luhn_valid(n) then 'valid' else 'invalid') )
- Output:
49927398716 is valid 49927398717 is invalid 1234567812345678 is invalid 1234567812345670 is valid
FutureBasic
include "NSLog.incl"
local fn LuhnCheck( cardStr as CFStringRef ) as BOOL
NSInteger i, j, count, s1 = 0, s2 = 0
BOOL result = NO
// Build array of individual numbers in credit card string
NSUInteger strLength = len(cardStr)
CFMUtableArrayRef mutArr = fn MutableArrayWithCapacity(strLength)
for i = 0 to strLength - 1
CFStringRef tempStr = fn StringWithFormat( @"%C", fn StringCharacterAtIndex( cardStr, i ) )
MutableArrayInsertObjectAtIndex( mutArr, tempStr, i )
next
// Reverse the number array
CFArrayRef reversedArray = fn EnumeratorAllObjects( fn ArrayReverseObjectEnumerator( mutArr ) )
// Get number of array elements
count = len(reversedArray)
// Handle odd numbers
for i = 0 to count - 1 step 2
s1 = s1 + fn StringIntegerValue( reversedArray[i] )
next
// Hnadle even numbers
for i = 1 to count - 1 step 2
j = fn StringIntegerValue( reversedArray[i] )
j = j * 2
if j > 9 then j = j mod 10 + 1
s2 = s2 + j
next
if (s1 + s2) mod 10 = 0 then result = YES else result = NO
end fn = result
NSLogClear
if fn LuhnCheck( @"49927398716" ) then NSLog (@"%@ is valid.", @"49927398716" ) else NSLog (@"%@ is not valid.", @"49927398716" )
if fn LuhnCheck( @"49927398717" ) then NSLog (@"%@ is valid.", @"49927398717" ) else NSLog (@"%@ is not valid.", @"49927398717" )
if fn LuhnCheck( @"1234567812345678" ) then NSLog (@"%@ is valid.", @"1234567812345678" ) else NSLog (@"%@ is not valid.", @"1234567812345678" )
if fn LuhnCheck( @"1234567812345670" ) then NSLog (@"%@ is valid.", @"1234567812345670" ) else NSLog (@"%@ is not valid.", @"1234567812345670" )
HandleEvents
- Output:
49927398716 is valid. 49927398717 is not valid. 1234567812345678 is not valid. 1234567812345670 is valid.
Gambas
Click this link to run this code
Public Sub Main()
Dim sTrial As String[] = ["49927398716", "49927398717", "1234567812345678", "1234567812345670"]
Dim sRev As String
Dim siCount, siOdd, siEven, siHold, siQty As Short
For siQty = 0 To sTrial.Max
siOdd = 0
siEven = 0
sRev = ""
For siCount = Len(sTrial[siQty]) DownTo 1
sRev &= Mid(sTrial[siQty], siCount, 1)
Next
For siCount = 1 To Len(sRev)
If Odd(siCount) Then siOdd += Val(Mid(sRev, siCount, 1))
If Even(siCount) Then
siHold = Val(Mid(sRev, siCount, 1)) * 2
If siHold > 9 Then
siEven += Val(Mid(Str(siHold), 1, 1)) + Val(Mid(Str(siHold), 2, 1))
Else
siEven += Val(Mid(sRev, siCount, 1)) * 2
End If
End If
Next
sRev = Str(siOdd + siEven)
If sRev Ends "0" Then
Print sTrial[siQty] & " is a valid number"
Else
Print sTrial[siQty] & " is NOT a valid number"
End If
Next
End
Output:
49927398716 is a valid number 49927398717 is NOT a valid number 1234567812345678 is NOT a valid number 1234567812345670 is a valid number
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);
# [ true, false, false, true ]
# Will also work on strings, and will skip non-digits
IsLuhn("4-992-739-871-6");
# true
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))
}
}
- Output:
49927398716 true 49927398717 false 1234567812345678 false 1234567812345670 true
Groovy
def checkLuhn(number) {
int total
(number as String).reverse().eachWithIndex { ch, index ->
def digit = Integer.parseInt(ch)
total += (index % 2 ==0) ? digit : [0, 2, 4, 6, 8, 1, 3, 5, 7, 9][digit]
}
total % 10 == 0
}
Testing the function:
def verifyLuhn(number, expected) {
println "Checking: $number (${checkLuhn(number)})"
assert expected == checkLuhn(number)
}
[49927398716: true, 49927398717: false, 1234567812345678: false, 1234567812345670: true].each { number, expected ->
verifyLuhn number, expected
}
- Output:
Checking: 49927398716 (true) Checking: 49927398717 (false) Checking: 1234567812345678 (false) Checking: 1234567812345670 (true)
Haskell
import Data.Char (digitToInt)
luhn = (0 ==) . (`mod` 10) . sum . map (uncurry (+) . (`divMod` 10)) .
zipWith (*) (cycle [1,2]) . map digitToInt . reverse
- Output:
map luhn ["49927398716", "49927398717", "1234567812345678", "1234567812345670"]
[True,False,False,True]
Or, aiming for a legible relationship with the stages shown in the task description:
import Data.Char (digitToInt)
import Data.List (transpose)
import Data.List.Split (chunksOf)
luhn :: String -> Bool
luhn x = 0 == rem (s1 + s2) 10
where
stringInts = fmap digitToInt
[odds, evens] =
(transpose . chunksOf 2)
(stringInts $ reverse x)
s1 = sum odds
s2 = sum $ sum . stringInts . show . (2 *) <$> evens
main :: IO ()
main =
mapM_
(print . ((,) <*> luhn))
[ "49927398716",
"49927398717",
"1234567812345678",
"1234567812345670"
]
- Output:
("49927398716",True) ("49927398717",False) ("1234567812345678",False) ("1234567812345670",True)
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
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.
- 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.
luhn=: 0 = 10 (| +/@,) 10 #.inv 1 2 *&|: _2 "."0\ |.
Example use:
luhn&> '49927398716';'49927398717';'1234567812345678';'1234567812345670'
1 0 0 1
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
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;
}
}
- Output:
true false false true
Java Long type version
public class Luhn {
public static void main(String[] args) {
System.out.println(luhnTest(49927398716L));
System.out.println(luhnTest(499273987163L));
System.out.println(luhnTest(1234567L));
System.out.println(luhnTest(0L));
}
public static boolean luhnTest(Long digits) {
int s1 = 0, s2 = 0;
//Use an alternator for separate odd/even processing
boolean alternator = true;
//Confine digit numbers to 8 - 19 per ISO
if (digits < 1e7 || digits >= 1e19) return false;
for ( int i = 0; digits > 0; ++i) {
Long oneDigit = digits % 10;
if (alternator) {
s1 += oneDigit.intValue();
} else {
oneDigit *= 2;
s2 += oneDigit > 9 ? oneDigit.intValue() - 9: oneDigit.intValue();
}
digits /= 10;
alternator = !alternator;
}
return (s1 + s2) % 10 == 0 ? true : false;
}
}
- Output:
true false false false
JavaScript
Using prototype.
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))});
Without any library.
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);
}
})();
Highly compressed version.
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')
Naive implementation
const lunhCheck = (str) => {
const sumDigit = (c) => (c < 10) ? c :
sumDigit( Math.trunc(c / 10) + (c % 10));
return str.split('').reverse()
.map(Number)
.map((c, i) => i % 2 !== 0 ? sumDigit(c * 2) : c)
.reduce((acc,v) => acc + v) % 10 === 0;
};
lunhCheck('49927398716'); // returns true
lunhCheck('49927398717'); // returns false
lunhCheck('1234567812345678'); // returns false
lunhCheck('1234567812345670'); // returns true
jq
Works with gojq, the Go implementation of jq
For the specific task defined here, both jq (version 1.4 or later) and gojq should suffice, but for very large integers (greater than 2^53), either gojq or a version of the C implementation of jq with support for very large external integers would be required.
def luhn:
def odds: . as $in | reduce range(0; length) as $i
([]; if ($i % 2) == 0 then . + [$in[$i]] else . end);
def evens: . as $in | reduce range(1; length) as $i
([]; if ($i % 2) == 1 then . + [$in[$i]] else . end);
def digits: map([.]|implode|tonumber);
def sumdigits: tostring | explode | digits | add;
(tostring | explode | reverse ) as $reverse
| ($reverse | odds | digits | add) as $s1
| ($reverse | evens | digits | map(. * 2 | sumdigits) | add) as $s2
| 0 == ($s1 + $s2) % 10 ;
Example
( 49927398716,
49927398717,
1234567812345678,
1234567812345670
) | "\(.) => \(luhn)";
- Output:
$ jq -r -M -n -f luhn.jq 49927398716 => true 49927398717 => false 1234567812345678 => false 1234567812345670 => true
Julia
The test function itself is only a single line of code:
luhntest(x::Integer) = (sum(digits(x)[1:2:end]) + sum(map(x -> sum(digits(x)), 2 * digits(x)[2:2:end]))) % 10 == 0
More readable version:
function luhntest(x::Integer)
d = reverse(digits(x))
s = sum(d[1:2:end])
s += sum(sum.(digits.(2d[2:2:end])))
return s % 10 == 0
end
for card in [49927398716, 49927398717, 1234567812345678, 1234567812345670]
println(luhntest(card) ? "PASS " : "FAIL ", card)
end
- Output:
PASS 49927398716 FAIL 49927398717 FAIL 1234567812345678 PASS 1234567812345670
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}
luhn2: {~(+/,//10_vs'1 2*+-1 2#((#n)!2){x,0}/n:0$'|$x)
Example:
luhn'49927398716 49927398717 1234567812345678 1234567812345670
1 0 0 1
luhn2'49927398716 49927398717 1234567812345678 1234567812345670
1 0 0 1
luhn:{[cc]
digits:0$/:|cc / convert chars to digit ints
s:digits*(#cc)#1 2 / evens doubled, odds not
nines:+/s>9 / number of sums greater than 9
:~((+/s)-(9*nines))!10 / sum minus the nines is mod ten?
}
Example:
test:("49927398716";"49927398717";"1234567812345678";"1234567812345670")
luhn'test
1 0 0 1
Kotlin
// version 1.0
fun checkLuhn(number: String): Boolean {
var isOdd = true
var sum = 0
for (index in number.indices.reversed()) {
val digit = number[index] - '0'
sum += if (isOdd) digit else (digit * 2).let { (it / 10) + (it % 10) }
isOdd = !isOdd
}
return (sum % 10) == 0
}
fun main(args: Array<String>) {
val numbers = arrayOf("49927398716", "49927398717", "1234567812345678", "1234567812345670")
for (number in numbers)
println("${number.padEnd(16)} is ${if(checkLuhn(number)) "valid" else "invalid"}")
}
- Output:
49927398716 is valid 49927398717 is invalid 1234567812345678 is invalid 1234567812345670 is valid
langur
val luhntest = fn(s) {
val t = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
val numbers = s -> s2n
val oddeven = len(numbers) rem 2
for[=0] i of numbers {
_for += if(i rem 2 == oddeven: numbers[i]; t[numbers[i]+1])
} div 10
}
val tests = {
"49927398716": true,
"49927398717": false,
"1234567812345678": false,
"1234567812345670": true,
}
for key in sort(keys(tests)) {
val pass = luhntest(key)
write key, ": ", pass
writeln if(pass == tests[key]: ""; " (LUHN TEST FAILED)")
}
- Output:
49927398717: false 49927398716: true 1234567812345678: false 1234567812345670: true
Lasso
Part of the Lasso's implementation of "valid_creditcard".
#!/usr/bin/lasso9
define luhn_check(number) => {
local(
rev = #number->asString,
checksum = 0
)
#rev->reverse
iterate(#rev, local(digit)) => {
if((loop_count % 2) == 0) => {
#checksum += (2 * integer(#digit))
integer(#digit) >= 5 ? #checksum -= 9
else
#checksum += integer(#digit)
}
}
(#checksum % 10) != 0 ? return false
return true
}
stdoutnl(luhn_check(49927398716)) // true
stdoutnl(luhn_check(49927398717)) // false
stdoutnl(luhn_check(1234567812345678)) // false
stdoutnl(luhn_check(1234567812345670)) // true
Liberty BASIC
' [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
LiveCode
function LuhnTest cc
local s1,evens, s2
repeat with n = 1 to len(cc)
if n mod 2 is not 0 then
add (char -n of cc) to s1
else
put (char -n of cc) * 2 into evens
if evens > 9 then subtract 9 from evens
add evens to s2
end if
end repeat
return the last char of (s1 + s2) is 0
end LuhnTest
-- test
repeat for each item ccno in "49927398716,49927398717,1234567812345678,1234567812345670"
put ccno && LuhnTest(ccno) & cr after luhncheck
end repeat
put luhncheck
49927398716 true
49927398717 false
1234567812345678 false
1234567812345670 true
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
Lua
function luhn_test(cc_number)
assert(type(cc_number) == 'string')
local sum = 0
local is_odd = true
for idx = #cc_number , 1 , -1 do -- reverse order
-- extract single character as string then convert to integer
local digit = cc_number:sub(idx, idx) + 0
if is_odd then
sum = sum + digit
else
sum = sum + ((digit * 2) % 10) + (digit // 5)
end -- if
is_odd = not is_odd -- toggle between odd and even
end -- for
return (sum % 10) == 0
end -- function luhn_test
for _, val in ipairs{"49927398716", "49927398717", "1234567812345678", "1234567812345670"} do
print(val, luhn_test(val))
end -- for
M2000 Interpreter
Module Checkit {
Function luhntest(cardnr$) {
cardnr$ = Trim$(cardnr$) ' we don't want spaces
if len(cardnr$)=0 then exit
Dim Base 0, reverse_nr$(Len(cardnr$))
Def integer i, j, s1, s2, l , l2
Let l=Len(cardnr$)-1, l2=l+1
' reverse string
For i = 0 To l
reverse_nr$(i) = mid$(cardnr$,l2-i,1)
Next i
' sum odd numbers
For i = 0 To l Step 2
s1 = s1 + (Asc(reverse_nr$(i)) - Asc("0"))
Next i
' sum even numbers
For i = 1 To l Step 2
j = Asc(reverse_nr$(i)) - Asc("0")
j = j * 2
If j > 9 Then j = j Mod 10 + 1
s2 = s2 + j
Next i
If (s1 + s2) Mod 10 = 0 Then
= 1=1
Else
= 1=0
End If
}
Flush
Data "49927398716", "49927398717", "1234567812345678", "1234567812345670"
while not empty
over
print letter$;" = ";luhntest(letter$)
end while
}
Checkit
- Output:
49927398716 = True 49927398717 = False 1234567812345678 = False 1234567812345670 = True
MACRO-11
.TITLE LUHN
.MCALL .GTLIN,.PRINT,.EXIT
LUHN:: .GTLIN #5$
MOV #5$,R0
TSTB (R0)
BEQ 2$
JSR PC,TEST
BNE 1$
.PRINT #3$
BR LUHN
1$: .PRINT #4$
BR LUHN
2$: .EXIT
3$: .ASCIZ /PASS/
4$: .ASCIZ /FAIL/
5$: .BLKB 200
.EVEN
TEST: MOV R0,R1
CLR R2
1$: TSTB (R0)+
BNE 1$
DEC R0
2$: MOVB -(R0),R3
SUB #60,R3
ADD R3,R2
CMP R0,R1
BLOS 3$
MOVB -(R0),R3
SUB #60,R3
MOVB 4$(R3),R3
ADD R3,R2
CMP R0,R1
BHI 2$
3$: SUB #12,R2
BHI 3$
RTS PC
4$: .BYTE ^D0,^D2,^D4,^D6,^D8
.BYTE ^D1,^D3,^D5,^D7,^D9
.END LUHN
- Output:
.LUHN 49927398716 PASS .LUHN 49927398717 FAIL .LUHN 1234567812345678 FAIL .LUHN 1234567812345670 PASS
Mathematica /Wolfram Language
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}
Alternate Code
Eliminates conversion of numbers to strings and back
LuhnQ[n_Integer] :=
Block[{digits = Reverse@IntegerDigits@n},
Mod[Total[{digits[[;; ;; 2]],
IntegerDigits[2 #] & /@ digits[[2 ;; ;; 2]]}, -1], 10] == 0]
LuhnQ /@ {49927398716, 49927398717, 1234567812345678, 1234567812345670}
- Output:
{True,False,False,True}
MATLAB
The solution is basically the same as for Octave.
function passed = luhn(num)
if nargin == 0 % evaluate test cases
testnum = [49927398716 49927398717 1234567812345678 1234567812345670];
for num = testnum
disp([int2str(num) ': ' int2str(luhn(num))])
end
return
end
% luhn function starts here
d = int2str(num) - '0'; % convert number into vector of digits
m = [2:2:8,1:2:9]; % rule 3: maps 1:9 to [2 4 6 8 1 3 5 7 9]
passed = ~mod(sum(d(end:-2:1)) + sum(m(d(end-1:-2:1))), 10);
end
- Output:
49927398716: 1 49927398717: 0 1234567812345678: 0 1234567812345670: 1
min
((dup 10 <) 'quote (((10 mod) (10 div)) cleave) 'cons linrec) :digits
((0 0) dip (pop 'succ dip over even?) partition ((pop pop) dip) dip) :evens/odds
((2 * digits sum) map sum) :evens-sum
(digits evens/odds ('evens-sum 'sum) => spread + 10 mod 0 ==) :luhn?
(49927398716 49927398717 1234567812345678 1234567812345670)
(dup print! " " print! luhn? puts!) foreach
- Output:
49927398716 true 49927398717 false 1234567812345678 false 1234567812345670 true
MiniScript
isValid = function(s)
sum = 0
odd = true
for i in range(s.len-1)
d = val(s[i])
sum = sum + d * (2 - odd)
if not odd and d > 4 then sum = sum - 9
odd = not odd
end for
return sum % 10 == 0
end function
test = function(s)
if isValid(s) then print s + ": valid" else print s + ": invalid"
end function
test "49927398716"
test "49927398717"
test "1234567812345678"
test "1234567812345670"
- Output:
49927398716: valid 49927398717: invalid 1234567812345678: invalid 1234567812345670: valid
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)
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
Nanoquery
def reverse_str(string)
to_return = ""
for i in range(len(string) - 1, 0, -1)
to_return += string[i]
end for
return to_return
end reverse_str
def luhnTest(number)
s1 = 0; s2 = 0
reversed = reverse_str(number)
for i in range(0, len(reversed) - 1)
digit = int(reversed[i])
if (i % 2) = 0
s1 += digit
else
s2 += 2 * digit
if digit >= 5
s2 -= 9
end if
end if
end for
return ((s1 + s2) % 10) = 0
end luhnTest
println luhnTest("49927398716")
println luhnTest("49927398717")
println luhnTest("1234567812345678")
println luhnTest("1234567812345670")
- Output:
true false false true
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
Nim
proc luhn(cc: string): bool =
const m = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
var sum = 0
var odd = true
for i in countdown(cc.high, 0):
let digit = ord(cc[i]) - ord('0')
sum += (if odd: digit else: m[digit])
odd = not odd
result = sum mod 10 == 0
for cc in ["49927398716", "49927398717", "1234567812345678", "1234567812345670"]:
echo cc, ' ', luhn(cc)
- Output:
49927398716 true 49927398717 false 1234567812345678 false 1234567812345670 true
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();
}
}
}
Objective-C
- (NSArray *) 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;
}
+ (BOOL) luhnCheck:(NSString *)stringToTest {
NSArray *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[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
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
;;
- Output:
# List.map luhn [ "49927398716"; "49927398717"; "1234567812345678"; "1234567812345670" ];;
- : bool list = [true; false; false; true]
Octave
function y = isluhn(s);
if isnumeric(s) s = mat2str(s); end; % make sure s is a string
d = s-'0'; % convert string into vector of digits
m = [2:2:8,1:2:9]; % rule 3: maps [1:9] -> i
y = ~mod(sum(d(end:-2:1)) + sum(m(d(end-1:-2:1))),10);
end;
- Output:
isluhn('49927398716')
ans = 1
isluhn('49927398717')
ans = 0
isluhn('1234567812345678')
ans = 0
isluhn('1234567812345670')
ans = 1
Oforth
: luhnTest(n)
| s i |
n asString reverse ->s
0 s size loop: i [
i s at asDigit
i isEven ifTrue: [ 2 * dup 10 >= ifTrue: [ 9 - ] ] +
]
10 mod ==0 ;
- Output:
[ 49927398716, 49927398717, 1234567812345678, 1234567812345670 ] map(#luhnTest) println [1, 0, 0, 1]
OpenEdge/Progress
FUNCTION fnLuhnAlgorithm RETURNS LOGICAL
(INPUT pcNumber AS CHARACTER):
/*------------------------------------------------------------------------------
Purpose: Applies Luhn Algorithm to check a Number
Notes: Returns True/False Validation based on check digit
------------------------------------------------------------------------------*/
DEFINE VARIABLE cNum AS CHARACTER NO-UNDO.
DEFINE VARIABLE iCheck AS INTEGER NO-UNDO.
DEFINE VARIABLE iLength AS INTEGER NO-UNDO.
DEFINE VARIABLE iLoopCnt AS INTEGER NO-UNDO.
DEFINE VARIABLE iNum AS INTEGER NO-UNDO.
DEFINE VARIABLE iNum1 AS INTEGER NO-UNDO.
DEFINE VARIABLE iNum2 AS INTEGER NO-UNDO.
DEFINE VARIABLE iTestLength AS INTEGER NO-UNDO.
ASSIGN
iLength = LENGTH(pcNumber)
iTestLength = iLength - 1
iCheck = 1. /* 1 for the check digit we skip */
DO iLoopCnt = iTestLength TO 1 BY -1:
ASSIGN
iNum = INTEGER(SUBSTR(pcNumber,iLoopCnt,1))
iCheck = iCheck + 1.
IF iCheck MODULO 2 = 1 THEN
ASSIGN iNum1 = iNum1 + iNum.
ELSE
DO:
ASSIGN iNum2 = iNum * 2.
IF iNum2 < 10 THEN
ASSIGN iNum1 = iNum1 + iNum2.
ELSE
ASSIGN
cNum = STRING(iNum2)
iNum1 = iNum1 + INTEGER(SUBSTR(cNum,1,1)) + INTEGER(SUBSTR(cNum,2,1)).
END.
END.
ASSIGN
iNum2 = iNum1 * 9
iNum = iNum2 MODULO 10.
IF iNum = INTEGER(SUBSTR(pcNumber,iLength,1)) THEN
RETURN TRUE.
ELSE
RETURN FALSE.
END FUNCTION. /* fnLuhnAlgorithm */
- Output:
49927398716 - yes 49927398717 - no 1234567812345678 - no 1234567812345670 - yes
Order
This example highlights Order's unusual treatment of numbers.
Numbers larger than 100 are not recognized by the interpreter in literal form and must instead be entered as "native" numbers (i.e. listing the separate digits as arguments to 8nat
).
Since internally, a native number is just a sequence of the digits in reverse order with an end digit marker, converting the number into a reversed list of digits mainly involves removing this terminator, so that we can immediately treat the digits as number elements.
#include <order/interpreter.h>
#define ORDER_PP_DEF_8luhn ORDER_PP_FN( \
8fn(8N, 8if(8is_seq(8N), 8luhn_wk(8num_to_seq(8N)), 8false)) )
#define ORDER_PP_DEF_8num_to_seq ORDER_PP_FN( \
8fn(8N, 8seq_push_back(8seq(8seq_last(8N)), 8seq_pop_back(8N))) )
#define ORDER_PP_DEF_8luhn_wk ORDER_PP_FN( \
8fn(8N, \
8lets((8P, 8unzip(8N, 8nil, 8nil, 8true)) \
(8O, 8seq_fold(8plus, 0, 8tuple_at_0(8P))) \
(8E, 8seq_fold(8plus, 0, \
8seq_map(8dig_map, 8tuple_at_1(8P)))), \
8is_0(8remainder(8plus(8O, 8E), 10)))) )
#define ORDER_PP_DEF_8dig_map ORDER_PP_FN( \
8fn(8X, 8tuple_at(8X, 8tuple(0,2,4,6,8,1,3,5,7,9))) )
#define ORDER_PP_DEF_8unzip ORDER_PP_FN( \
8fn(8S, 8L, 8R, 8O, \
8if(8is_nil(8S), \
8pair(8L, 8R), \
8if(8O, \
8unzip(8seq_tail(8S), 8seq_push_back(8seq_head(8S), 8L), \
8R, 8false), \
8unzip(8seq_tail(8S), 8L, \
8seq_push_back(8seq_head(8S), 8R), 8true)))) )
ORDER_PP(8seq_to_tuple(
8seq_map(8luhn, 8seq(8nat(4,9,9,2,7,3,9,8,7,1,6),
8nat(4,9,9,2,7,3,9,8,7,1,7),
8nat(1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8),
8nat(1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,0)))
))
- Output:
(8true,8false,8false,8true)
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}}
Pascal
see also: Free Pascal
This implementation skips the reversal step and simply reads the input backwards.
program luhnTestOfCreditCardNumbers(input, output);
type
{
`string(…)` is an Extended Pascal, ISO 10206, extension.
`string(64)` discriminates the “schema” data type `string`
to contain at most 64 `char` values.
}
creditCardNumber = string(64);
{
\brief determines whether a string contains digits only
\param sample the string to inspect
\return `false` iff `sample` contains non-digit characters
}
{ Extended Pascal: `protected` means the function cannot modify `sample` }
function containsDigitsOnly(protected sample: creditCardNumber): Boolean;
var
{ EP: the `… value []` initializes this variable as an empty set }
characters: set of char value [];
{ `sample.capacity` refers to `64` in this code. }
i: 1..sample.capacity;
begin
for i := 1 to length(sample) do
begin
{ Union of sets indicated by `+`. }
characters := characters + [sample[i]]
end;
{
In a Pascal `function` definition,
there must be one assignment to the (implicit) variable
bearing the same name as of the function.
This will be the return value.
}
{ NB: This will return `true` even if `length(sample)` is zero. }
containsDigitsOnly := card(characters - ['0'..'9']) = 0
{ `card` is an Extended Pascal extension. }
end;
{
\brief determines whether a string complies with ISO/IEC 7812-1 Luhn test
\param sample the potentially correct credit card number
\return `true` if verification succeeds
}
function luhnCheck(protected sample: creditCardNumber): Boolean;
{
This _nested_ function is only accessible _within_ `luhnCheck`.
Outsourcing this code allows us to write a neat expression below.
}
function check: Boolean;
var
{ Using `integer` sub-ranges ensures only these values are assigned. }
sum: 0..maxInt value 0;
i: 0..sample.capacity-1;
begin
for i := 0 to length(sample) - 1 do
begin
{ `1 + ord(odd(i))` produces an alternating scale factor `* 1`/`* 2`. }
sum := sum + (1 + ord(odd(i))) *
{ Obtain digit value for `integer` calculation. }
(ord(sample[length(sample) - i]) - ord('0')) -
{ Reverse operation if digit sum > 9, i.e. we added “too much”. }
ord(odd(i) and (sample[length(sample) - i] >= '5')) * 9
end;
check := sum mod 10 = 0
end;
begin
{
The Extended Pascal Boolean operator `and_then` (and `or_else`)
allows for “short-circuit evaluation”.
Otherwise, in Pascal `and` and `or` mandate complete evaluation.
}
luhnCheck := (length(sample) > 0) and_then containsDigitsOnly(sample)
and_then check
end;
{ === MAIN ============================================================= }
var
s: creditCardNumber;
begin
{ `EOF` is short for `EOF(input)`. }
while not EOF do
begin
readLn(s); { equivalent to `readLn(input, s)` }
writeLn(luhnCheck(s)) { = `writeLn(output, …)` }
end
end.
- Input:
49927398716 49927398717 1234567812345678 1234567812345670
- Output:
True False False True
The shown output was generated by a program compiled with the GPC, the GNU Pascal Compiler. A different compiler may emit a different rendition of true and false, for example in all-caps.
Perl
sub luhn_test
{
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 luhn_test('49927398716');
print luhn_test('49927398717');
print luhn_test('1234567812345678');
print luhn_test('1234567812345670');
Or using map( ) and a precomputed array:
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);
- Output:
1: 49927398716 0: 49927398717 0: 1234567812345678 1: 1234567812345670
Phix
with javascript_semantics function Luhn(string st) integer s=0, d for i=1 to length(st) do d = st[-i]-'0' s += iff(mod(i,2)?d,d*2-(d>4)*9) end for return remainder(s,10)=0 end function procedure test(string s) printf(1,"%20s : %t\n",{s,Luhn(s)}) end procedure papply({"49927398716","49927398717","1234567812345678","1234567812345670"},test)
- Output:
49927398716 : true 49927398717 : false 1234567812345678 : false 1234567812345670 : true
PHP
$numbers = "49927398716 49927398717 1234567812345678 1234567812345670";
foreach (split(' ', $numbers) as $n)
echo "$n is ", luhnTest($n) ? 'valid' : 'not valid', '</br>';
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;
}
- Output:
49927398716 is valid 49927398717 is not valid 1234567812345678 is not valid 1234567812345670 is valid
And a more concise example using PHP core methods:
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', "</br>\n";
- Output:
49927398716 is valid 49927398717 is not valid 1234567812345678 is not valid 1234567812345670 is valid
Picat
go =>
Nums = ["49927398716","49927398717","1234567812345678","1234567812345670"],
foreach (N in Nums)
println([N, isluhn10(N)])
end,
nl.
%
% isluhn10(num) returns 1 is valid, else 0
%
% Assumption: input num is a string.
%
isluhn10(Num) = V =>
X = [I : I in Num.reverse()] ++ [""],
Digits = "0246813579",
M = new_map([(I.to_string()=Digits[I+1]) : I in 0..9]),
V1 = sum([X[I].to_integer() + M.get2(X[I+1].to_string(),0) : I in 1..2..Num.length]),
V := cond(V1 mod 10 == 0, 1, 0).
% A variant of Map.get with conversions
get2(M, Key, Default)=V =>
if M.has_key(Key) then V= M.get(Key).to_integer() else V=Default end.
- Output:
[49927398716,1] [49927398717,0] [1234567812345678,0] [1234567812345670,1]
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 ) ) )
- Output:
: (mapcar luhn (49927398716 49927398717 1234567812345678 1234567812345670)) -> (0 NIL NIL 0)
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;
- 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.
PL/M
100H:
BDOS: PROCEDURE(F,A); DECLARE F BYTE, A ADDRESS; GO TO 5; END BDOS;
EXIT: PROCEDURE; GO TO 0; END EXIT;
PRINT: PROCEDURE(S); DECLARE S ADDRESS; CALL BDOS(9,S); END PRINT;
LUHN: PROCEDURE(NUM) BYTE;
DECLARE MAP DATA (0, 2, 4, 6, 8, 1, 3, 5, 7, 9);
DECLARE (START, NUM, MASK, TOTAL) ADDRESS;
DECLARE (CHR BASED NUM, DGT) BYTE;
START = NUM;
DO WHILE CHR <> '$'; NUM = NUM + 1; END;
MASK = NUM := NUM - 1;
TOTAL = 0;
DO WHILE NUM >= START;
DGT = CHR - '0';
IF NUM XOR MASK THEN DGT = MAP(DGT);
TOTAL = TOTAL + DGT;
NUM = NUM - 1;
END;
RETURN TOTAL MOD 10 = 0;
END LUHN;
DECLARE TEST (4) ADDRESS, I BYTE;
TEST(0) = .'49927398716$';
TEST(1) = .'49927398717$';
TEST(2) = .'1234567812345678$';
TEST(3) = .'1234567812345670$';
DO I=0 TO LAST(TEST);
CALL PRINT(TEST(I));
CALL PRINT(.': $');
IF LUHN(TEST(I))
THEN CALL PRINT(.'PASS$');
ELSE CALL PRINT(.'FAIL$');
CALL PRINT(.(13,10,'$'));
END;
CALL EXIT;
EOF
- Output:
49927398716: PASS 49927398717: FAIL 1234567812345678: FAIL 1234567812345670: PASS
PL/SQL
FUNCTION algoLuhn ( p_numeroVerif VARCHAR2 )
RETURN NUMBER
IS
i NUMBER;
v_NBi SMALLINT;
v_retour SMALLINT;
v_somme NUMBER := 0;
v_nbCar NUMBER;
BEGIN
v_nbCar := LENGTH(p_numeroVerif);
FOR i IN 1..v_nbCar
LOOP
v_NBi := TO_NUMBER(SUBSTR(p_numeroVerif,v_nbCar+1-i,1));
v_somme := v_somme
+ MOD(i,2) * v_NBi
+ MOD(i+1,2) * SIGN(-SIGN(v_Nbi-4)+1) * (2*v_NBi)
+ MOD(i+1,2) * SIGN( SIGN(v_Nbi-5)+1) * (2*v_NBi-9);
END LOOP;
v_retour := SIGN(MOD(v_somme,10));
RETURN v_retour;
EXCEPTION
WHEN OTHERS
THEN
RETURN 1;
END algoLuhn;
Plain English
To run:
Start up.
Test whether "49927398716" will pass the luhn test.
Test whether "49927398717" will pass the luhn test.
Test whether "1234567812345678" will pass the luhn test.
Test whether "1234567812345670" will pass the luhn test.
Wait for the escape key.
Shut down.
To test whether a string will pass the luhn test:
Write the string then ": " on the console without advancing.
If the string will pass the luhn test, write "valid" on the console; exit.
Write "invalid" on the console.
A sum is a number.
To decide if a string will pass the luhn test:
If the string is blank, say no.
Privatize the string.
Reverse the string.
Put 0 into a first sum.
Put 0 into a second sum.
Put 1 into a count.
Slap a rider on the string.
Loop.
If the rider's source is blank, break.
Put the rider's source's first's target into a byte.
If the byte is not any digit, say no.
Convert the byte to a number.
If the count is odd, add the number to the first sum; bump the count; bump the rider; repeat.
Double the number.
If the number is greater than 9, digital root the number.
Add the number to the second sum.
Bump the count. Bump the rider.
Repeat.
If the first sum plus the second sum is evenly divisible by 10, say yes.
Say no.
To digital root a number:
Divide the number by 10 giving a quotient and a remainder.
Put the quotient plus the remainder into the number.
To convert a byte to a number:
If the byte is any digit, put the byte minus 48 into the number.
- Output:
49927398716: valid 49927398717: invalid 1234567812345678: invalid 1234567812345670: valid
PowerBASIC
#COMPILE EXE
#DIM ALL
#COMPILER PBCC 6
FUNCTION LuhnCheckPassed(BYVAL dgts AS STRING) AS INTEGER
LOCAL i, s, s1 AS DWORD
dgts = STRREVERSE$(dgts)
FOR i = 1 TO LEN(dgts) STEP 2
s += VAL(MID$(dgts, i, 1))
NEXT i
FOR i = 2 TO LEN(dgts) STEP 2
s1 = 2 * VAL(MID$(dgts, i, 1))
IF s1 >= 10 THEN
s -= 9
END IF
s += s1
NEXT i
FUNCTION = NOT ISTRUE (s MOD 10)
END FUNCTION
FUNCTION PBMAIN () AS LONG
' this test is expected to pass:
CON.PRINT IIF$(LuhnCheckPassed("49927398716"), "passed", "failed")
' this test is expected to fail:
CON.PRINT IIF$(LuhnCheckPassed("49927398717"), "passed", "failed")
' this test is expected to fail:
CON.PRINT IIF$(LuhnCheckPassed("1234567812345678"), "passed", "failed")
' this test is expected to pass:
CON.PRINT IIF$(LuhnCheckPassed("1234567812345670"), "passed", "failed")
END FUNCTION
- Output:
passed failed failed passed
PowerShell
function Test-LuhnNumber
{
<#
.SYNOPSIS
Tests validity of credit card numbers.
.DESCRIPTION
Tests validity of credit card numbers using the Luhn test.
.PARAMETER Number
The number must be 11 or 16 digits.
.EXAMPLE
Test-LuhnNumber 49927398716
.EXAMPLE
[int64[]]$numbers = 49927398716, 49927398717, 1234567812345678, 1234567812345670
C:\PS>$numbers | ForEach-Object {
"{0,-17}: {1}" -f $_,"$(if(Test-LuhnNumber $_) {'Is valid.'} else {'Is not valid.'})"
}
#>
[CmdletBinding()]
[OutputType([bool])]
Param
(
[Parameter(Mandatory=$true,
Position=0)]
[ValidateScript({$_.Length -eq 11 -or $_.Length -eq 16})]
[ValidatePattern("^\d+$")]
[string]
$Number
)
$digits = ([Regex]::Matches($Number,'.','RightToLeft')).Value
$digits |
ForEach-Object `
-Begin {$i = 1} `
-Process {if ($i++ % 2) {$_}} |
ForEach-Object `
-Begin {$sumOdds = 0} `
-Process {$sumOdds += [Char]::GetNumericValue($_)}
$digits |
ForEach-Object `
-Begin {$i = 0} `
-Process {if ($i++ % 2) {$_}} |
ForEach-Object `
-Process {[Char]::GetNumericValue($_) * 2} |
ForEach-Object `
-Begin {$sumEvens = 0} `
-Process {
$_number = $_.ToString()
if ($_number.Length -eq 1)
{
$sumEvens += [Char]::GetNumericValue($_number)
}
elseif ($_number.Length -eq 2)
{
$sumEvens += [Char]::GetNumericValue($_number[0]) + [Char]::GetNumericValue($_number[1])
}
}
($sumOdds + $sumEvens).ToString()[-1] -eq "0"
}
Test-LuhnNumber 49927398716
- Output:
True
49927398716, 49927398717, 1234567812345678, 1234567812345670 | ForEach-Object {
"{0,-17}: {1}" -f $_,"$(if(Test-LuhnNumber $_) {'Is valid.'} else {'Is not valid.'})"
}
- Output:
49927398716 : Is valid. 49927398717 : Is not valid. 1234567812345678 : Is not valid. 1234567812345670 : Is valid.
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
- Output:
49927398716 is valid 49927398717 is not valid 1234567812345678 is not valid 1234567812345670 is valid
Python
Functional
The divmod in the function below conveniently splits a number into its two digits ready for summing:
>>> 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))
- Output:
49927398716 True 49927398717 False 1234567812345678 False 1234567812345670 True
Or, using itertools cycle with map and reduce:
'''Luhn test of credit card numbers'''
from operator import add, mul
from functools import reduce
from itertools import cycle
# luhn :: Integer -> Bool
def luhn(n):
'''True if n is a valid Luhn credit card number.'''
def divMod10Sum(a, x):
return a + add(*divmod(x, 10))
return 0 == reduce(
divMod10Sum,
map(
mul,
cycle([1, 2]),
map(int, reversed(str(n)))
),
0
) % 10
# ---------------------------TEST---------------------------
# main :: IO ()
def main():
'''Tests'''
print(list(
map(luhn, [
49927398716, 49927398717,
1234567812345678, 1234567812345670
])
))
if __name__ == '__main__':
main()
- Output:
[True, False, False, True]
Or alternatively, we can prune out some of the imports, define the Luhn predicate over strings rather than integers,
and cycle lambdas rather than integers.
'''Luhn test of credit card numbers'''
from itertools import cycle
# luhn :: String -> Bool
def luhn(k):
'''True if k is a valid Luhn credit card number string
'''
def asDigits(s):
return (int(c) for c in s)
return 0 == sum(map(
lambda f, x: f(x),
cycle([
lambda n: n,
lambda n: sum(asDigits(str(2 * n)))
]),
asDigits(reversed(k))
)) % 10
# ------------------------- TEST -------------------------
# main :: IO ()
def main():
'''Tests'''
print('\n'.join([
repr((x, luhn(x))) for x in [
"49927398716",
"49927398717",
"1234567812345678",
"1234567812345670"
]
]))
if __name__ == '__main__':
main()
- Output:
('49927398716', True) ('49927398717', False) ('1234567812345678', False) ('1234567812345670', True)
Procedural
Without usingsum() and divmod() functions:
>>> def vérifLuhn(ch):
sum = 0
chParity = len(ch) % 2
for i in range (len(ch)-1, -1, -1):
j = int(ch[i])
if ((i + 1) % 2 != chParity):
j = j * 2
if (j > 9):
j = j - 9
sum = sum + j
print("value calculated = ", str(sum))
return sum % 10 == 0
for n in (49927398716, 49927398717, 1234567812345678, 1234567812345670):
print (str(n)+" =>", vérifLuhn(str(n)))
Q
sd:{s:0; while[x<>0; s+:x mod 10; x:floor x%10]; s} / Sum digits of x
luhn:{
r:reverse string x; / Reversed credit card number
o:("I"$) each r[2*til ceiling (count r) % 2]; / Odd-indexed numbers
e:("I"$) each r[1+2*til floor (count r) % 2]; / Even-indexed numbers
0=(sum o,sd each e*2) mod 10 / Return 1b if checksum ends in 0; 0b otherwise
}
- Output:
q)luhn each 49927398716 49927398717 1234567812345678 1234567812345670 1001b
Quackery
[ 1 & ] is odd ( n --> b )
[ [] swap
[ 10 /mod
rot join swap
dup 0 = until ]
drop ] is digits ( n --> [ )
[ [] [] rot
dup size odd dip
[ witheach
[ nested join
swap ] ]
not if swap ] is unzip ( [ --> [ [ )
[ [] swap
witheach
[ 2 *
10 /mod +
join ] ] is 2*digitsum ( [ --> [ )
[ behead swap
witheach + ] is sum ( [ --> n )
[ digits
reverse
unzip
2*digitsum
sum
swap sum
+ 10 mod 0 = ] is luhn ( n --> b )
' [ 49927398716 49927398717
1234567812345678 1234567812345670 ]
witheach
[ dup echo
luhn iff
[ say " valid" ]
else
[ say " invalid" ]
cr ]
- Output:
49927398716 valid 49927398717 invalid 1234567812345678 invalid 1234567812345670 valid
R
is.luhn <- function(cc){
numbers <- as.numeric(rev(unlist(strsplit(cc,""))))
(sum(numbers[seq(1,length(numbers),by=2)]) + sum({numbers[seq(2,length(numbers),by=2)]*2 ->.; .%%10 +.%/%10})) %% 10 == 0
}
sapply(c("49927398716","49927398717","1234567812345678","1234567812345670"),is.luhn)
- Output:
sapply(c("49927398716","49927398717","1234567812345678","1234567812345670"),is.luhn) 49927398716 49927398717 1234567812345678 1234567812345670 TRUE FALSE FALSE TRUE
Racket
#lang racket
(define (luhn-test n)
(let loop ([n n] [odd? #t] [s 0])
(if (zero? n)
(zero? (modulo s 10))
(let*-values ([(q r) (quotient/remainder n 10)]
[(rq rr) (quotient/remainder (* (if odd? 1 2) r) 10)])
(loop q (not odd?) (+ s rq rr))))))
(map luhn-test '(49927398716 49927398717 1234567812345678 1234567812345670))
;; -> '(#t #f #f #t)
Raku
(formerly 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. %% is the divisible-by operator.
sub luhn-test ($number --> Bool) {
my @digits = $number.comb.reverse;
my $sum = @digits[0,2...*].sum
+ @digits[1,3...*].map({ |($_ * 2).comb }).sum;
return $sum %% 10;
}
# And we can test it like this:
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.";
}
- 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.
Refal
$ENTRY Go {
= <Test '49927398716'>
<Test '49927398717'>
<Test '1234567812345678'>
<Test '1234567812345670'>;
};
Test {
e.Digits = <Prout e.Digits ': ' <Luhn e.Digits>>;
};
Luhn {
(s.Sum) e.Digits s.Even s.Odd,
<Mul 2 <Numb s.Even>>: s.Even2,
<Divmod s.Even2 10>: (s.EvenD1) s.EvenD2,
<+ s.EvenD1 s.EvenD2>: s.EvenV,
<+ <Numb s.Odd> s.EvenV>: s.Step
= <Luhn (<+ s.Sum s.Step>) e.Digits>;
(s.Sum) s.Odd = <Luhn (<+ s.Sum <Numb s.Odd>>)>;
(s.Sum), <Divmod s.Sum 10>: (s.Rest) s.Last,
s.Last: {
0 = Valid;
s.X = Invalid;
};
e.Digits = <Luhn (0) e.Digits>;
};
- Output:
49927398716: Valid 49927398717: Invalid 1234567812345678: Invalid 1234567812345670: Valid
REXX
version 1
/*REXX program validates credit card numbers using the Luhn algorithm. */
#.=; #.1= 49927398716 /*the 1st sample credit card number. */
#.2= 49927398717 /* " 2nd " " " " */
#.3= 1234567812345678 /* " 3rd " " " " */
#.4= 1234567812345670 /* " 4th " " " " */
do k=1 while #.k\=='' /*validate all the credit card numbers.*/
say right( Luhn(#.k), 9) ' the Luhn test, credit card number: ' #.k
end /*k*/ /* [↑] function returns passed│flunked.*/
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
Luhn: procedure; parse arg x; $= 0 /*get credit card number; zero $ sum. */
y= reverse( left(0, length(x) // 2)x) /*add leading zero if needed, reverse. */
do j=1 to length(y)-1 by 2; _= substr(y, j+1, 1) * 2
$= $ + substr(y, j, 1) + left(_, 1) + substr(_, 2, 1, 0) /* ◄────────┐*/
end /*j*/ /*sum odd and even decimal digits ►────┘*/
return word('passed flunked',1+($//10==0)) /*$ ending in zero? Then the # passed.*/
- output when using the (internal) default inputs:
flunked the Luhn test, credit card number: 49927398716 passed the Luhn test, credit card number: 49927398717 passed the Luhn test, credit card number: 1234567812345678 flunked the Luhn test, credit card number: 1234567812345670
Version 2
/* Rexx ***************************************************
* 09.04.2013 Walter Pachl
* Implements the task's description in a rather concise way
* Instead of reverting the ccn work it backwards
**********************************************************/
numeric digits 20
push 49927398716
push 49927398717
push 1234567812345678
push 1234567812345670
do while queued() > 0
parse pull ccnum
if luhn(ccnum) then ln = 'passed'
else ln = 'failed'
say right(ccnum, 20) ln
end
return
exit
luhn:
Parse Arg ccn /* credit card number */
sum=0 /* initialize test sum */
even=0 /* even indicator */
Do i=length(ccn) To 1 By -1 /* process all digits */
c=substr(ccn,i,1) /* pick one digit at a time */
If even Then Do /* even numbered digit */
c=c*2 /* double it */
If c>=10 Then /* 10, 12, 14, 16, 18 */
c=c-9 /* Sum of the two digits */
End /* end of even numbered */
even=\even /* flip even indicator */
sum=sum+c /* add into test sum */
End
Return right(sum,1)=0 /* ok if last digit is 0 */
- Output:
1234567812345670 passed 1234567812345678 failed 49927398717 failed 49927398716 passed
Ring
decimals(0)
test = ["49927398716", "49927398717", "1234567812345678", "1234567812345670"]
for n = 1 to len(test)
see test[n] + " -> " + cardtest(test[n]) + nl
next
func cardtest(numstr)
revstring = revstr(numstr)
s1 = revodd(revstring)
s2 = reveven(revstring)
s3 =right(string(s1+s2), 1)
if s3 = "0"
return "Valid"
else
return "Invalid"
ok
func revstr(str)
strnew = ""
for nr = len(str) to 1 step -1
strnew = strnew + str[nr]
next
return strnew
func revodd(str)
strnew = ""
for nr = 1 to len(str) step 2
strnew = strnew + str[nr]
next
sumodd = 0
for p = 1 to len(strnew)
sumodd = sumodd + number(strnew[p])
next
return sumodd
func reveven(str)
strnew = ""
for nr = 2 to len(str) step 2
strnew = strnew + str[nr]
next
lsteven = []
for p = 1 to len(strnew)
add(lsteven, string(2*number(strnew[p])))
next
arreven = list(len(lsteven))
for q = 1 to len(lsteven)
sum = 0
for w = 1 to len(lsteven[q])
sum = sum + lsteven[q][w]
next
arreven[q] = sum
next
sumarr = 0
for x = 1 to len(arreven)
sumarr = sumarr + arreven[x]
next
return sumarr
Output:
49927398716 -> Valid 49927398717 -> Invalid 1234567812345678 -> Invalid 1234567812345670 -> Valid
RPL
Card numbers shall be entered as strings to avoid any rounding error when testing long ones.
RPL code | Comment |
---|---|
« 0 → card even
« 0
card SIZE 1 FOR j
card j DUP SUB OBJ→
IF even THEN
DUP + 10 MOD LASTARG / IP + END
+ 1 'even' STO-
-1 STEP
10 MOD NOT
» » 'LUHN?' STO
|
LUHN? ( "card_number" -- boolean )
sum = 0
loop for j=n to 1
digit = card[j]
if even digit
multiply it by 2 and add digits
sum += digit ; reverse parity flag
return not(sum mod 10)
|
{ "49927398716" "49927398717" "1234567812345678" "1234567812345670" } 1 « LUHN? » DOLIST
- Output:
1: { 1 0 0 1 }
Ruby
def luhn_valid?(str)
str.scan(/\d/).reverse #using str.to_i.digits fails for cases with leading zeros
.each_slice(2)
.sum { |i, k = 0| i.to_i + ((k.to_i)*2).digits.sum }
.modulo(10).zero?
end
["49927398716", "49927398717", "1234567812345678", "1234567812345670"].map{ |i| luhn_valid?(i) }
- Output:
[true, false, false, true]
Simpler Alternative
def luhn_valid?(n) # Card values can be numbers or strings
d2sum = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
sum, num = 0, n.to_i
num.digits.each_with_index { |digit, i| sum += i.even? ? digit : d2sum[digit] }
sum % 10 == 0
end
cards = [49927398716, "49927398717", 1234567812345678, "1234567812345670"]
cards.each{ |i| puts "#{i}: #{luhn_valid?(i)}" }
- Output:
49927398716: true 49927398717: false 1234567812345678: false 1234567812345670: true
Run BASIC
card$(1) = "49927398716"
card$(2) = "49927398717"
card$(3) = "1234567812345678"
card$(4) = "1234567812345670"
for i = 1 to 4
print card$(i);" ";luhn$(card$(i))
next i
FUNCTION luhn$(card$)
lc = len(card$)
for i = lc to 1 step -1
digit = val(mid$(card$,i,1))
if ((lc -i) mod 2) = 0 then chkSum = chkSum + digit else chkSum = chkSum + int(digit * 2.2)
next i
if chkSum mod 10 = 0 then luhn$ = "True" else luhn$ = "False"
end function
- Output:
49927398716 True 49927398717 False 1234567812345678 False 1234567812345670 True
Rust
extern crate luhn_test_of_credit_card_numbers;
use luhn_test_of_credit_card_numbers::luhn_test;
fn validate_isin(isin: &str) -> bool {
if !isin.chars().all(|x| x.is_alphanumeric()) || isin.len() != 12 {
return false;
}
if !isin[..2].chars().all(|x| x.is_alphabetic())
|| !isin[2..12].chars().all(|x| x.is_alphanumeric())
|| !isin.chars().last().unwrap().is_numeric()
{
return false;
}
let bytes = isin.as_bytes();
let s2 = bytes
.iter()
.flat_map(|&c| {
if c.is_ascii_digit() {
vec![c]
} else {
(c + 10 - ('A' as u8)).to_string().into_bytes()
}
})
.collect::<Vec<u8>>();
let string = std::str::from_utf8(&s2).unwrap();
let number = string.parse::<u64>().unwrap();
return luhn_test(number as u64);
}
#[cfg(test)]
mod tests {
use super::validate_isin;
#[test]
fn test_validate_isin() {
assert_eq!(validate_isin("US0378331005"), true);
assert_eq!(validate_isin("US0373831005"), false);
assert_eq!(validate_isin("U50378331005"), false);
assert_eq!(validate_isin("US03378331005"), false);
assert_eq!(validate_isin("AU0000XVGZA3"), true);
assert_eq!(validate_isin("AU0000VXGZA3"), true);
assert_eq!(validate_isin("FR0000988040"), true);
}
}
- Output:
49927398716: true 49927398717: false 1234567812345678: false 1234567812345670: true
Scala
Functional style
object Luhn {
private def parse(s: String): Seq[Int] = s.map{c =>
assert(c.isDigit)
c.asDigit
}
def checksum(digits: Seq[Int]): Int = {
digits.reverse.zipWithIndex.foldLeft(0){case (sum,(digit,i))=>
if (i%2 == 0) sum + digit
else sum + (digit*2)/10 + (digit*2)%10
} % 10
}
def validate(digits: Seq[Int]): Boolean = checksum(digits) == 0
def checksum(number: String): Int = checksum(parse(number))
def validate(number: String): Boolean = validate(parse(number))
}
object LuhnTest extends App {
Seq(("49927398716", true),
("49927398717", false),
("1234567812345678", false),
("1234567812345670", true)
).foreach { case (n, expected) =>
println(s"$n ${Luhn.validate(n)}")
assert(Luhn.validate(n) == expected)
}
}
- Output:
49927398716 true 49927398717 false 1234567812345678 false 1234567812345670 true
Imperative style
def luhnTest1(number: String): Boolean = {
var (odd, sum) = (true, 0)
for (int <- number.reverse.map { _.toString.toShort }) {
if (odd) sum += int
else sum += (int * 2 % 10) + (int / 5)
odd = !odd
}
sum % 10 == 0
}
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))))))))))
- Output:
(map luhn '(49927398716 49927398717 1234567812345678 1234567812345670)) (#t #f #f #t)
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
- Output:
$ sed -f luhn.sed <<! 49927398716 49927398717 1234567812345678 1234567812345670 ! Pass Fail Fail Pass
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;
- Output:
49927398716: TRUE 49927398717: FALSE 1234567812345678: FALSE 1234567812345670: TRUE
SenseTalk
function LuhnCheck ccNum
put length of ccNum into numDigits
put the last character of ccNum into total
put numDigits modulo 2 into parity
repeat for each character of the first numDigits - 1 characters of ccNum
put it into digit
if (the counter - 1) modulo 2 equals parity
multiply digit by 2
end if
if digit is greater than 9
subtract 9 from digit
end if
add digit to total
end repeat
return total is divisible by 10
end LuhnCheck
repeat for each item of (49927398716, 49927398717, 1234567812345678, 1234567812345670)
put it && LuhnCheck(it)
end repeat
SequenceL
main(args(2)) :=
sum(luhnTest(asciiToInt(args[1]) - asciiToInt('0'))) mod 10 = 0;
s2Mapping := [0,2,4,6,8,1,3,5,7,9];
luhnTest(x(1))[i] :=
x[i] when i mod 2 = size(x) mod 2 else
s2Mapping[x[i] + 1];
SETL
program luhn_test;
tests := [
49927398716,
49927398717,
1234567812345678,
1234567812345670
];
loop for test in tests do
print(test, if luhn test then "pass" else "fail" end);
end loop;
op luhn(n);
fac := 2;
digits := [val d * (fac := 3-fac) : d in reverse str n];
return 0 = +/[d - if d>9 then 9 else 0 end : d in digits] mod 10;
end op;
end program;
- Output:
49927398716 pass 49927398717 fail 1234567812345678 fail 1234567812345670 pass
Shen
(define mapi
_ _ [] -> []
F N [X | Xs] -> [(F N X) | (mapi F (+ N 1) Xs)])
(define double
X -> (let Y (* 2 X) (if (> Y 9) (- Y 9) Y)))
(define luhn?
Number ->
(let Exploded (explode Number)
Digits (map (/. H (- (string->n H) 48)) Exploded)
Reversed (reverse Digits)
Doubled (mapi (/. N X (if (= 1 (shen.mod N 2)) (double X) X)) 0 Reversed)
Summed (sum Doubled)
Modded (shen.mod Summed 10)
(= 0 Modded)))
"Expected: [true false false true]"
(map (function luhn?) ["49927398716" "49927398717" "1234567812345678" "1234567812345670"])
- Output:
mapi transform-x luhn? "Expected: [true false false true]" [true false false true] run time: 0.014999985694885254 secs loaded
Sidef
func luhn (n) {
static a = {|j| (2*j // 10) + (2*j % 10) }.map(^10)
var checksum = n.digits.map_kv {|i,j|
i.is_odd ? a[j] : j
}.sum
checksum % 10 == 0
}
for n in [49927398716, 49927398717, 1234567812345678, 1234567812345670] {
say [n, luhn(n)]
}
- Output:
[49927398716, true] [49927398717, false] [1234567812345678, false] [1234567812345670, true]
SNOBOL4
Using a precomputed array.
define('luhn(n)a,d,i,j,sum') :(luhn_end)
luhn n = reverse(n); a = array('0:9')
ln1 a<i> = (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
ok = array('0:1')
ok<0> = 'FAIL'
ok<1> = 'OK'
* Test and display
define('test(n)') :(test_end)
test output = n ': ' ok<luhn(n)> :(return)
test_end
test('49927398716')
test('49927398717')
test('1234567812345678')
test('1234567812345670')
end
- Output:
49927398716: OK 49927398717: FAIL 1234567812345678: FAIL 1234567812345670: OK
SparForte
As a structured script.
#!/usr/local/bin/spar
pragma annotate( summary, "luhn test of credit card numbers" )
@( description, "The Luhn test is used by some credit card companies to " )
@( description, "distinguish valid credit card numbers from what could be a random selection of digits." )
@( see_also, "https://rosettacode.org/wiki/Luhn_test_of_credit_card_number" )
@( author, "Ken O. Burtch" );
pragma license( unrestricted );
pragma restriction( no_external_commands );
procedure luhn is
bad_digit : exception;
-- return true if the card number passes the luhn test
function is_luhn( card_number : string) return boolean is
card_num_len : constant natural := strings.length( card_number );
checksum: natural := 0;
ch : character;
begin
for i in reverse 1..card_num_len loop
ch := strings.element( card_number, i );
if strings.is_digit( ch ) then
declare
ord : constant natural := numerics.pos(ch);
begin
if ((card_num_len-1) and (i-1) ) /= 0 then
checksum := @ + ord;
else
checksum := @ + numerics.floor(ord / 5) + ((2*ord) mod 10);
end if;
end;
else
raise bad_digit;
end if;
end loop;
return checksum mod 10 = 0;
end is_luhn;
-- check a credit card and display the result
procedure check_card( card_number : string ) is
begin
put( card_number )
@( ": " )
@( is_luhn( card_number ) );
new_line;
end check_card;
begin
check_card("49927398716");
check_card("49927398717");
check_card("1234567812345678");
check_card("1234567812345670");
end luhn;
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.
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;
- Output:
49927398716 is valid. 49927398717 is not valid. 1234567812345678 is not valid. 1234567812345670 is valid. 123456781234567D is not valid.
SQL PL
With SQL PL:
--#SET TERMINATOR @
SET SERVEROUTPUT ON @
CREATE OR REPLACE FUNCTION LUHN_TEST (
IN NUMBER VARCHAR(24)
) RETURNS SMALLINT
--) RETURNS BOOLEAN
BEGIN
DECLARE TYPE CARD_NUMBER AS VARCHAR(1) ARRAY [24];
DECLARE LENGTH SMALLINT;
DECLARE REVERSE CARD_NUMBER;
DECLARE I SMALLINT;
DECLARE POS SMALLINT;
DECLARE S1 SMALLINT;
DECLARE S2 SMALLINT;
DECLARE TEMP SMALLINT;
DECLARE RET SMALLINT;
--DECLARE RET BOOLEAN;
DECLARE INVALID_CHAR CONDITION FOR SQLSTATE 'LUHN1';
-- Reverse the order of the digits in the number.
SET LENGTH = LENGTH(NUMBER);
SET I = 1;
WHILE (I <= LENGTH) DO
SET POS = LENGTH - I + 1;
SET REVERSE[POS] = SUBSTR(NUMBER, I, 1);
IF (ASCII(REVERSE[POS]) < 48 OR 57 < ASCII(REVERSE[POS])) THEN
SIGNAL INVALID_CHAR SET MESSAGE_TEXT = 'Invalid character, not a digit';
END IF;
SET I = I + 1;
END WHILE;
-- Take the first, third, ... and every other odd digit in the reversed digits and sum them to form the partial sum s1
SET S1 = 0;
SET I = 1;
WHILE (I <= LENGTH) DO
IF (MOD(I, 2) = 1) THEN
SET S1 = S1 + REVERSE[I];
END IF;
-- CALL DBMS_OUTPUT.PUT_LINE('I ' || I || ', S1 ' || S1 || ', val ' || REVERSE[I]);
SET I = I + 1;
END WHILE;
-- Taking the second, fourth ... and every other even digit in the reversed digits:
SET S2 = 0;
SET TEMP = 0;
SET I = 1;
WHILE (I <= LENGTH) DO
IF (MOD(I, 2) = 0) THEN
-- Multiply each digit by two and sum the digits if the answer is greater than nine to form partial sums for the even digits
SET TEMP = REVERSE[I] * 2;
IF (TEMP > 9) THEN
SET TEMP = (TEMP / 10) + (MOD(TEMP, 10));
END IF;
-- Sum the partial sums of the even digits to form s2
SET S2 = S2 + TEMP;
END IF;
-- CALL DBMS_OUTPUT.PUT_LINE('I ' || I || ', S2 ' || S2 || ', TEMP ' || TEMP || ' val ' || REVERSE[I]);
SET I = I + 1;
END WHILE;
-- 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.
SET RET = 1;
--SET RET = FALSE;
SET TEMP = S1 + S2;
IF (MOD(TEMP, 10) = 0) THEN
SET RET = 0;
--SET RET = TRUE;
CALL DBMS_OUTPUT.PUT_LINE('It is a valid number ' || S1 || '+' || S2 || '=' || TEMP);
ELSE
CALL DBMS_OUTPUT.PUT_LINE('It is NOT a valid number ' || S1 || '+' || S2 || '=' || TEMP);
END IF;
RETURN RET;
END
@
Output:
db2 -td@ db2 => SET SERVEROUTPUT ON @ DB20000I The SET SERVEROUTPUT command completed successfully. db2 => CREATE OR REPLACE FUNCTION VALIDATE_CREDIT_CARD_NUMBER ( ... db2 (cont.) => END @ DB20000I The SQL command completed successfully. db2 => values VALIDATE_CREDIT_CARD_NUMBER(49927398716)@ 1 ------ 0 1 record(s) selected. It is a valid number 42+28=70 db2 => VALUES VALIDATE_CREDIT_CARD_NUMBER(49927398717)@ 1 ------ 1 1 record(s) selected. It is NOT a valid number 43+28=71 db2 => VALUES VALIDATE_CREDIT_CARD_NUMBER(1234567812345678)@ 1 ------ 1 1 record(s) selected. It is NOT a valid number 40+28=68 db2 => VALUES VALIDATE_CREDIT_CARD_NUMBER(1234567812345670)@ 1 ------ 0 1 record(s) selected. It is a valid number 32+28=60
Standard ML
local
fun revDigits 0 = []
| revDigits n = (n mod 10) :: revDigits (n div 10)
fun digitSum n = if n > 9 then digitSum (n div 10 + n mod 10)
else n
fun luhn_sum [] = 0
| luhn_sum [d] = d
| luhn_sum (d::d'::ds) = d + digitSum (2*d') + luhn_sum ds
in
fun luhn_test n = luhn_sum (revDigits n) mod 10 = 0
val res = map luhn_test [49927398716, 49927398717, 1234567812345678, 1234567812345670];
end;
(*
[opening file "luhn.sml"]
> val luhn_test = fn : int -> bool
val res = [true, false, false, true] : bool list
[closing file "luhn.sml"]
*)
Swift
func luhn(_ number: String) -> Bool {
return number.reversed().enumerated().map({
let digit = Int(String($0.element))!
let even = $0.offset % 2 == 0
return even ? digit : digit == 9 ? 9 : digit * 2 % 9
}).reduce(0, +) % 10 == 0
}
luhn("49927398716") // true
luhn("49927398717") // false
Tcl
Based on an algorithmic encoding for the test on Wikipedia.
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}]
}
Driver:
foreach testNumber {
49927398716
49927398717
1234567812345678
1234567812345670
} {
puts [format "%s is %s" $testNumber \
[lindex {"NOT valid" "valid"} [luhn $testNumber]]]
}
- Output:
49927398716 is valid 49927398717 is NOT valid 1234567812345678 is NOT valid 1234567812345670 is valid
Terraform
variable number {
type = "string"
}
locals {
digits = reverse(split("", var.number))
count = length(local.digits)
odds = [for i in range(local.count): local.digits[i] if i%2==0]
evens = [for i in range(local.count): local.digits[i] if i%2==1]
s1 = length(flatten([for d in local.odds: range(d)]))
doubles = [for d in local.evens: d * 2]
partials = [for d in local.doubles: d < 10 ? d : floor(d/10)+d%10]
s2 = length(flatten([for p in local.partials: range(p)]))
check = (local.s1 + local.s2) % 10
}
output "valid" {
value = local.check == 0
}
- Output:
$ terraform apply var.number Enter a value: 49927398716 Apply complete! Resources: 0 added, 0 changed, 0 destroyed. Outputs: valid = true $ TF_VAR_number=49927398717 terraform apply >/dev/null; terraform output valid false $ TF_VAR_number=1234567812345678 terraform apply >/dev/null; terraform output valid false $ TF_VAR_number=1234567812345670 terraform apply >/dev/null; terraform output valid true
TI-83 BASIC
PROGRAM:LUHN
:Disp "ENTER NUMBER"
:Input Str1
:0→S
:0→E
:For(I,length(Str1),1,-1)
:inString("0123456789",sub(Str1,I,1))–1→X
:If X<0
:Goto BA
:If E≠0
:Then
:2X→X
:If X>9
:X–9→X
:End
:X+S→S
:not(E)→E
:End
:If fPart(S/10)=0
:Then
:Disp "GOOD CARD"
:Else
:Lbl BA
:Disp "BAD CARD"
:End
Transact-SQL
CREATE FUNCTION dbo._CreditCardNumCheck( @strCCNum VarChar(40) )
RETURNS VarChar(7)
AS
BEGIN
DECLARE @string VarChar(40) = REVERSE(@strCCNum); -- usage: set once, never changed
DECLARE @strS2Values VarChar(10) = '0246813579'; -- constant: maps digits to their S2 summed values
DECLARE @table TABLE (ID INT, Value INT, S_Value INT); -- ID=digit position. S_Value is used for SUM().
DECLARE @p INT = 0; -- loop counter: position in string
-- Convert the reversed string's digits into rows in a table variable, S_Values to be updated afterwards
WHILE @p < LEN(@string)
BEGIN
SET @p = @p+1;
INSERT INTO @table (ID,Value,S_Value) VALUES (@p, CONVERT(INT,SUBSTRING(@string,@p,1)), 0);
END
-- Update S_Value column : the digit's value to be summed (for even-positioned digits this is mapped via @strS2Values)
UPDATE @table SET S_Value = CASE WHEN ID % 2 = 1 THEN Value ELSE CONVERT(INT,SUBSTRING(@strS2Values,Value+1,1)) END
-- If the SUM of S_Values ends in 0 (modulo 10 = 0) then the CC Number is valid
RETURN CASE WHEN (SELECT SUM(S_Value) FROM @table) % 10 = 0 THEN 'Valid' ELSE 'Invalid' END
END
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
- Output:
49927398716 true 49927398717 false 1234567812345678 false 1234567812345670 true
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)
$ txr luhn.txr luhn.txt 49927398716 -> good 49927398717 -> bad 1234567812345678 -> bad 1234567812345670 -> good
Uiua
Luhn ← =0◿10+⊃(/+⊢|/+∵(⍥(-9)>9.×2)⊡1)⍉⬚0↯∞_2⇌
T ← {"49927398716"
"49927398717"
"1234567812345678"
"1234567812345670"}
≡◇(Luhn ≡⋕) T
- Output:
[1 0 0 1]
UNIX Shell
function luhn {
typeset n p s t=('0123456789' '0516273849')
while ((-n<${#1})); do
p="${t[n--%2]%${1:n:1}*}"
((s+=${#p}))
done
((s%10))
}
for c in 49927398716 49927398717 1234567812345678 1234567812345670; do
if luhn $c; then
echo $c is invalid
else
echo $c is valid
fi
done
Notes:
- The parameter expansion hack (p=${t…%${1:n:1}};…${#p}…) is an interesting way of converting a set of characters to ordinals. It's highly extensible to larger character sets (e.g. for ISBN and Code 39 checksums).
- Invalid characters are effectively treated as 0s. This is actually useful sometimes for ignoring alphabetic prefixes.
- When attempting to understand the function, remember that n is negative, so it indexes from the end of the input string.
- Output:
49927398716 is valid 49927398717 is invalid 1234567812345678 is invalid 1234567812345670 is valid
Ursala
#import std
#import nat
luhn = %nP; %np*hxiNCNCS; not remainder\10+ //sum:-0@DrlrHK32 ~&iK27K28TK25 iota10
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:
#cast %bL
test = luhn* <49927398716,49927398717,1234567812345678,1234567812345670>
- Output:
<true,false,false,true>
VBA
Option Explicit
Sub Main()
Debug.Print "Number 49927398716 is "; Luhn("49927398716")
Debug.Print "Number 49927398717 is "; Luhn("49927398717")
Debug.Print "Number 1234567812345678 is "; Luhn("1234567812345678")
Debug.Print "Number 1234567812345670 is "; Luhn("1234567812345670")
End Sub
Private Function Luhn(Nb As String) As String
Dim t$, i&, Summ&, s&
t = StrReverse(Nb)
For i = 1 To Len(t) Step 2
Summ = Summ + CInt(Mid(t, i, 1))
Next i
For i = 2 To Len(t) Step 2
s = 2 * (CInt(Mid(t, i, 1)))
If s >= 10 Then
Summ = Summ - 9
End If
Summ = Summ + s
Next i
If Summ Mod 10 = 0 Then
Luhn = "valid"
Else
Luhn = "invalid"
End If
End Function
- Output:
Number 49927398716 is valid Number 49927398717 is invalid Number 1234567812345678 is invalid Number 1234567812345670 is valid
VBScript
Function Luhn_Test(cc)
cc = RevString(cc)
s1 = 0
s2 = 0
For i = 1 To Len(cc)
If i Mod 2 > 0 Then
s1 = s1 + CInt(Mid(cc,i,1))
Else
tmp = CInt(Mid(cc,i,1))*2
If tmp < 10 Then
s2 = s2 + tmp
Else
s2 = s2 + CInt(Right(CStr(tmp),1)) + 1
End If
End If
Next
If Right(CStr(s1 + s2),1) = "0" Then
Luhn_Test = "Valid"
Else
Luhn_Test = "Invalid"
End If
End Function
Function RevString(s)
For i = Len(s) To 1 Step -1
RevString = RevString & Mid(s,i,1)
Next
End Function
WScript.Echo "49927398716 is " & Luhn_Test("49927398716")
WScript.Echo "49927398717 is " & Luhn_Test("49927398717")
WScript.Echo "1234567812345678 is " & Luhn_Test("1234567812345678")
WScript.Echo "1234567812345670 is " & Luhn_Test("1234567812345670")
- Output:
49927398716 is Valid 49927398717 is Invalid 1234567812345678 is Invalid 1234567812345670 is Valid
Visual Basic
Public Function LuhnCheckPassed(ByVal dgts As String) As Boolean
Dim i As Long, s As Long, s1 As Long
dgts = VBA.StrReverse(dgts)
For i = 1 To Len(dgts) Step 2
s = s + CInt(Mid$(dgts, i, 1))
Next i
For i = 2 To Len(dgts) Step 2
s1 = 2 * (CInt(Mid$(dgts, i, 1)))
If s1 >= 10 Then
s = s - 9
End If
s = s + s1
Next i
LuhnCheckPassed = Not CBool(s Mod 10)
End Function
Test:
Sub Main()
Debug.Assert LuhnCheckPassed("49927398716")
Debug.Assert Not LuhnCheckPassed("49927398717")
Debug.Assert Not LuhnCheckPassed("1234567812345678")
Debug.Assert LuhnCheckPassed("1234567812345670")
End Sub
Visual Basic .NET
Imports System.Linq
Function ValidLuhn(value As String)
Return value.Select(Function(c, i) (AscW(c) - 48) << ((value.Length - i - 1) And 1)).Sum(Function(n) If(n > 9, n - 9, n)) Mod 10 = 0
End Function
Sub Main()
Console.WriteLine(ValidLuhn("49927398716"))
Console.WriteLine(ValidLuhn("49927398717"))
Console.WriteLine(ValidLuhn("1234567812345678"))
Console.WriteLine(ValidLuhn("1234567812345670"))
End Sub
- Output:
True False False True
V (Vlang)
const (
input = '49927398716
49927398717
1234567812345678
1234567812345670'
t = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9]
)
fn luhn(s string) bool {
odd := s.len & 1
mut sum := 0
for i, c in s.split('') {
if c < '0' || c > '9' {
return false
}
if i&1 == odd {
sum += t[c.int()-'0'.int()]
} else {
sum += c.int() - '0'.int()
}
}
return sum%10 == 0
}
fn main() {
for s in input.split("\n") {
println('$s ${luhn(s)}')
}
}
- Output:
49927398716 true 49927398717 false 1234567812345678 false 1234567812345670 true
Wren
import "./fmt" for Fmt
import "./iterate" for Stepped
var luhn = Fn.new { |s|
// reverse digits
s = s[-1..0]
// sum the odd digits
var s1 = Stepped.new(s, 2).reduce(0) { |sum, d| sum + d.bytes[0] - 48 }
// sum two times the even digits
var s2 = Stepped.new(s[1..-1], 2).reduce(0) { |sum, d|
var d2 = (d.bytes[0] - 48) * 2
return sum + ((d2 > 9) ? d2%10 + 1 : d2)
}
// check if s1 + s2 ends in zero
return (s1 + s2)%10 == 0
}
var tests = [ "49927398716", "49927398717", "1234567812345678", "1234567812345670"]
for (test in tests) {
var ans = (luhn.call(test)) ? "pass" : "fail"
Fmt.print("$-16s -> $s", test, ans)
}
- Output:
49927398716 -> pass 49927398717 -> fail 1234567812345678 -> fail 1234567812345670 -> pass
Xojo
Public Function Modulus10(digits As String) as String
//
// Confirm the digits are really, well, digits
//
dim validator as new RegEx
validator.SearchPattern = "\A\d+\z"
if validator.Search( digits ) is nil then
//
// Raise an exception or something
//
end if
static doublingTable() as string = array( "0", "2", "4", "6", "8", "1", "3", "5", "7", "9" )
dim digitArr() as string = digits.Split( "" )
for i as integer = digitArr.Ubound downto 0 step 2
digitArr( i ) = doublingTable( digitArr( i ).Val )
next
dim sum as integer
for each digit as string in digitArr
sum = sum + digit.Val
next
dim check as integer = ( sum * 9 ) mod 10
return str( check )
End Function
Public Function ValidateMod10(digits As String) as Boolean
dim checkDigit as string = digits.Right( 1 )
digits = digits.Left( digits.Len - 1 )
return Modulus10( digits ) = checkDigit
End Function
- Output:
ValididateMod10( "49927398716" ) = True ValididateMod10( "49927398717" ) = False ValididateMod10( "1234567812345678" ) = False ValididateMod10( "1234567812345670" ) = True
XPL0
string 0; \use zero-terminated strings
func Valid(Str); \Return 'true' if digits in Str pass Luhn test
char Str;
int Len, Sum, I, Dig;
[Len:= 0; \find length of Str
while Str(Len) do Len:= Len+1;
Sum:= 0; \sum even and odd digits
for I:= 0 to Len-1 do \(no need to reverse)
[if (I xor Len) and 1 then
Sum:= Sum + Str(I) - ^0
else [Dig:= Str(I) - ^0;
Dig:= Dig*2;
Sum:= Sum + Dig/10 + rem(0);
];
];
return rem(Sum/10) = 0;
];
int Luhn, N;
[Luhn:= ["49927398716",
"49927398717",
"1234567812345678",
"1234567812345670"];
for N:= 0 to 4-1 do
[Text(0, Luhn(N));
Text(0, if Valid(Luhn(N))
then " is valid"
else " is not valid");
CrLf(0);
];
]
- Output:
49927398716 is valid 49927398717 is not valid 1234567812345678 is not valid 1234567812345670 is valid
zkl
fcn luhnTest(n){
0 == (n.split().reverse().reduce(fcn(s,n,clk){
s + if(clk.next()) n else 2*n%10 + n/5 },0,Walker.cycle(1,0)) %10)
}
T(49927398716,49927398717,1234567812345678,1234567812345670)
.apply(luhnTest).println();
- Output:
L(True,False,False,True)
ZX Spectrum Basic
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
- Output:
49927398716 VALID! 49927398717 INVALID! 1234567812345678 INVALID! 1234567812345670 VALID!
- Programming Tasks
- Checksums
- GUISS/Omit
- 11l
- 360 Assembly
- 8080 Assembly
- 8086 Assembly
- 8th
- ABAP
- ACL2
- Action!
- ActionScript
- Ada
- ALGOL 68
- ALGOL W
- APL
- AppleScript
- ARM Assembly
- Arturo
- AutoHotkey
- AutoIt
- AWK
- Bash
- BASIC
- BASIC256
- Chipmunk Basic
- IS-BASIC
- QBasic
- True BASIC
- UBasic/4tH
- Yabasic
- Batch File
- BBC BASIC
- Bc
- BCPL
- Befunge
- BQN
- Bracmat
- Brainf***
- Bruijn
- Burlesque
- C
- C sharp
- C++
- Caché ObjectScript
- Ceylon
- Clojure
- CLU
- COBOL
- Comal
- Common Lisp
- Cowgol
- Crystal
- D
- Delphi
- SysUtils,StdCtrls
- Draco
- EasyLang
- EchoLisp
- Elixir
- Emacs Lisp
- Seq.el
- Erlang
- Euphoria
- Excel
- F Sharp
- Factor
- Forth
- Fortran
- FreeBASIC
- Free Pascal
- FunL
- FutureBasic
- Gambas
- GAP
- Go
- Groovy
- Haskell
- HicEst
- Icon
- Unicon
- J
- Java
- Java Long type version
- JavaScript
- Jq
- Julia
- K
- Kotlin
- Langur
- Lasso
- Liberty BASIC
- LiveCode
- Logo
- Lua
- M2000 Interpreter
- MACRO-11
- Mathematica
- Wolfram Language
- MATLAB
- Min
- MiniScript
- MUMPS
- Nanoquery
- NetRexx
- Nim
- Objeck
- Objective-C
- OCaml
- Octave
- Oforth
- OpenEdge/Progress
- Order
- Oz
- Pascal
- Perl
- Phix
- PHP
- Picat
- PicoLisp
- PL/I
- PL/M
- PL/SQL
- Plain English
- PowerBASIC
- PowerShell
- PureBasic
- Python
- Q
- Quackery
- R
- Racket
- Raku
- Refal
- REXX
- Ring
- RPL
- Ruby
- Run BASIC
- Rust
- Scala
- Scheme
- Sed
- Seed7
- SenseTalk
- SequenceL
- SETL
- Shen
- Sidef
- SNOBOL4
- SparForte
- SPARK
- SQL PL
- Standard ML
- Swift
- Tcl
- Terraform
- TI-83 BASIC
- Transact-SQL
- TUSCRIPT
- TXR
- Uiua
- UNIX Shell
- Ursala
- VBA
- VBScript
- Visual Basic
- Visual Basic .NET
- V (Vlang)
- Wren
- Wren-fmt
- Wren-iterate
- Xojo
- XPL0
- Zkl
- ZX Spectrum Basic
- Pages with too many expensive parser function calls