ISBN13 check digit: Difference between revisions
Langurmonkey (talk | contribs) |
(Added OmniMark solution, which includes additional conditions to ignore non-ISBN-13 numbers) |
||
(123 intermediate revisions by 53 users not shown) | |||
Line 1: | Line 1: | ||
{{ |
{{task}} |
||
;Task: |
;Task: |
||
Validate the check digit of an ISBN-13 code |
Validate the check digit of an ISBN-13 code: |
||
::* Multiply every other digit by '''3'''. |
|||
Multiply every other digit by 3. Add the digits together. Take the remainder of division by 10. If it is 0, the ISBN-13 check digit is correct. |
|||
::* Add these numbers and the other digits. |
|||
::* Take the remainder of this number after division by '''10'''. |
|||
::* If it is '''0''', the ISBN-13 check digit is correct. |
|||
You might use the following codes for testing: |
|||
::::* 978-0596528126 (good) |
|||
::::* 978-0596528120 (bad) |
|||
::::* 978-1788399081 (good) |
|||
::::* 978-1788399083 (bad) |
|||
Use the following codes for testing: |
|||
<pre>978-1734314502: good |
|||
978-1734314509: bad |
|||
978-1788399081: good |
|||
978-1788399083: bad</pre> |
|||
Show output here, on this page |
Show output here, on this page |
||
;See also: |
|||
<p>See https://isbn-information.com/the-13-digit-isbn.html for details on the method of validation. |
|||
;See also: |
|||
:* for details: [https://isbn-information.com/the-13-digit-isbn.html 13-digit ISBN method of validation]. (installs cookies.) |
|||
<br><br> |
<br><br> |
||
=={{header|11l}}== |
|||
{{trans|Python}} |
|||
<syntaxhighlight lang="11l">F is_isbn13(=n) |
|||
n = n.replace(‘-’, ‘’).replace(‘ ’, ‘’) |
|||
I n.len != 13 |
|||
R 0B |
|||
V product = sum(n[(0..).step(2)].map(ch -> Int(ch))) |
|||
+ sum(n[(1..).step(2)].map(ch -> Int(ch) * 3)) |
|||
R product % 10 == 0 |
|||
V tests = |‘978-0596528126 |
|||
978-0596528120 |
|||
978-1788399081 |
|||
978-1788399083’.split("\n") |
|||
L(t) tests |
|||
print(‘ISBN13 ’t‘ validates ’is_isbn13(t))</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
ISBN13 978-0596528126 validates 1B |
|||
ISBN13 978-0596528120 validates 0B |
|||
ISBN13 978-1788399081 validates 1B |
|||
ISBN13 978-1788399083 validates 0B |
|||
</pre> |
|||
=={{header|8080 Assembly}}== |
|||
<syntaxhighlight lang="8080asm"> org 100h |
|||
jmp demo |
|||
;;; --------------------------------------------------------------- |
|||
;;; Check if the string at BC is a valid ISBN-13 code. |
|||
;;; Carry set if true, clear if not. |
|||
isbn13: lxi h,0 ; HL = accumulator |
|||
mov d,h ; D = 0 (such that if E=A, DE=A). |
|||
call isbngc ; Get first character |
|||
rnc ; Carry clear = invalid |
|||
dad d ; Add to running total once |
|||
call isbngc ; Get second character |
|||
rnc ; Carry clear = invalid |
|||
dad d ; Add to running total thrice |
|||
dad d |
|||
dad d |
|||
call isbngc ; Get third character |
|||
rnc ; Carry clear = invalid |
|||
dad d ; Add to running total once |
|||
ldax b ; Fourth character should be a dash '-' |
|||
inx b |
|||
cpi '-' |
|||
stc ; Clear carry w/o touching other flags |
|||
cmc |
|||
rnz ; If not equal, invalid. |
|||
push h ; Keep loop counter on stack |
|||
mvi l,5 ; 5 times 2 characters |
|||
isbnlp: xthl ; Accumulator in HL |
|||
call isbngc ; Get even character |
|||
jnc isbnex ; If invalid, stop |
|||
dad d ; Add to running total thrice |
|||
dad d |
|||
dad d |
|||
call isbngc ; Get odd character |
|||
jnc isbnex ; If invalid, stop |
|||
dad d ; Add to running total once |
|||
xthl ; Loop counter in (H)L |
|||
dcr l ; Done yet? |
|||
jnz isbnlp ; If not, do next two characters |
|||
pop h ; Get accumulator |
|||
lxi d,-10 ; Trial division by ten |
|||
isbndv: dad d ; Subtract 10 |
|||
jc isbndv ; Until zero passed |
|||
mov a,l ; Move low byte to A |
|||
adi 10 ; Add ten back (the mod loop went one step too far) |
|||
rz ; If zero, return (carry will have been set) |
|||
ana a ; Otherwise, make sure carry is clear |
|||
ret ; And then return |
|||
isbnex: pop h ; Test failed - throw away accumulator and return |
|||
ret |
|||
isbngc: ldax b ; Get character from [BC] |
|||
inx b ; Increment BC |
|||
sui '0' ; Subtract ASCII '0' to get digit value |
|||
cpi 10 ; If 10 or higher (unsigned), invalid digit. |
|||
mov e,a ; Set (D)E = value |
|||
ret |
|||
;;; --------------------------------------------------------------- |
|||
;;; Demo: see if the CP/M command line contains a valid ISBN13 |
|||
;;; code. |
|||
demo: lxi b,82h ; Start of command line argument, skipping first space |
|||
call isbn13 ; Is it valid? |
|||
mvi c,9 ; CP/M print string |
|||
lxi d,good ; If carry is set, then yes |
|||
jc 5 |
|||
lxi d,bad ; Otherwise, no. |
|||
jmp 5 |
|||
good: db 'good$' |
|||
bad: db 'bad$'</syntaxhighlight> |
|||
{{out}} |
|||
<pre>A>isbn13 978-0596528126 |
|||
good |
|||
A>isbn13 978-0596528120 |
|||
bad |
|||
A>isbn13 978-1788399081 |
|||
good |
|||
A>isbn13 978-1788399083 |
|||
bad</pre> |
|||
=={{header|8086 Assembly}}== |
|||
<syntaxhighlight lang="asm"> cpu 8086 |
|||
bits 16 |
|||
org 100h |
|||
section .text |
|||
jmp demo |
|||
isbn13: ;;; --------------------------------------------------------------- |
|||
;;; Check if the string at DS:SI is a valid ISBN-13 code. |
|||
;;; Carry set if true, clear if false. |
|||
xor dx,dx ; DX = running total |
|||
xor ah,ah ; Set AH=0 so that AX=AL |
|||
call .digit ; Get first digit and add to DX |
|||
call .digit ; Get second digit and add to DX |
|||
add dx,ax ; Add to DX twice more |
|||
add dx,ax |
|||
call .digit ; Get third digit and add to DX |
|||
lodsb ; Fourth character should be a '-' |
|||
cmp al,'-' |
|||
jne .fail ; If not equal, fail |
|||
mov cx,5 ; Then loop 5 times for the next 10 digits |
|||
.loop: call .digit ; Get even digit and add to DX |
|||
add dx,ax ; Add to running total twice more |
|||
add dx,ax |
|||
call .digit ; Get odd digit and add to DX |
|||
loop .loop ; Do this 5 times |
|||
mov ax,dx ; Divide running total by 10 |
|||
mov dl,10 |
|||
div dl |
|||
test ah,ah ; Is the remainder zero? |
|||
jnz .out ; If not, stop (TEST clears carry) |
|||
stc ; Otherwise, set carry and return |
|||
ret |
|||
.digit: lodsb ; Get first character |
|||
sub al,'0' ; Subtract ASCII 0 to get digit value |
|||
cmp al,9 |
|||
ja .dfail |
|||
add dx,ax ; Add to ASCII |
|||
ret |
|||
.dfail: pop dx ; Remove return pointer for .digit from stack |
|||
.fail: clc ; Failure - clear carry |
|||
.out: ret |
|||
;;; --------------------------------------------------------------- |
|||
;;; Demo: see if the MS-DOS command line contains a valid ISBN13 |
|||
;;; code. |
|||
demo: mov si,82h ; Start of command line argument skipping space |
|||
call isbn13 ; Is it valid? |
|||
mov ah,9 ; MS-DOS print string |
|||
mov dx,good ; If carry is set, it is good |
|||
jc .print |
|||
mov dx,bad ; Otherwise, it is bad |
|||
.print: int 21h |
|||
ret |
|||
section .data |
|||
good: db 'good$' |
|||
bad: db 'bad$'</syntaxhighlight> |
|||
{{out}} |
|||
<pre>C:\>isbn13 978-0596528126 |
|||
good |
|||
C:\>isbn13 978-0596528120 |
|||
bad |
|||
C:\>isbn13 978-1788399081 |
|||
good |
|||
C:\>isbn13 978-1788399083 |
|||
bad</pre> |
|||
=={{header|ABC}}== |
|||
<syntaxhighlight lang="abc">HOW TO REPORT valid.isbn13 str: |
|||
PUT {} IN digits |
|||
FOR d IN {0..9}: PUT d IN digits["`d`"] |
|||
IF #str <> 14 OR str item 4 <> '-': FAIL |
|||
PUT 1, 0 IN mul, sum |
|||
FOR c IN str|3 ^ str@5: |
|||
IF c not.in keys digits: FAIL |
|||
PUT sum + digits[c] * mul IN sum |
|||
PUT 4 - mul IN mul |
|||
REPORT sum mod 10 = 0 |
|||
PUT {} IN tests |
|||
PUT "978-0596528126" IN tests[1] |
|||
PUT "978-0596528120" IN tests[2] |
|||
PUT "978-1788399081" IN tests[3] |
|||
PUT "978-1788399083" IN tests[4] |
|||
FOR test IN tests: |
|||
SELECT: |
|||
valid.isbn13 test: WRITE test^": good"/ |
|||
ELSE: WRITE test^": bad"/</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126: good |
|||
978-0596528120: bad |
|||
978-1788399081: good |
|||
978-1788399083: bad</pre> |
|||
=={{header|Action!}}== |
|||
{{libheader|Action! Tool Kit}} |
|||
<syntaxhighlight lang="action!">INCLUDE "D2:CHARTEST.ACT" ;from the Action! Tool Kit |
|||
BYTE FUNC CheckISBN13(CHAR ARRAY t) |
|||
BYTE i,index,sum,v |
|||
sum=0 index=0 |
|||
FOR i=1 TO t(0) |
|||
DO |
|||
v=t(i) |
|||
IF IsDigit(v) THEN |
|||
v==-'0 |
|||
IF index MOD 2=1 THEN |
|||
v==*3 |
|||
FI |
|||
sum==+v |
|||
index==+1 |
|||
ELSEIF v#' AND v#'- THEN |
|||
RETURN (0) |
|||
FI |
|||
OD |
|||
IF index#13 OR sum MOD 10#0 THEN |
|||
RETURN (0) |
|||
FI |
|||
RETURN (1) |
|||
PROC Test(CHAR ARRAY t) |
|||
BYTE correct |
|||
correct=CheckISBN13(t) |
|||
Print(t) Print(" is ") |
|||
IF correct THEN |
|||
PrintE("correct") |
|||
ELSE |
|||
PrintE("incorrect") |
|||
FI |
|||
RETURN |
|||
PROC Main() |
|||
Put(125) PutE() ;clear screen |
|||
Test("978-0596528126") |
|||
Test("978-0596528120") |
|||
Test("978-1788399081") |
|||
Test("978-1788399083") |
|||
RETURN</syntaxhighlight> |
|||
{{out}} |
|||
[https://gitlab.com/amarok8bit/action-rosetta-code/-/raw/master/images/ISBN13_check_digit.png Screenshot from Atari 8-bit computer] |
|||
<pre> |
|||
978-0596528126 is correct |
|||
978-0596528120 is incorrect |
|||
978-1788399081 is correct |
|||
978-1788399083 is incorrect |
|||
</pre> |
|||
=={{header|Ada}}== |
|||
<syntaxhighlight lang="ada">with Ada.Text_IO; |
|||
procedure ISBN_Check is |
|||
function Is_Valid (ISBN : String) return Boolean is |
|||
Odd : Boolean := True; |
|||
Sum : Integer := 0; |
|||
Value : Integer; |
|||
begin |
|||
for I in ISBN'Range loop |
|||
if ISBN (I) in '0' .. '9' then |
|||
Value := Character'Pos (ISBN (I)) - Character'Pos ('0'); |
|||
if Odd then |
|||
Sum := Sum + Value; |
|||
else |
|||
Sum := Sum + 3 * Value; |
|||
end if; |
|||
Odd := not Odd; |
|||
end if; |
|||
end loop; |
|||
return Sum mod 10 = 0; |
|||
end Is_Valid; |
|||
procedure Show (ISBN : String) is |
|||
use Ada.Text_IO; |
|||
Valid : constant Boolean := Is_Valid (ISBN); |
|||
begin |
|||
Put (ISBN); Put (" "); |
|||
Put ((if Valid then "Good" else "Bad")); |
|||
New_Line; |
|||
end Show; |
|||
begin |
|||
Show ("978-0596528126"); |
|||
Show ("978-0596528120"); |
|||
Show ("978-1788399081"); |
|||
Show ("978-1788399083"); |
|||
end ISBN_Check;</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126 Good |
|||
978-0596528120 Bad |
|||
978-1788399081 Good |
|||
978-1788399083 Bad</pre> |
|||
=={{header|ALGOL 68}}== |
|||
{{works with|ALGOL 68G|Any - tested with release 2.8.3.win32}} |
|||
<syntaxhighlight lang="algol68">BEGIN # Check some IsBN13 check digits # |
|||
# returns TRUE if the alledged isbn13 has the correct check sum, # |
|||
# FALSE otherwise # |
|||
# non-digit characters are ignored and there must be 13 digits # |
|||
PROC check isbn13 = ( STRING isbn13 )BOOL: |
|||
BEGIN |
|||
INT sum := 0; |
|||
INT digits := 0; |
|||
BOOL other digit := FALSE; |
|||
FOR pos FROM LWB isbn13 TO UPB isbn13 DO |
|||
IF CHAR c = isbn13[ pos ]; |
|||
c >= "0" AND c <= "9" |
|||
THEN |
|||
# have another digit # |
|||
digits +:= 1; |
|||
sum +:= ( ABS c - ABS "0" ) * IF other digit THEN 3 ELSE 1 FI; |
|||
other digit := NOT other digit |
|||
FI |
|||
OD; |
|||
digits = 13 AND sum MOD 10 = 0 |
|||
END; # check isbn13 # |
|||
# task test cases # |
|||
[]STRING tests = ( "978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083" ); |
|||
[]BOOL expected = ( TRUE, FALSE, TRUE, FALSE ); |
|||
FOR pos FROM LWB tests TO UPB tests DO |
|||
BOOL result = check isbn13( tests[ pos ] ); |
|||
print( ( tests[ pos ] |
|||
, ": " |
|||
, IF result THEN "good" ELSE "bad" FI |
|||
, IF result = expected[ pos ] THEN "" ELSE " NOT AS EXPECTED" FI |
|||
, newline |
|||
) |
|||
) |
|||
OD |
|||
END</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
978-0596528126: good |
|||
978-0596528120: bad |
|||
978-1788399081: good |
|||
978-1788399083: bad |
|||
</pre> |
|||
=={{header|APL}}== |
|||
{{works with|Dyalog APL}} |
|||
<syntaxhighlight lang="apl">check_isbn13←{ |
|||
13≠⍴n←(⍵∊⎕D)/⍵:0 |
|||
0=10|(⍎¨n)+.×13⍴1 3 |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> check_isbn13¨ '978-0596528126' '978-0596528120' '978-1788399081' '978-1788399083' |
|||
1 0 1 0</pre> |
|||
=={{header|AppleScript}}== |
=={{header|AppleScript}}== |
||
===Composition of pure functions=== |
===Composition of pure functions=== |
||
< |
<syntaxhighlight lang="applescript">-------------------- ISBN13 CHECK DIGIT -------------------- |
||
-- isISBN13 :: String -> Bool |
|||
on isISBN13(s) |
on isISBN13(s) |
||
script digitValue |
script digitValue |
||
Line 38: | Line 407: | ||
---------------------------TEST |
--------------------------- TEST --------------------------- |
||
on run |
on run |
||
script test |
script test |
||
Line 46: | Line 415: | ||
end script |
end script |
||
map(test, {"978- |
map(test, {"978-0596528126", "978-0596528120", ¬ |
||
"978-1788399081", "978-1788399083"}) |
"978-1788399081", "978-1788399083"}) |
||
end run |
end run |
||
-------------------- |
-------------------- GENERIC FUNCTIONS --------------------- |
||
-- concatMap :: (a -> [b]) -> [a] -> [b] |
-- concatMap :: (a -> [b]) -> [a] -> [b] |
||
Line 64: | Line 433: | ||
return acc |
return acc |
||
end concatMap |
end concatMap |
||
-- cycle :: [a] -> Generator [a] |
-- cycle :: [a] -> Generator [a] |
||
Line 85: | Line 455: | ||
end script |
end script |
||
end cycle |
end cycle |
||
-- foldl :: (a -> b -> a) -> a -> [b] -> a |
-- foldl :: (a -> b -> a) -> a -> [b] -> a |
||
Line 97: | Line 468: | ||
end tell |
end tell |
||
end foldl |
end foldl |
||
-- isDigit :: Char -> Bool |
-- isDigit :: Char -> Bool |
||
Line 103: | Line 475: | ||
48 ≤ n and 57 ≥ n |
48 ≤ n and 57 ≥ n |
||
end isDigit |
end isDigit |
||
-- length :: [a] -> Int |
-- length :: [a] -> Int |
||
Line 113: | Line 486: | ||
end if |
end if |
||
end |length| |
end |length| |
||
-- map :: (a -> b) -> [a] -> [b] |
-- map :: (a -> b) -> [a] -> [b] |
||
Line 127: | Line 501: | ||
end tell |
end tell |
||
end map |
end map |
||
-- min :: Ord a => a -> a -> a |
-- min :: Ord a => a -> a -> a |
||
Line 136: | Line 511: | ||
end if |
end if |
||
end min |
end min |
||
-- mReturn :: First-class m => (a -> b) -> m (a -> b) |
-- mReturn :: First-class m => (a -> b) -> m (a -> b) |
||
Line 148: | Line 524: | ||
end if |
end if |
||
end mReturn |
end mReturn |
||
-- mul (*) :: Num a => a -> a -> a |
-- mul (*) :: Num a => a -> a -> a |
||
Line 153: | Line 530: | ||
a * b |
a * b |
||
end mul |
end mul |
||
-- sum :: [Num] -> Num |
-- sum :: [Num] -> Num |
||
Line 164: | Line 542: | ||
foldl(add, 0, xs) |
foldl(add, 0, xs) |
||
end sum |
end sum |
||
-- take :: Int -> [a] -> [a] |
-- take :: Int -> [a] -> [a] |
||
Line 196: | Line 575: | ||
end if |
end if |
||
end take |
end take |
||
-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] |
-- zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] |
||
Line 210: | Line 590: | ||
return lst |
return lst |
||
end tell |
end tell |
||
end zipWith</ |
end zipWith</syntaxhighlight> |
||
{{Out}} |
{{Out}} |
||
<pre>{{"978- |
<pre>{{"978-0596528126", true}, {"978-0596528120", false}, {"978-1788399081", true}, {"978-1788399083", false}}</pre> |
||
===Straightforward=== |
===Straightforward=== |
||
Line 218: | Line 598: | ||
This task ''can'' be tackled very simply by working through the numeric text two characters at a time: |
This task ''can'' be tackled very simply by working through the numeric text two characters at a time: |
||
< |
<syntaxhighlight lang="applescript">on validateISBN13(ISBN13) |
||
if (ISBN13's class is not text) then return false |
if (ISBN13's class is not text) then return false |
||
Line 246: | Line 626: | ||
set output to {} |
set output to {} |
||
set verdicts to {"bad", "good"} |
set verdicts to {"bad", "good"} |
||
repeat with thisISBN13 in {"978- |
repeat with thisISBN13 in {"978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"} |
||
set isValid to validateISBN13(thisISBN13) |
set isValid to validateISBN13(thisISBN13) |
||
set end of output to thisISBN13 & ": " & item ((isValid as integer) + 1) of verdicts |
set end of output to thisISBN13 & ": " & item ((isValid as integer) + 1) of verdicts |
||
Line 255: | Line 635: | ||
set output to output as text |
set output to output as text |
||
set AppleScript's text item delimiters to astid |
set AppleScript's text item delimiters to astid |
||
return output</ |
return output</syntaxhighlight> |
||
{{output}} |
{{output}} |
||
<pre>"978- |
<pre>"978-0596528126: good |
||
978- |
978-0596528120: bad |
||
978-1788399081: good |
978-1788399081: good |
||
978-1788399083: bad"</pre> |
978-1788399083: bad"</pre> |
||
Line 265: | Line 645: | ||
Or it can be handled purely numerically. Since the "weights" alternate and are palindromic, it makes no difference whether the last digit or the first is treated as the check digit. In fact, if preferred, the repeat below can go round 7 times with the <tt>return</tt> line as simply: <tt>return (sum mod 10 = 0)</tt>. |
Or it can be handled purely numerically. Since the "weights" alternate and are palindromic, it makes no difference whether the last digit or the first is treated as the check digit. In fact, if preferred, the repeat below can go round 7 times with the <tt>return</tt> line as simply: <tt>return (sum mod 10 = 0)</tt>. |
||
< |
<syntaxhighlight lang="applescript">on validateISBN13(ISBN13) |
||
if (ISBN13's class is not text) then return false |
if (ISBN13's class is not text) then return false |
||
Line 289: | Line 669: | ||
return ((sum + ISBN13) mod 10 = 0) |
return ((sum + ISBN13) mod 10 = 0) |
||
end validateISBN13</ |
end validateISBN13</syntaxhighlight> |
||
=={{header|Arturo}}== |
|||
<syntaxhighlight lang="rebol">validISBN?: function [isbn][ |
|||
currentCheck: to :integer to :string last isbn |
|||
isbn: map split chop replace isbn "-" "" 'd -> to :integer d |
|||
s: 0 |
|||
loop.with:'i isbn 'n [ |
|||
if? even? i -> s: s + n |
|||
else -> s: s + 3*n |
|||
] |
|||
checkDigit: 10 - s % 10 |
|||
return currentCheck = checkDigit |
|||
] |
|||
tests: [ |
|||
"978-0596528126" "978-0596528120" |
|||
"978-1788399081" "978-1788399083" |
|||
] |
|||
loop tests 'test [ |
|||
print [test "=>" validISBN? test] |
|||
]</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126 => true |
|||
978-0596528120 => false |
|||
978-1788399081 => true |
|||
978-1788399083 => false</pre> |
|||
=={{header|AutoHotkey}}== |
|||
<syntaxhighlight lang="autohotkey">ISBN13_check_digit(n){ |
|||
for i, v in StrSplit(RegExReplace(n, "[^0-9]")) |
|||
sum += !Mod(i, 2) ? v*3 : v |
|||
return n "`t" (Mod(sum, 10) ? "(bad)" : "(good)") |
|||
}</syntaxhighlight> |
|||
Examples:<syntaxhighlight lang="autohotkey">output := "" |
|||
nums := ["978-0596528126","978-0596528120","978-1788399081","978-1788399083"] |
|||
for i, n in nums |
|||
output .= ISBN13_check_digit(n) "`n" |
|||
MsgBox % output |
|||
return</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126 (good) |
|||
978-0596528120 (bad) |
|||
978-1788399081 (good) |
|||
978-1788399083 (bad)</pre> |
|||
=={{header|AWK}}== |
=={{header|AWK}}== |
||
<syntaxhighlight lang="awk"> |
|||
<lang AWK> |
|||
# syntax: GAWK -f ISBN13_CHECK_DIGIT.AWK |
# syntax: GAWK -f ISBN13_CHECK_DIGIT.AWK |
||
BEGIN { |
BEGIN { |
||
arr[++n] = "978- |
arr[++n] = "978-0596528126" |
||
arr[++n] = "978- |
arr[++n] = "978-0596528120" |
||
arr[++n] = "978-1788399081" |
arr[++n] = "978-1788399081" |
||
arr[++n] = "978-1788399083" |
arr[++n] = "978-1788399083" |
||
Line 315: | Line 744: | ||
return(substr(isbn,13,1) == check_digit ? "OK" : sprintf("NG check digit S/B %d",check_digit)) |
return(substr(isbn,13,1) == check_digit ? "OK" : sprintf("NG check digit S/B %d",check_digit)) |
||
} |
} |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
978- |
978-0596528126 OK |
||
978- |
978-0596528120 NG check digit S/B 2 |
||
978-1788399081 OK |
978-1788399081 OK |
||
978-1788399083 NG check digit S/B 1 |
978-1788399083 NG check digit S/B 1 |
||
Line 325: | Line 754: | ||
0820424528 NG length |
0820424528 NG length |
||
</pre> |
</pre> |
||
=={{header|BASIC256}}== |
|||
{{trans|Ring}} |
|||
<syntaxhighlight lang="vb">arraybase 1 |
|||
dim isbn = {"978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083", "978-2-74839-908-0", "978-2-74839-908-5", "978 1 86197 876 9"} |
|||
for n = 1 to isbn[?] |
|||
sum = 0 |
|||
isbnStr = isbn[n] |
|||
isbnStr = replace(isbnStr, "-", "") |
|||
isbnStr = replace(isbnStr, " ", "") |
|||
for m = 1 to length(isbnStr) |
|||
if m mod 2 = 0 then |
|||
num = 3 * int(mid(isbnStr, m, 1)) |
|||
else |
|||
num = int(mid(isbnStr, m, 1)) |
|||
end if |
|||
sum += num |
|||
next m |
|||
if sum mod 10 = 0 then |
|||
print isbn[n]; ": good" |
|||
else |
|||
print isbn[n]; ": bad" |
|||
end if |
|||
next n</syntaxhighlight> |
|||
=={{header|BCPL}}== |
|||
<syntaxhighlight lang="bcpl">get "libhdr" |
|||
let checkISBN(s) = valof |
|||
$( let tally = 0 |
|||
unless s%0 = 14 resultis false |
|||
unless s%4 = '-' resultis false |
|||
for i=1 to 3 |
|||
$( let digit = s%i-'0' |
|||
test i rem 2 = 0 |
|||
do tally := tally + 3 * digit |
|||
or tally := tally + digit |
|||
$) |
|||
for i=5 to 14 |
|||
$( let digit = s%i-'0' |
|||
test i rem 2 = 0 |
|||
do tally := tally + digit |
|||
or tally := tally + 3 * digit |
|||
$) |
|||
resultis tally rem 10 = 0 |
|||
$) |
|||
let show(s) be |
|||
writef("%S: %S*N", s, checkISBN(s) -> "good", "bad") |
|||
let start() be |
|||
$( show("978-0596528126") |
|||
show("978-0596528120") |
|||
show("978-1788399081") |
|||
show("978-1788399083") |
|||
$)</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126: good |
|||
978-0596528120: bad |
|||
978-1788399081: good |
|||
978-1788399083: bad</pre> |
|||
=={{header|C}}== |
=={{header|C}}== |
||
< |
<syntaxhighlight lang="c">#include <stdio.h> |
||
int check_isbn13(const char *isbn) { |
int check_isbn13(const char *isbn) { |
||
Line 353: | Line 847: | ||
int main() { |
int main() { |
||
int i; |
int i; |
||
const char* isbns[] = {"978- |
const char* isbns[] = {"978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"}; |
||
for (i = 0; i < 4; ++i) { |
for (i = 0; i < 4; ++i) { |
||
printf("%s: %s\n", isbns[i], check_isbn13(isbns[i]) ? "good" : "bad"); |
printf("%s: %s\n", isbns[i], check_isbn13(isbns[i]) ? "good" : "bad"); |
||
} |
} |
||
return 0; |
return 0; |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
978- |
978-0596528126: good |
||
978- |
978-0596528120: bad |
||
978-1788399081: good |
978-1788399081: good |
||
978-1788399083: bad |
978-1788399083: bad |
||
</pre> |
</pre> |
||
=={{header|C++}}== |
|||
{{trans|C}} |
|||
<syntaxhighlight lang="cpp">#include <iostream> |
|||
bool check_isbn13(std::string isbn) { |
|||
int count = 0; |
|||
int sum = 0; |
|||
for (auto ch : isbn) { |
|||
/* skip hyphens or spaces */ |
|||
if (ch == ' ' || ch == '-') { |
|||
continue; |
|||
} |
|||
if (ch < '0' || ch > '9') { |
|||
return false; |
|||
} |
|||
if (count & 1) { |
|||
sum += 3 * (ch - '0'); |
|||
} else { |
|||
sum += ch - '0'; |
|||
} |
|||
count++; |
|||
} |
|||
if (count != 13) { |
|||
return false; |
|||
} |
|||
return sum % 10 == 0; |
|||
} |
|||
int main() { |
|||
auto isbns = { "978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083" }; |
|||
for (auto isbn : isbns) { |
|||
std::cout << isbn << ": " << (check_isbn13(isbn) ? "good" : "bad") << '\n'; |
|||
} |
|||
return 0; |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126: good |
|||
978-0596528120: bad |
|||
978-1788399081: good |
|||
978-1788399083: bad</pre> |
|||
=={{header|C sharp}}== |
|||
<syntaxhighlight lang="csharp">using System; |
|||
using System.Linq; |
|||
public class Program |
|||
{ |
|||
public static void Main() { |
|||
Console.WriteLine(CheckISBN13("978-0596528126")); |
|||
Console.WriteLine(CheckISBN13("978-0596528120")); |
|||
Console.WriteLine(CheckISBN13("978-1788399081")); |
|||
Console.WriteLine(CheckISBN13("978-1788399083")); |
|||
static bool CheckISBN13(string code) { |
|||
code = code.Replace("-", "").Replace(" ", ""); |
|||
if (code.Length != 13) return false; |
|||
int sum = 0; |
|||
foreach (var (index, digit) in code.Select((digit, index) => (index, digit))) { |
|||
if (char.IsDigit(digit)) sum += (digit - '0') * (index % 2 == 0 ? 1 : 3); |
|||
else return false; |
|||
} |
|||
return sum % 10 == 0; |
|||
} |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
True |
|||
False |
|||
True |
|||
False</pre> |
|||
=={{header|CLU}}== |
|||
<syntaxhighlight lang="clu">isbn13_check = proc (s: string) returns (bool) |
|||
if string$size(s) ~= 14 then return(false) end |
|||
if s[4] ~= '-' then return(false) end |
|||
begin |
|||
check: int := 0 |
|||
for i: int in int$from_to(1, 14) do |
|||
if i=4 then continue end |
|||
d: int := int$parse(string$c2s(s[i])) |
|||
if i=2 cor (i>4 cand i//2=1) then d := d*3 end |
|||
check := check + d |
|||
end |
|||
return(check//10 = 0) |
|||
end except when bad_format: |
|||
return(false) |
|||
end |
|||
end isbn13_check |
|||
start_up = proc () |
|||
po: stream := stream$primary_output() |
|||
tests: array[string] := array[string]$ |
|||
["978-0596528126", |
|||
"978-0596528120", |
|||
"978-1788399081", |
|||
"978-1788399083"] |
|||
for test: string in array[string]$elements(tests) do |
|||
stream$puts(po, test) |
|||
stream$puts(po, ": ") |
|||
if isbn13_check(test) |
|||
then stream$putl(po, "good") |
|||
else stream$putl(po, "bad") |
|||
end |
|||
end |
|||
end start_up</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126: good |
|||
978-0596528120: bad |
|||
978-1788399081: good |
|||
978-1788399083: bad</pre> |
|||
=={{header|COBOL}}== |
|||
{{works with|GnuCOBOL}} |
|||
<syntaxhighlight lang="cobol"> ****************************************************************** |
|||
* Author: Jay Moseley |
|||
* Date: November 10, 2019 |
|||
* Purpose: Testing various subprograms/ functions. |
|||
* Tectonics: cobc -xj testSubs.cbl |
|||
****************************************************************** |
|||
IDENTIFICATION DIVISION. |
|||
PROGRAM-ID. testSubs. |
|||
ENVIRONMENT DIVISION. |
|||
CONFIGURATION SECTION. |
|||
REPOSITORY. |
|||
FUNCTION ALL INTRINSIC |
|||
FUNCTION validISBN13. |
|||
INPUT-OUTPUT SECTION. |
|||
FILE-CONTROL. |
|||
DATA DIVISION. |
|||
FILE SECTION. |
|||
WORKING-STORAGE SECTION. |
|||
01 IX PIC S9(4) COMP. |
|||
01 TEST-ISBNS. |
|||
02 FILLER PIC X(14) VALUE '978-0596528126'. |
|||
02 FILLER PIC X(14) VALUE '978-0596528120'. |
|||
02 FILLER PIC X(14) VALUE '978-1788399081'. |
|||
02 FILLER PIC X(14) VALUE '978-1788399083'. |
|||
01 TEST-ISBN REDEFINES TEST-ISBNS |
|||
OCCURS 4 TIMES |
|||
PIC X(14). |
|||
PROCEDURE DIVISION. |
|||
MAIN-PROCEDURE. |
|||
PERFORM |
|||
VARYING IX |
|||
FROM 1 |
|||
BY 1 |
|||
UNTIL IX > 4 |
|||
DISPLAY TEST-ISBN (IX) ' ' WITH NO ADVANCING |
|||
END-DISPLAY |
|||
IF validISBN13(TEST-ISBN (IX)) = -1 |
|||
DISPLAY '(bad)' |
|||
ELSE |
|||
DISPLAY '(good)' |
|||
END-IF |
|||
END-PERFORM. |
|||
GOBACK. |
|||
END PROGRAM testSubs. |
|||
****************************************************************** |
|||
* Author: Jay Moseley |
|||
* Date: May 19, 2016 |
|||
* Purpose: validate ISBN-13 (International Standard |
|||
* Book Number). |
|||
****************************************************************** |
|||
IDENTIFICATION DIVISION. |
|||
FUNCTION-ID. validISBN13. |
|||
ENVIRONMENT DIVISION. |
|||
CONFIGURATION SECTION. |
|||
REPOSITORY. |
|||
FUNCTION ALL INTRINSIC. |
|||
INPUT-OUTPUT SECTION. |
|||
FILE-CONTROL. |
|||
DATA DIVISION. |
|||
FILE SECTION. |
|||
WORKING-STORAGE SECTION. |
|||
01 PASSED-SIZE PIC S9(6) COMP-5. |
|||
01 IX PIC S9(4) COMP. |
|||
01 WORK-FIELDS. |
|||
02 WF-DIGIT PIC X. |
|||
02 WF-COUNT PIC 9(2). |
|||
88 WEIGHT-1 VALUE 1, 3, 5, 7, 9, 11, 13. |
|||
88 WEIGHT-3 VALUE 2, 4, 6, 8, 10, 12. |
|||
02 WF-SUM PIC S9(8) COMP. |
|||
LINKAGE SECTION. |
|||
01 PASSED-ISBN PIC X ANY LENGTH. |
|||
01 RETURN-VALUE PIC S9. |
|||
PROCEDURE DIVISION USING PASSED-ISBN |
|||
RETURNING RETURN-VALUE. |
|||
CALL 'C$PARAMSIZE' |
|||
USING 1 |
|||
GIVING PASSED-SIZE |
|||
END-CALL. |
|||
COMPUTE-CKDIGIT. |
|||
INITIALIZE WORK-FIELDS. |
|||
PERFORM |
|||
VARYING IX |
|||
FROM 1 |
|||
BY 1 |
|||
UNTIL IX GREATER THAN PASSED-SIZE |
|||
MOVE PASSED-ISBN (IX:1) TO WF-DIGIT |
|||
IF WF-DIGIT IS NUMERIC |
|||
ADD 1 TO WF-COUNT |
|||
IF WEIGHT-1 |
|||
ADD NUMVAL(WF-DIGIT) TO WF-SUM |
|||
ELSE |
|||
COMPUTE WF-SUM = WF-SUM + |
|||
(NUMVAL(WF-DIGIT) * 3) |
|||
END-COMPUTE |
|||
END-IF |
|||
END-IF |
|||
END-PERFORM. |
|||
IF MOD(WF-SUM, 10) = 0 |
|||
MOVE +0 TO RETURN-VALUE |
|||
ELSE |
|||
MOVE -1 TO RETURN-VALUE |
|||
END-IF. |
|||
GOBACK. |
|||
* - - - - - - - - - - - - - - - - - - - - - - PROGRAM EXIT POINT |
|||
END FUNCTION validISBN13. |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126 (good) |
|||
978-0596528120 (bad) |
|||
978-1788399081 (good) |
|||
978-1788399083 (bad) |
|||
</pre> |
|||
=={{header|Cowgol}}== |
|||
<syntaxhighlight lang="cowgol">include "cowgol.coh"; |
|||
sub check_isbn13(isbn: [uint8]): (r: uint8) is |
|||
var n: uint8 := 0; |
|||
r := 0; |
|||
loop |
|||
var c := [isbn]; |
|||
isbn := @next isbn; |
|||
if c == 0 then break; end if; |
|||
c := c - '0'; |
|||
if c <= 9 then |
|||
r := r + c; |
|||
n := n + 1; |
|||
if (n & 1) == 0 then |
|||
r := r + c * 2; |
|||
end if; |
|||
end if; |
|||
end loop; |
|||
if n == 13 and r%10 == 0 then |
|||
r := 1; |
|||
else |
|||
r := 0; |
|||
end if; |
|||
end sub; |
|||
var isbns: [uint8][] := { |
|||
"978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083" |
|||
}; |
|||
var result: [uint8][] := {": bad\n", ": good\n"}; |
|||
var n: uint8 := 0; |
|||
while n < @sizeof isbns loop |
|||
print(isbns[n]); |
|||
print(result[check_isbn13(isbns[n])]); |
|||
n := n + 1; |
|||
end loop;</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126: good |
|||
978-0596528120: bad |
|||
978-1788399081: good |
|||
978-1788399083: bad</pre> |
|||
=={{header|D}}== |
|||
{{trans|Kotlin}} |
|||
<syntaxhighlight lang="d">import std.stdio; |
|||
bool isValidISBN13(string text) { |
|||
int sum, i; |
|||
foreach (c; text) { |
|||
if ('0' <= c && c <= '9') { |
|||
int value = c - '0'; |
|||
if (i % 2 == 0) { |
|||
sum += value; |
|||
} else { |
|||
sum += 3 * value; |
|||
} |
|||
i++; |
|||
} |
|||
} |
|||
return i == 13 && 0 == sum % 10; |
|||
} |
|||
unittest { |
|||
assert(isValidISBN13("978-0596528126")); |
|||
assert(!isValidISBN13("978-0596528120")); |
|||
assert(isValidISBN13("978-1788399081")); |
|||
assert(!isValidISBN13("978-1788399083")); |
|||
}</syntaxhighlight> |
|||
=={{header|Delphi}}== |
|||
{{works with|Delphi|6.0}} |
|||
{{libheader|SysUtils,StdCtrls}} |
|||
<syntaxhighlight lang="Delphi"> |
|||
function ValidateISBN(ISBN: string): boolean; |
|||
{Validate an ISBN number} |
|||
var I,N,Sum: integer; |
|||
begin |
|||
Sum:=0; |
|||
{Go througha chars, ignoring non-digits} |
|||
for I:=1 to Length(ISBN) do |
|||
if ISBN[I] in ['0'..'9'] then |
|||
begin |
|||
N:=StrToInt(ISBN[I]); |
|||
{Every other digit multiplied by 3} |
|||
if (I and 1)=1 then N:=N*3; |
|||
{Sum digits} |
|||
Sum:=Sum+N; |
|||
end; |
|||
{The sum must be an even multiple of 10} |
|||
Result:=(Sum mod 10)=0; |
|||
end; |
|||
procedure ValidateAndShow(Memo: TMemo; ISBN: string); |
|||
{Validate ISBN number and show the result} |
|||
var S: string; |
|||
begin |
|||
S:=ISBN; |
|||
if ValidateISBN(ISBN) then S:=S+' (Good)' |
|||
else S:=S+' (Bad)'; |
|||
Memo.Lines.Add(S); |
|||
end; |
|||
procedure TestISBNSet(Memo: TMemo); |
|||
{Test supplied set of ISBN numbers} |
|||
begin |
|||
ValidateAndShow(Memo,'978-0596528126'); //(good) |
|||
ValidateAndShow(Memo,'978-0596528120'); //(bad) |
|||
ValidateAndShow(Memo,'978-1788399081'); //(good) |
|||
ValidateAndShow(Memo,'978-1788399083'); //(bad) |
|||
end; |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
978-0596528126 (Good) |
|||
978-0596528120 (Bad) |
|||
978-1788399081 (Good) |
|||
978-1788399083 (Bad) |
|||
</pre> |
|||
=={{header|Draco}}== |
|||
<syntaxhighlight lang="draco">proc nonrec isbn13_check(*char isbn) bool: |
|||
byte n, check, d; |
|||
char cur; |
|||
bool ok; |
|||
n := 0; |
|||
check := 0; |
|||
ok := true; |
|||
while |
|||
cur := isbn*; |
|||
isbn := isbn + 1; |
|||
ok and cur ~= '\e' |
|||
do |
|||
if n=3 then |
|||
if cur ~= '-' then ok := false fi |
|||
elif cur<'0' or cur>'9' then |
|||
ok := false |
|||
else |
|||
d := cur - '0'; |
|||
if n=1 or (n>3 and n&1 = 0) then |
|||
d := d * 3; |
|||
fi; |
|||
check := check + d |
|||
fi; |
|||
n := n + 1 |
|||
od; |
|||
ok and n = 14 and check%10 = 0 |
|||
corp |
|||
proc nonrec test(*char isbn) void: |
|||
writeln(isbn, ": ", |
|||
if isbn13_check(isbn) then "good" else "bad" fi) |
|||
corp |
|||
proc nonrec main() void: |
|||
test("978-0596528126"); |
|||
test("978-0596528120"); |
|||
test("978-1788399081"); |
|||
test("978-1788399083") |
|||
corp</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126: good |
|||
978-0596528120: bad |
|||
978-1788399081: good |
|||
978-1788399083: bad</pre> |
|||
=={{header|EasyLang}}== |
|||
<syntaxhighlight lang="text"> |
|||
func ISBN13check isbn$ . |
|||
for c$ in strchars isbn$ |
|||
if c$ <> "-" |
|||
ndigs += 1 |
|||
. |
|||
dig = number c$ |
|||
if ndigs mod 2 = 0 |
|||
dig *= 3 |
|||
. |
|||
sum += dig |
|||
. |
|||
if sum mod 10 <> 0 |
|||
return 0 |
|||
. |
|||
return 1 |
|||
. |
|||
codes$[] = [ "978-0596528126" "978-0596528120" "978-1788399081" "978-1788399083" ] |
|||
for code$ in codes$[] |
|||
if ISBN13check code$ = 1 |
|||
print code$ & " is a valid ISBN" |
|||
else |
|||
print code$ & " is not a valid ISBN" |
|||
. |
|||
. |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
978-0596528126 is a valid ISBN |
|||
978-0596528120 is not a valid ISBN |
|||
978-1788399081 is a valid ISBN |
|||
978-1788399083 is not a valid ISBN |
|||
</pre> |
|||
=={{header|Excel}}== |
|||
===LAMBDA=== |
|||
Binding the name '''ISBN13Check''' 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">ISBN13Check |
|||
=LAMBDA(s, |
|||
LET( |
|||
ns, FILTERP( |
|||
LAMBDA(v, |
|||
NOT(ISERROR(v)) |
|||
) |
|||
)( |
|||
VALUE(CHARSROW(s)) |
|||
), |
|||
ixs, SEQUENCE( |
|||
1, COLUMNS(ns), |
|||
1, 1 |
|||
), |
|||
0 = MOD( |
|||
SUM( |
|||
IF(0 <> MOD(ixs, 2), |
|||
INDEX(ns, ixs), |
|||
3 * INDEX(ns, ixs) |
|||
) |
|||
), |
|||
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 |
|||
) |
|||
) |
|||
FILTERP |
|||
=LAMBDA(p, |
|||
LAMBDA(xs, |
|||
FILTER(xs, p(xs)) |
|||
) |
|||
) |
|||
ISDIGIT |
|||
=LAMBDA(c, |
|||
LET( |
|||
ic, CODE(c), |
|||
AND(47 < ic, 58 > ic) |
|||
) |
|||
)</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;"|=ISBN13Check(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" | Candidate string |
|||
| style="font-weight:bold" | ISBN13 checked |
|||
|- |
|||
| style="text-align:center; font-family:Arial, Helvetica, sans-serif !important; background-color:#000000; color:#ffffff" | 2 |
|||
| 978-0596528126 |
|||
| style="background-color:#cbcefb" | TRUE |
|||
|- |
|||
| style="text-align:center; font-family:Arial, Helvetica, sans-serif !important; background-color:#000000; color:#ffffff" | 3 |
|||
| 978-0596528120 |
|||
| FALSE |
|||
|- |
|||
| style="text-align:center; font-family:Arial, Helvetica, sans-serif !important; background-color:#000000; color:#ffffff" | 4 |
|||
| 978-1788399081 |
|||
| TRUE |
|||
|- |
|||
| style="text-align:center; font-family:Arial, Helvetica, sans-serif !important; background-color:#000000; color:#ffffff" | 5 |
|||
| 978-1788399083 |
|||
| FALSE |
|||
|} |
|||
=={{header|Factor}}== |
=={{header|Factor}}== |
||
< |
<syntaxhighlight lang="factor">USING: combinators.short-circuit formatting kernel math |
||
math.functions math.parser math.vectors qw sequences |
math.functions math.parser math.vectors qw sequences |
||
sequences.extras sets unicode ; |
sequences.extras sets unicode ; |
||
Line 381: | Line 1,446: | ||
{ [ length 13 = ] [ [ digit? ] all? ] [ (isbn13?) ] } 1&& ; |
{ [ length 13 = ] [ [ digit? ] all? ] [ (isbn13?) ] } 1&& ; |
||
qw{ 978- |
qw{ 978-0596528126 978-0596528120 978-1788399081 978-1788399083 } |
||
[ dup isbn13? "good" "bad" ? "%s: %s\n" printf ] each</ |
[ dup isbn13? "good" "bad" ? "%s: %s\n" printf ] each</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
978- |
978-0596528126: good |
||
978- |
978-0596528120: bad |
||
978-1788399081: good |
978-1788399081: good |
||
978-1788399083: bad |
978-1788399083: bad |
||
</pre> |
</pre> |
||
=={{header|Forth}}== |
|||
The following word not only identifies correct 13 digit ISBN (and EAN) codes, but also 8 digit EAN and GTIN codes. |
|||
<syntaxhighlight lang="text">: is-digit [char] 0 - 10 u< ; |
|||
: isbn? ( a n -- f) |
|||
1- chars over + 2>r 0 1 2r> ?do |
|||
dup i c@ dup is-digit \ get length and factor, setup loop |
|||
if [char] 0 - * rot + swap 3 * 8 mod else drop drop then |
|||
-1 chars +loop drop 10 mod 0= \ now calculate checksum |
|||
;</syntaxhighlight> |
|||
{{out}} |
|||
In Forth, a "true" value is indicated by "-1". |
|||
<pre> |
|||
s" 978-0596528126" isbn? . -1 ok |
|||
s" 978-0596528120" isbn? . 0 ok |
|||
s" 978-1788399081" isbn? . -1 ok |
|||
s" 978-1788399083" isbn? . 0 ok |
|||
</pre> |
|||
=={{header|Fortran}}== |
|||
<syntaxhighlight lang="fortran"> |
|||
program isbn13 |
|||
implicit none |
|||
character(len=14), dimension(4), parameter :: isbns=["978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"] |
|||
integer :: i |
|||
do i = 1, ubound(isbns, 1) |
|||
if (check_isbn13(isbns(i))) then |
|||
print*, isbns(i), " : ", "good" |
|||
else |
|||
print*, isbns(i), " : ", "bad" |
|||
end if |
|||
end do |
|||
contains |
|||
pure function check_isbn13(isbn) |
|||
character(len=*), intent(in) :: isbn |
|||
logical :: check_isbn13 |
|||
integer :: summ, counter, i, digit |
|||
check_isbn13 = .false. |
|||
counter = 0 |
|||
summ = 0 |
|||
do i = 1, len(isbn) |
|||
if (isbn(i:i) == ' ' .or. isbn(i:i) == '-') cycle |
|||
counter = counter + 1 |
|||
read(isbn(i:i), '(I1)') digit |
|||
if (modulo(counter, 2) == 0) then |
|||
summ = summ + 3*digit |
|||
else |
|||
summ = summ + digit |
|||
end if |
|||
end do |
|||
if (counter == 13 .and. modulo(summ, 10) == 0) check_isbn13 = .true. |
|||
end function check_isbn13 |
|||
end program isbn13 |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
978-0596528126 : good |
|||
978-0596528120 : bad |
|||
978-1788399081 : good |
|||
978-1788399083 : bad |
|||
</pre> |
|||
=={{header|FreeBASIC}}== |
|||
<syntaxhighlight lang="freebasic">#define ZEROC asc("0") |
|||
function is_num( byval c as string ) as boolean |
|||
if asc(c) >= ZEROC andalso asc(c)<ZEROC+10 then return true |
|||
return false |
|||
end function |
|||
function is_good_isbn( isbn as string ) as boolean |
|||
dim as uinteger charno = 0, digitno = 0, sum = 0 |
|||
dim as string*1 currchar |
|||
while charno <= len(isbn) |
|||
currchar = mid(isbn,charno,1) |
|||
if is_num(currchar) then |
|||
if digitno mod 2 = 1 then |
|||
sum += 2*(asc(currchar)-ZEROC) |
|||
end if |
|||
sum += asc(currchar)-ZEROC |
|||
digitno += 1 |
|||
end if |
|||
charno += 1 |
|||
wend |
|||
if sum mod 10 = 0 then |
|||
return true |
|||
else |
|||
return false |
|||
end if |
|||
end function |
|||
dim as string isbns(0 to 3) = { "978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083" } |
|||
dim as uinteger i |
|||
for i = 0 to 3 |
|||
if is_good_isbn( isbns(i) ) then |
|||
print isbns(i)+": good" |
|||
else |
|||
print isbns(i)+": bad" |
|||
end if |
|||
next i</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
978-0596528126: good |
|||
978-0596528120: bad |
|||
978-1788399081: good |
|||
978-1788399083: bad |
|||
</pre> |
|||
=={{header|F_Sharp|F#}}== |
|||
<syntaxhighlight lang="fsharp">// ISBN13 Check Digit |
|||
open System |
|||
let parseInput (input: string) = |
|||
Seq.map(fun c -> c |> string) input |> Seq.toList |> List.map(fun x -> Int32.Parse x) |
|||
[<EntryPoint>] |
|||
let main argv = |
|||
let isbnnum = parseInput (String.filter (fun x -> x <> '-') argv.[0]) |
|||
// Multiply every other digit by 3 |
|||
let everyOther = List.mapi (fun i x -> if i % 2 = 0 then x * 3 else x) isbnnum |
|||
// Sum the *3 list with the original list |
|||
let sum = List.sum everyOther + List.sum isbnnum |
|||
// If the remainder of sum / 10 is 0 then it's a valid ISBN13 |
|||
if sum % 10 = 0 then |
|||
printfn "%s Valid ISBN13" argv.[0] |
|||
else |
|||
printfn "%s Invalid ISBN13" argv.[0] |
|||
0</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126 Valid ISBN13 |
|||
978-0596528120 Inalid ISBN13 |
|||
978-1788399081 Valid ISBN13 |
|||
978-1788399083 Inalid ISBN13</pre> |
|||
=={{header|Fōrmulæ}}== |
|||
{{FormulaeEntry|page=https://formulae.org/?script=examples/ISBN13_check_digit}} |
|||
'''Solution''' |
|||
[[File:Fōrmulæ - ISBN13 check digit 01.png]] |
|||
'''Test cases''' |
|||
[[File:Fōrmulæ - ISBN13 check digit 02.png]] |
|||
[[File:Fōrmulæ - ISBN13 check digit 03.png]] |
|||
=={{header|Gambas}}== |
|||
{{trans|BASIC256}} |
|||
<syntaxhighlight lang="vbnet">Public Sub Main() |
|||
Dim isbn As String[] = ["978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083", "978-2-74839-908-0", "978-2-74839-908-5", "978 1 86197 876 9"] |
|||
For n As Integer = 1 To isbn.Count |
|||
Dim sum As Integer = 0, num As Integer |
|||
Dim isbnStr As String = isbn[n] |
|||
isbnStr = Replace(isbnStr, "-", "") |
|||
isbnStr = Replace(isbnStr, " ", "") |
|||
For m As Integer = 1 To Len(isbnStr) |
|||
If m Mod 2 = 0 Then |
|||
num = 3 * CInt(Mid(isbnStr, m, 1)) |
|||
Else |
|||
num = CInt(Mid(isbnStr, m, 1)) |
|||
End If |
|||
sum += num |
|||
Next |
|||
If sum Mod 10 = 0 Then |
|||
Print isbn[n]; ": good" |
|||
Else |
|||
Print isbn[n]; ": bad" |
|||
End If |
|||
Next |
|||
End</syntaxhighlight> |
|||
=={{header|Go}}== |
=={{header|Go}}== |
||
< |
<syntaxhighlight lang="go">package main |
||
import ( |
import ( |
||
Line 424: | Line 1,672: | ||
func main() { |
func main() { |
||
isbns := []string{"978- |
isbns := []string{"978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"} |
||
for _, isbn := range isbns { |
for _, isbn := range isbns { |
||
res := "bad" |
res := "bad" |
||
Line 432: | Line 1,680: | ||
fmt.Printf("%s: %s\n", isbn, res) |
fmt.Printf("%s: %s\n", isbn, res) |
||
} |
} |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
978- |
978-0596528126: good |
||
978- |
978-0596528120: bad |
||
978-1788399081: good |
978-1788399081: good |
||
978-1788399083: bad |
978-1788399083: bad |
||
</pre> |
</pre> |
||
=={{header|Haskell}}== |
=={{header|Haskell}}== |
||
< |
<syntaxhighlight lang="haskell">import Data.Char (digitToInt, isDigit) |
||
import Control.Monad (forM_) |
|||
import Text.Printf (printf) |
import Text.Printf (printf) |
||
testISBNs :: [String] |
|||
testISBNs = ["978-1734314502", "978-1734314509", "978-1788399081", "978-1788399083"] |
|||
pair :: Num a => [a] -> [(a, a)] |
pair :: Num a => [a] -> [(a, a)] |
||
pair [] = [] |
pair [] = [] |
||
pair xs = p (take 2 xs) : pair (drop 2 xs) |
pair xs = p (take 2 xs) : pair (drop 2 xs) |
||
where |
|||
where p ps = case ps of (x:y:zs) -> (x,y) |
|||
p ps = case ps of |
|||
(x:zs) -> (x,0) |
|||
(x : y : zs) -> (x, y) |
|||
(x : zs) -> (x, 0) |
|||
validIsbn13 :: String -> Bool |
validIsbn13 :: String -> Bool |
||
validIsbn13 isbn |
validIsbn13 isbn |
||
| length (digits isbn) /= 13 = False |
| length (digits isbn) /= 13 = False |
||
| otherwise = calc isbn `rem` 10 == 0 |
| otherwise = calc isbn `rem` 10 == 0 |
||
where |
|||
where digits = map digitToInt . filter isDigit |
|||
digits = map digitToInt . filter isDigit |
|||
calc = foldl (\a (x, y) -> x + y * 3 + a) 0 . pair . digits |
|||
calc = sum . map (\(x, y) -> x + y * 3) . pair . digits |
|||
main :: IO () |
main :: IO () |
||
main = |
|||
main = forM_ testISBNs (\isbn -> printf "%s: Valid: %s\n" isbn (show $ validIsbn13 isbn))</lang> |
|||
mapM_ |
|||
(printf "%s: Valid: %s\n" <*> (show . validIsbn13)) |
|||
[ "978-0596528126", |
|||
"978-0596528120", |
|||
"978-1788399081", |
|||
"978-1788399083" |
|||
]</syntaxhighlight> |
|||
{{out}} |
{{out}} |
||
<pre>978- |
<pre>978-0596528126: Valid: True |
||
978- |
978-0596528120: Valid: False |
||
978-1788399081: Valid: True |
978-1788399081: Valid: True |
||
978-1788399083: Valid: False</pre> |
978-1788399083: Valid: False</pre> |
||
Line 472: | Line 1,727: | ||
Or, expressed in terms of ''cycle'': |
Or, expressed in terms of ''cycle'': |
||
< |
<syntaxhighlight lang="haskell">import Data.Char (digitToInt, isDigit) |
||
isISBN13 :: String -> Bool |
isISBN13 :: String -> Bool |
||
isISBN13 = |
isISBN13 = |
||
(0 ==) |
(0 ==) |
||
flip rem 10 |
. flip rem 10 |
||
. sum |
|||
sum . flip (zipWith ((*) . digitToInt) . filter isDigit) (cycle [1, 3]) |
|||
. flip |
|||
(zipWith ((*) . digitToInt) . filter isDigit) |
|||
(cycle [1, 3]) |
|||
main :: IO () |
main :: IO () |
||
main = |
main = |
||
mapM_ |
mapM_ |
||
((,) <*> isISBN13) |
(print . ((,) <*> isISBN13)) |
||
[ "978-0596528126", |
|||
["978-1734314502", "978-1734314509", "978-1788399081", "978-1788399083"]</lang> |
|||
"978-0596528120", |
|||
"978-1788399081", |
|||
"978-1788399083" |
|||
]</syntaxhighlight> |
|||
{{Out}} |
{{Out}} |
||
<pre>("978- |
<pre>("978-0596528126",True) |
||
("978- |
("978-0596528120",False) |
||
("978-1788399081",True) |
("978-1788399081",True) |
||
("978-1788399083",False)</pre> |
("978-1788399083",False)</pre> |
||
=={{header|IS-BASIC}}== |
|||
<syntaxhighlight lang="is-basic">100 PROGRAM "ISBN13.bas" |
|||
110 DO |
|||
120 PRINT :INPUT PROMPT "ISBN-13 code: ":IS$ |
|||
130 IF IS$="" THEN EXIT DO |
|||
140 IF ISBN(IS$) THEN |
|||
150 PRINT "Ok." |
|||
160 ELSE |
|||
170 PRINT "CRC error." |
|||
180 END IF |
|||
190 LOOP |
|||
200 DEF TRIM$(S$) |
|||
210 LET T$="" |
|||
220 FOR I=1 TO LEN(S$) |
|||
230 IF S$(I)>CHR$(47) AND S$(I)<CHR$(58) THEN LET T$=T$&S$(I) |
|||
240 NEXT |
|||
250 LET TRIM$=T$ |
|||
260 END DEF |
|||
270 DEF ISBN(S$) |
|||
280 LET SUM,ISBN=0:LET ISBN$=TRIM$(IS$) |
|||
290 IF LEN(ISBN$)<>13 THEN PRINT "Invalid length.":EXIT DEF |
|||
300 FOR I=1 TO 11 STEP 2 |
|||
310 LET SUM=SUM+VAL(ISBN$(I))+VAL(ISBN$(I+1))*3 |
|||
320 NEXT |
|||
330 LET SUM=SUM+VAL(ISBN$(13)) |
|||
340 IF MOD(SUM,10)=0 THEN LET ISBN=-1 |
|||
350 END DEF</syntaxhighlight> |
|||
=={{header|J}}== |
|||
<syntaxhighlight lang="j"> D =: '0123456789' |
|||
isbn13c =: D&([ check@:i. clean) |
|||
check =: 0 = 10 | lc |
|||
lc =: [ +/@:* weight |
|||
weight =: 1 3 $~ # |
|||
clean =: ] -. a. -. [</syntaxhighlight> |
|||
{{out}} |
|||
<syntaxhighlight lang="j"> isbn13c;._1 ' 978-0596528126 978-0596528120 978-1788399081 978-1788399083' |
|||
1 0 1 0</syntaxhighlight> |
|||
=={{header|Java}}== |
|||
<syntaxhighlight lang="java"> |
|||
public static void main(String[] args) { |
|||
String[] isbn13s = { |
|||
"978-0596528126", |
|||
"978-0596528120", |
|||
"978-1788399081", |
|||
"978-1788399083" |
|||
}; |
|||
for (String isbn13 : isbn13s) |
|||
System.out.printf("%s %b%n", isbn13, validateISBN13(isbn13)); |
|||
} |
|||
static boolean validateISBN13(String string) { |
|||
int[] digits = digits(string.strip().replace("-", "")); |
|||
return digits[12] == checksum(digits); |
|||
} |
|||
static int[] digits(String string) { |
|||
int[] digits = new int[13]; |
|||
int index = 0; |
|||
for (char character : string.toCharArray()) { |
|||
if (character < '0' || character > '9') |
|||
throw new IllegalArgumentException("Invalid ISBN-13"); |
|||
/* convert ascii to integer */ |
|||
digits[index++] = Character.digit(character, 10); |
|||
} |
|||
return digits; |
|||
} |
|||
static int checksum(int[] digits) { |
|||
int total = 0; |
|||
int index = 0; |
|||
for (int digit : digits) { |
|||
if (index == 12) break; |
|||
if (index++ % 2 == 1) digit *= 3; |
|||
total += digit; |
|||
} |
|||
return 10 - (total % 10); |
|||
} |
|||
</syntaxhighlight> |
|||
<pre> |
|||
978-0596528126 true |
|||
978-0596528120 false |
|||
978-1788399081 true |
|||
978-1788399083 false |
|||
</pre> |
|||
<br /> |
|||
An alternate demonstration |
|||
<syntaxhighlight lang="java">public static void main(){ |
|||
System.out.println(isISBN13("978-0596528126")); |
|||
System.out.println(isISBN13("978-0596528120")); |
|||
System.out.println(isISBN13("978-1788399081")); |
|||
System.out.println(isISBN13("978-1788399083")); |
|||
} |
|||
public static boolean isISBN13(String in){ |
|||
int pre = Integer.parseInt(in.substring(0,3)); |
|||
if (pre!=978)return false; |
|||
String postStr = in.substring(4); |
|||
if (postStr.length()!=10)return false; |
|||
int post = Integer.parseInt(postStr); |
|||
int sum = 38; |
|||
for(int x = 0; x<10;x+=2) |
|||
sum += (postStr.charAt(x)-48)*3 + ((postStr.charAt(x+1)-48)); |
|||
if(sum%10==0) return true; |
|||
return false; |
|||
} |
|||
</syntaxhighlight>{{out}} |
|||
<pre> |
|||
true |
|||
false |
|||
true |
|||
false |
|||
</pre> |
|||
=={{header|jq}}== |
|||
{{works with|jq}} |
|||
'''Works with gojq, the Go implementation of jq''' |
|||
<syntaxhighlight lang="jq"> |
|||
def isbn_check: |
|||
def digits: tostring | explode | map( select(. >= 48 and . <= 57) | [.] | implode | tonumber); |
|||
def sum(s): reduce s as $x (null; . + $x); |
|||
digits |
|||
| . as $digits |
|||
| sum(range(0;length;2) | $digits[.]) as $one |
|||
| (3 * sum(range(1;length;2) | $digits[.])) as $two |
|||
| (($one+$two) % 10) == 0; |
|||
def testingcodes: |
|||
["978-0596528126", "978-0596528120", |
|||
"978-1788399081", "978-1788399083"]; |
|||
testingcodes[] |
|||
| "\(.): \(if isbn_check then "good" else "bad" end)" |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
978-0596528126: good |
|||
978-0596528120: bad |
|||
978-1788399081: good |
|||
978-1788399083: bad |
|||
</pre> |
|||
=={{header|Julia}}== |
=={{header|Julia}}== |
||
< |
<syntaxhighlight lang="julia">function isbncheck(str) |
||
return sum(iseven(i) ? 3 * parse(Int, ch) : parse(Int, ch) |
return sum(iseven(i) ? 3 * parse(Int, ch) : parse(Int, ch) |
||
for (i, ch) in enumerate(replace(str, r"\D" => ""))) % 10 == 0 |
for (i, ch) in enumerate(replace(str, r"\D" => ""))) % 10 == 0 |
||
end |
end |
||
const testingcodes = ["978- |
const testingcodes = ["978-0596528126", "978-0596528120", |
||
"978-1788399081", "978-1788399083"] |
"978-1788399081", "978-1788399083"] |
||
Line 503: | Line 1,909: | ||
println(code, ": ", isbncheck(code) ? "good" : "bad") |
println(code, ": ", isbncheck(code) ? "good" : "bad") |
||
end |
end |
||
</ |
</syntaxhighlight>{{out}} |
||
<pre> |
<pre> |
||
978- |
978-0596528126: good |
||
978- |
978-0596528120: bad |
||
978-1788399081: good |
|||
978-1788399083: bad |
|||
</pre> |
|||
=={{header|K}}== |
|||
{{works with|ngn/k}} |
|||
<syntaxhighlight lang=K>digits: {x[&(x>"/")&x<":"]-"0"} |
|||
isbn13c: 0=10!+/{x*(#x)#1 3} digits@ |
|||
isbn13c "978-0596528126" |
|||
1 |
|||
isbn13c "978-0596528120" |
|||
0 |
|||
isbn13c " 978-1788399081" |
|||
1 |
|||
isbn13c "978-1788399083" |
|||
0</syntaxhighlight> |
|||
=={{header|Kotlin}}== |
|||
<syntaxhighlight lang="kotlin"> |
|||
fun isValidISBN13(text: String): Boolean { |
|||
val isbn = text.replace(Regex("[- ]"), "") |
|||
return isbn.length == 13 && isbn.map { it - '0' } |
|||
.mapIndexed { index, value -> when (index % 2) { 0 -> value else -> 3 * value } } |
|||
.sum() % 10 == 0 |
|||
} |
|||
</syntaxhighlight> |
|||
Tested using Spek |
|||
<syntaxhighlight lang="kotlin"> |
|||
describe("ISBN Utilities") { |
|||
mapOf( |
|||
"978-0596528126" to true, |
|||
"978-0596528120" to false, |
|||
"978-1788399081" to true, |
|||
"978-1788399083" to false |
|||
).forEach { (input, expected) -> |
|||
it("returns $expected for $input") { |
|||
println("$input: ${when(isValidISBN13(input)) { |
|||
true -> "good" |
|||
else -> "bad" |
|||
}}") |
|||
assert(isValidISBN13(input) == expected) |
|||
} |
|||
} |
|||
} |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
978-0596528126: good |
|||
978-0596528120: bad |
|||
978-1788399081: good |
978-1788399081: good |
||
978-1788399083: bad |
978-1788399083: bad |
||
Line 512: | Line 1,969: | ||
=={{header|langur}}== |
=={{header|langur}}== |
||
{{works with|langur|0.8.11}} |
|||
In this example, we map to multiple functions (actually 1 no-op). |
In this example, we map to multiple functions (actually 1 no-op). |
||
<syntaxhighlight lang="langur"> |
|||
<lang langur>val .isbn13checkdigit = f(var .s) { |
|||
val isbn13checkdigit = fn(var s) { |
|||
.s = replace(.s, RE/[\- ]/) |
|||
s = replace(s, RE/[\- ]/) |
|||
s -> re/^[0-9]{13}$/ and |
|||
fold(f{+}, map [_, f{x 3}], s2n .s) div 10 |
|||
fold(fn{+}, map([_, fn{*3}], s2n(s))) div 10 |
|||
} |
} |
||
val |
val tests = { |
||
"978- |
"978-0596528126": true, |
||
"978- |
"978-0596528120": false, |
||
"978-1788399081": true, |
"978-1788399081": true, |
||
"978-1788399083": false, |
"978-1788399083": false, |
||
} |
} |
||
for |
for key of tests { |
||
val |
val pass = isbn13checkdigit(key) |
||
write |
write key, ": ", if(pass: "good"; "bad") |
||
writeln if( |
writeln if(pass == tests[key]: ""; " (ISBN-13 CHECK DIGIT TEST FAILED)") |
||
}</lang> |
|||
{{works with|langur|0.9.0}} |
|||
In this example, we set a for loop value as it progresses. |
|||
<lang langur>val .isbn13checkdigit = f(var .s) { |
|||
.s = replace(.s, RE/[\- ]/) |
|||
var .alt = true |
|||
matching(re/^[0-9]{13}$/, .s) and |
|||
for[=0] .d in s2n(.s) { _for += if(not= .alt: .d x 3; .d) } div 10 |
|||
} |
} |
||
</syntaxhighlight> |
|||
{{out}} |
|||
val .tests = h{ |
|||
<pre>978-0596528126: good |
|||
978-0596528120: bad |
|||
978-1788399081: good |
|||
978-1788399083: bad</pre> |
|||
} |
|||
=={{header|Lua}}== |
|||
{{trans|C}} |
|||
<syntaxhighlight lang="lua">function checkIsbn13(isbn) |
|||
local count = 0 |
|||
local sum = 0 |
|||
for c in isbn:gmatch"." do |
|||
if c == ' ' or c == '-' then |
|||
-- skip |
|||
elseif c < '0' or '9' < c then |
|||
return false |
|||
else |
|||
local digit = c - '0' |
|||
if (count % 2) > 0 then |
|||
sum = sum + 3 * digit |
|||
else |
|||
sum = sum + digit |
|||
end |
|||
count = count + 1 |
|||
end |
|||
end |
|||
if count ~= 13 then |
|||
return false |
|||
end |
|||
return (sum % 10) == 0 |
|||
end |
|||
function test(isbn) |
|||
if checkIsbn13(isbn) then |
|||
print(isbn .. ": good") |
|||
else |
|||
print(isbn .. ": bad") |
|||
end |
|||
end |
|||
function main() |
|||
test("978-0596528126") |
|||
test("978-0596528120") |
|||
test("978-1788399081") |
|||
test("978-1788399083") |
|||
end |
|||
main()</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126: good |
|||
978-0596528120: bad |
|||
978-1788399081: good |
|||
978-1788399083: bad</pre> |
|||
=={{header|Mathematica}} / {{header|Wolfram Language}}== |
|||
<syntaxhighlight lang="mathematica">ClearAll[ValidISBNQ] |
|||
ValidISBNQ[iban_String] := Module[{i}, |
|||
i = StringReplace[iban, {" " -> "", "-" -> ""}]; |
|||
If[StringMatchQ[i, Repeated[DigitCharacter]], |
|||
i = ToExpression /@ Characters[i]; |
|||
i[[2 ;; ;; 2]] *= 3; |
|||
Mod[Total[i], 10] == 0 |
|||
, |
|||
False |
|||
] |
|||
] |
|||
ValidISBNQ["978-0596528126"] |
|||
ValidISBNQ["978-0596528120"] |
|||
ValidISBNQ["978-1788399081"] |
|||
ValidISBNQ["978-1788399083"]</syntaxhighlight> |
|||
{{out}} |
|||
<pre>True |
|||
False |
|||
True |
|||
False</pre> |
|||
=={{header|MiniScript}}== |
|||
This GUI implementation is for use with [http://miniscript.org/MiniMicro Mini Micro]. |
|||
<syntaxhighlight lang="miniscript"> |
|||
isISBN13 = function(n) |
|||
n = n.replace("-","").replace(" ","") |
|||
s = 0 |
|||
for i in range(0, n.len-1,2) |
|||
s += n[i].val |
|||
end for |
|||
for i in range(1, n.len-1,2) |
|||
s += n[i].val * 3 |
|||
end for |
|||
return not (s % 10) |
|||
end function |
|||
testValues = "978-0596528126 978-0596528120 978-1788399081 978-1788399083".split(" ") |
|||
for val in testValues |
|||
print val + " " + ["bad", "good"][isISBN13(val)] |
|||
end for |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
978-0596528126 good |
|||
978-0596528120 bad |
|||
978-1788399081 good |
|||
978-1788399083 bad</pre> |
|||
=={{header|Miranda}}== |
|||
<syntaxhighlight lang="miranda">main :: [sys_message] |
|||
main = [Stdout (lay (map test tests))] |
|||
test :: [char]->[char] |
|||
test isbn = isbn ++ ": good", if isbn13 isbn |
|||
= isbn ++ ": bad", otherwise |
|||
tests :: [[char]] |
|||
tests = ["978-0596528126", |
|||
"978-0596528120", |
|||
"978-1788399081", |
|||
"978-1788399083"] |
|||
isbn13 :: [char]->bool |
|||
isbn13 str = False, if #isbn ~= 13 \/ ~and (map digit isbn) |
|||
= check mod 10 = 0, otherwise |
|||
where isbn = filter (~= '-') str |
|||
digits = map (numval.(:[])) isbn |
|||
check = sum (zipwith (*) digits (concat (repeat [1,3]))) |
|||
uncurry :: (*->**->***)->(*,**)->*** |
|||
uncurry f (a,b) = f a b |
|||
zipwith :: (*->**->***)->[*]->[**]->[***] |
|||
zipwith f a b = map (uncurry f) (zip2 a b)</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126: good |
|||
978-0596528120: bad |
|||
978-1788399081: good |
|||
978-1788399083: bad</pre> |
|||
=={{header|Modula-2}}== |
|||
<syntaxhighlight lang="modula2">MODULE ISBN; |
|||
FROM InOut IMPORT WriteString, WriteLn; |
|||
FROM Strings IMPORT Length; |
|||
PROCEDURE validISBN(s: ARRAY OF CHAR): BOOLEAN; |
|||
VAR total, i, length: CARDINAL; |
|||
BEGIN |
|||
total := 0; |
|||
length := Length(s); |
|||
IF (length # 14) OR (s[3] # '-') THEN |
|||
RETURN FALSE; |
|||
END; |
|||
FOR i := 0 TO length-1 DO |
|||
IF i # 3 THEN |
|||
IF (s[i] < '0') OR (s[i] > '9') THEN |
|||
RETURN FALSE; |
|||
ELSIF (i < 3) AND (i MOD 2 = 1) OR (i > 3) AND (i MOD 2 = 0) THEN |
|||
total := total + 3 * (ORD(s[i]) - ORD('0')); |
|||
ELSE |
|||
total := total + ORD(s[i]) - ORD('0'); |
|||
END; |
|||
END; |
|||
END; |
|||
RETURN total MOD 10 = 0; |
|||
END validISBN; |
|||
PROCEDURE check(s: ARRAY OF CHAR); |
|||
for .key of .tests { |
|||
BEGIN |
|||
val .pass = .isbn13checkdigit(.key) |
|||
WriteString(s); |
|||
write .key, ": ", if(.pass: "good"; "bad") |
|||
WriteString(': '); |
|||
writeln if(.pass == .tests[.key]: ""; " (ISBN-13 CHECK DIGIT TEST FAILED)") |
|||
IF validISBN(s) THEN |
|||
}</lang> |
|||
WriteString('good'); |
|||
ELSE |
|||
WriteString('bad'); |
|||
END; |
|||
WriteLn; |
|||
END check; |
|||
BEGIN |
|||
check('978-0596528126'); |
|||
check('978-0596528120'); |
|||
check('978-1788399081'); |
|||
check('978-1788399083'); |
|||
END ISBN.</syntaxhighlight> |
|||
{{out}} |
{{out}} |
||
<pre>978- |
<pre>978-0596528126: good |
||
978- |
978-0596528120: bad |
||
978-1788399081: good |
978-1788399081: good |
||
978-1788399083: bad</pre> |
978-1788399083: bad</pre> |
||
Line 563: | Line 2,181: | ||
=={{header|Nanoquery}}== |
=={{header|Nanoquery}}== |
||
{{trans|Go}} |
{{trans|Go}} |
||
< |
<syntaxhighlight lang="nanoquery">def checkIsbn13(isbn) |
||
// remove any hyphens or spaces |
// remove any hyphens or spaces |
||
isbn = str(isbn).replace("-","").replace(" ","") |
isbn = str(isbn).replace("-","").replace(" ","") |
||
Line 590: | Line 2,208: | ||
end |
end |
||
isbns = {"978- |
isbns = {"978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"} |
||
for isbn in isbns |
for isbn in isbns |
||
res = "bad" |
res = "bad" |
||
Line 598: | Line 2,216: | ||
print format("%s: %s\n", isbn, res) |
print format("%s: %s\n", isbn, res) |
||
end</ |
end</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>978- |
<pre>978-0596528126: good |
||
978- |
978-0596528120: bad |
||
978-1788399081: good |
978-1788399081: good |
||
978-1788399083: bad</pre> |
978-1788399083: bad</pre> |
||
=={{header|Nim}}== |
|||
<syntaxhighlight lang="nim">import strutils, strformat |
|||
func is_isbn*(s: string): bool = |
|||
var sum, len: int |
|||
for c in s: |
|||
if is_digit(c): |
|||
sum += (ord(c) - ord('0')) * (if len mod 2 == 0: 1 else: 3) |
|||
len += 1 |
|||
elif c != ' ' and c != '-': |
|||
return false |
|||
return (len == 13) and (sum mod 10 == 0) |
|||
when is_main_module: |
|||
let isbns = [ "978-0596528126", "978-0596528120", |
|||
"978-1788399081", "978-1788399083" ] |
|||
for isbn in isbns: |
|||
var quality: string = if is_isbn(isbn): "good" else: "bad" |
|||
echo &"{isbn}: {quality}"</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
978-0596528126: good |
|||
978-0596528120: bad |
|||
978-1788399081: good |
|||
978-1788399083: bad |
|||
</pre> |
|||
=={{header|OmniMark}}== |
|||
<syntaxhighlight lang="omnimark"> |
|||
define switch function isbn (value stream vs-s) as |
|||
local integer li-total initial {0} |
|||
local integer li-check |
|||
repeat scan vs-s |
|||
; Any digits in an ISBN-13 may be separated by a hyphen or space |
|||
match (digit => lpv-1 ('-' | space)? digit => lpv-3 ('-' | space)? ) => p |
|||
; Add the first digit to the total and add the second multiplied by 3 |
|||
increment li-total by (lpv-1 + (lpv-3 * 3)) |
|||
match any => lpv-c |
|||
; This is always the thirteenth digit, the 'check' digit |
|||
set li-check to lpv-c |
|||
again |
|||
do when 10 - li-total modulo 10 = li-check |
|||
return true |
|||
else when li-total modulo 10 = 0 and li-check = 0 |
|||
return true |
|||
else |
|||
return false |
|||
done |
|||
process |
|||
; These are the first four test ISBN-13s from the problem description, with |
|||
; the first and third being valid and the second and fourth invalid. |
|||
submit "978-0596528126" |
|||
submit "978-0596528120" |
|||
submit "978-1788399081" |
|||
submit "978-1788399083" |
|||
; This ISBN-13 is from https://isbn-information.com/the-13-digit-isbn.html |
|||
submit "978-1-86197-876-9" |
|||
submit "978 1 86197 876 9" |
|||
; These ISBNs are from https://en.wikipedia.org/wiki/ISBN |
|||
submit "978-3-16-148410-0" |
|||
submit "978 3 16 148410 9" ; But this one has had the 'check' digit changed |
|||
submit "This is valid: 978-0-306-40615-7^" ; Adding some extra complexity :) |
|||
submit "978-03-0640-615 7." ; Formatted as "EAN-Group-Publisher-Title Check" |
|||
; These strings show how non-ISBN-13 numbers must be (and are) ignored. |
|||
submit "This is a 14-digit number - 555-30640610555 - so it is ignored.%n" |
|||
submit "This is an ISBN-10, so it is ignored too: 0-306-40615-2.%n" |
|||
; This find rule will only find a string that is an ISBN-13 (digit/-/space) |
|||
; combination. It has been split into three lines to make it easier to see |
|||
; what is being done. |
|||
; 1. The string must either be preceded by any character other than a digit |
|||
; or start the stream, and |
|||
; 2. It must then have 12 digits (with optional hyphens or spaces |
|||
; intermingled) and must conclude with a digit, and |
|||
; 3. It either must not be followed by a another digit (making it more |
|||
; than 13 digits, so not an ISBN-13) or the stream ends. |
|||
find ([any \ digit] | value-start) => lpv-s |
|||
((digit ('-' | space)?){12} digit) => lpv-n |
|||
([any \ digit] | value-end) => lpv-e |
|||
output lpv-s || lpv-n || lpv-e |
|||
do when isbn(lpv-n) = true |
|||
output ' (good)%n' |
|||
else |
|||
output ' (bad)%n' |
|||
done |
|||
; output all other characters verbatim |
|||
find any => lpv-char |
|||
output lpv-char |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
978-0596528126 (good) |
|||
978-0596528120 (bad) |
|||
978-1788399081 (good) |
|||
978-1788399083 (bad) |
|||
978-1-86197-876-9 (good) |
|||
978 1 86197 876 9 (good) |
|||
978-3-16-148410-0 (good) |
|||
978 3 16 148410 9 (bad) |
|||
This is valid: 978-0-306-40615-7^ (good) |
|||
978-03-0640-615 7. (good) |
|||
This is a 14-digit number - 555-30640610555 - so it is ignored. |
|||
This is an ISBN-10, so it is ignored too: 0-306-40615-2. |
|||
</pre> |
|||
=={{header|Pascal}}== |
|||
{{works with|Extended Pascal}} |
|||
<syntaxhighlight lang="pascal">program ISBNChecksum(output); |
|||
const |
|||
codeIndexMaximum = 17; |
|||
ISBNIndexMinimum = 1; |
|||
ISBNIndexMaximum = 13; |
|||
ISBNIndexRange = ISBNIndexMaximum - ISBNIndexMinimum + 1; |
|||
type |
|||
code = string(codeIndexMaximum); |
|||
codeIndex = 1..codeIndexMaximum value 1; |
|||
decimalDigit = '0'..'9'; |
|||
decimalValue = 0..9; |
|||
ISBNIndex = ISBNIndexMinimum..ISBNIndexMaximum; |
|||
ISBN = array[ISBNIndex] of decimalDigit; |
|||
{ returns the integer value represented by a character } |
|||
function numericValue(protected c: decimalDigit): decimalValue; |
|||
begin |
|||
{ in Pascal result variable bears the same name as the function } |
|||
numericValue := ord(c) - ord('0') |
|||
end; |
|||
{ determines whether an ISBN is technically valid (checksum correct) } |
|||
function isValidISBN(protected n: ISBN): Boolean; |
|||
var |
|||
sum: 0..225 value 0; |
|||
i: ISBNIndex; |
|||
begin |
|||
{ NB: in Pascal for-loop-limits are _inclusive_ } |
|||
for i := ISBNIndexMinimum to ISBNIndexMaximum do |
|||
begin |
|||
{ alternating scale factor 3^0, 3^1 based on Boolean } |
|||
sum := sum + numericValue(n[i]) * 3 pow ord(not odd(i)) |
|||
end; |
|||
isValidISBN := sum mod 10 = 0 |
|||
end; |
|||
{ transform '978-0-387-97649-5' into '9780387976495' } |
|||
function digits(n: code): code; |
|||
var |
|||
sourceIndex, destinationIndex: codeIndex; |
|||
begin |
|||
for sourceIndex := 1 to length(n) do |
|||
begin |
|||
if n[sourceIndex] in ['0'..'9'] then |
|||
begin |
|||
n[destinationIndex] := n[sourceIndex]; |
|||
destinationIndex := destinationIndex + 1 |
|||
end |
|||
end; |
|||
{ to alter a string’s length you need overwrite it completely } |
|||
digits := subStr(n, 1, destinationIndex - 1) |
|||
end; |
|||
{ data type coercion } |
|||
function asISBN(protected n: code): ISBN; |
|||
var |
|||
result: ISBN; |
|||
begin |
|||
unpack(n[1..length(n)], result, 1); |
|||
asISBN := result |
|||
end; |
|||
{ tells whether a string value contains a proper ISBN representation } |
|||
function isValidISBNString(protected hyphenatedForm: code): Boolean; |
|||
var |
|||
digitOnlyForm: code; |
|||
begin |
|||
digitOnlyForm := digits(hyphenatedForm); |
|||
{ The Extended Pascal `and_then` Boolean operator allows us } |
|||
{ to first check the length before invoking `isValidISBN`. } |
|||
isValidISBNString := (length(digitOnlyForm) = ISBNIndexRange) |
|||
and_then isValidISBN(asISBN(digitOnlyForm)) |
|||
end; |
|||
{ === MAIN ============================================================= } |
|||
begin |
|||
writeLn(isValidISBNString('978-0596528126')); |
|||
writeLn(isValidISBNString('978-0596528120')); |
|||
writeLn(isValidISBNString('978-1788399081')); |
|||
writeLn(isValidISBNString('978-1788399083')) |
|||
end.</syntaxhighlight> |
|||
{{out}} |
|||
<pre>True |
|||
False |
|||
True |
|||
False</pre> |
|||
=={{header|Perl}}== |
=={{header|Perl}}== |
||
< |
<syntaxhighlight lang="perl">use strict; |
||
use warnings; |
use warnings; |
||
use feature 'say'; |
use feature 'say'; |
||
Line 616: | Line 2,438: | ||
} |
} |
||
for (<978- |
for (<978-0596528126 978-0596528120 978-1788399081 978-1788399083 978-2-74839-908-0 978-2-74839-908-5>) { |
||
my($isbn,$check) = /(.*)(.)/; |
my($isbn,$check) = /(.*)(.)/; |
||
my $check_d = check_digit($isbn); |
my $check_d = check_digit($isbn); |
||
say "$_ : " . ($check == $check_d ? 'Good' : "Bad check-digit $check; should be $check_d") |
say "$_ : " . ($check == $check_d ? 'Good' : "Bad check-digit $check; should be $check_d") |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>978- |
<pre>978-0596528126 : Good |
||
978- |
978-0596528120 : Bad check-digit 9; should be 2 |
||
978-1788399081 : Good |
978-1788399081 : Good |
||
978-1788399083 : Bad check-digit 3; should be 1 |
978-1788399083 : Bad check-digit 3; should be 1 |
||
Line 630: | Line 2,452: | ||
=={{header|Phix}}== |
=={{header|Phix}}== |
||
<!--<syntaxhighlight lang="phix">(phixonline)--> |
|||
<lang Phix>procedure check_isbn13(string isbn) |
|||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span> |
|||
integer digits = 0, checksum = 0, w = 1 |
|||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">check_isbn13</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">isbn</span><span style="color: #0000FF;">)</span> |
|||
for i=1 to length(isbn) do |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">digits</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">checksum</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">w</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span> |
|||
integer ch = isbn[i] |
|||
<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;">isbn</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span> |
|||
if ch!=' ' and ch!='-' then |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">ch</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">isbn</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> |
|||
ch -= '0' |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">!=</span><span style="color: #008000;">' '</span> <span style="color: #008080;">and</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">!=</span><span style="color: #008000;">'-'</span> <span style="color: #008080;">then</span> |
|||
if ch<0 or ch>9 then checksum = 9 exit end if |
|||
<span style="color: #000000;">ch</span> <span style="color: #0000FF;">-=</span> <span style="color: #008000;">'0'</span> |
|||
checksum += ch*w |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;"><</span><span style="color: #000000;">0</span> <span style="color: #008080;">or</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">></span><span style="color: #000000;">9</span> <span style="color: #008080;">then</span> <span style="color: #000000;">checksum</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">9</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
digits += 1 |
|||
<span style="color: #000000;">checksum</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">*</span><span style="color: #000000;">w</span> |
|||
w = 4-w |
|||
<span style="color: #000000;">digits</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span> |
|||
end if |
|||
<span style="color: #000000;">w</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">4</span><span style="color: #0000FF;">-</span><span style="color: #000000;">w</span> |
|||
end for |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
checksum = remainder(checksum,10) |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
string gb = iff(digits=13 and checksum=0 ? "good" : "bad") |
|||
<span style="color: #000000;">checksum</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">remainder</span><span style="color: #0000FF;">(</span><span style="color: #000000;">checksum</span><span style="color: #0000FF;">,</span><span style="color: #000000;">10</span><span style="color: #0000FF;">)</span> |
|||
printf(1,"%s: %s\n",{isbn,gb}) |
|||
<span style="color: #004080;">string</span> <span style="color: #000000;">gb</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">digits</span><span style="color: #0000FF;">=</span><span style="color: #000000;">13</span> <span style="color: #008080;">and</span> <span style="color: #000000;">checksum</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #0000FF;">?</span> <span style="color: #008000;">"good"</span> <span style="color: #0000FF;">:</span> <span style="color: #008000;">"bad"</span><span style="color: #0000FF;">)</span> |
|||
end procedure |
|||
<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;">"%s: %s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">isbn</span><span style="color: #0000FF;">,</span><span style="color: #000000;">gb</span><span style="color: #0000FF;">})</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
constant isbns = {"978-1734314502", "978-1734314509", "978-1788399081", "978-1788399083", |
|||
"978-2-74839-908-0","978-2-74839-908-5","978 1 86197 876 9"} |
|||
<span style="color: #008080;">constant</span> <span style="color: #000000;">isbns</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"978-0596528126"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"978-0596528120"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"978-1788399081"</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"978-1788399083"</span><span style="color: #0000FF;">,</span> |
|||
for i=1 to length(isbns) do check_isbn13(isbns[i]) end for</lang> |
|||
<span style="color: #008000;">"978-2-74839-908-0"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"978-2-74839-908-5"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"978 1 86197 876 9"</span><span style="color: #0000FF;">}</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;">isbns</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span> <span style="color: #000000;">check_isbn13</span><span style="color: #0000FF;">(</span><span style="color: #000000;">isbns</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">])</span> <span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
<!--</syntaxhighlight>--> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
978- |
978-0596528126: good |
||
978- |
978-0596528120: bad |
||
978-1788399081: good |
978-1788399081: good |
||
978-1788399083: bad |
978-1788399083: bad |
||
Line 662: | Line 2,487: | ||
=={{header|PicoLisp}}== |
=={{header|PicoLisp}}== |
||
< |
<syntaxhighlight lang="picolisp">(de isbn13? (S) |
||
(let L |
(let L |
||
(make |
(make |
||
Line 677: | Line 2,502: | ||
(if (isbn13? A) 'ok 'fail) ) ) |
(if (isbn13? A) 'ok 'fail) ) ) |
||
(quote |
(quote |
||
"978- |
"978-0596528126" |
||
"978- |
"978-0596528120" |
||
"978-1-86197-876-9" |
"978-1-86197-876-9" |
||
"978-2-74839-908-5" |
"978-2-74839-908-5" |
||
"978 1 86197 876 9" ) )</ |
"978 1 86197 876 9" ) )</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
978- |
978-0596528126 ok |
||
978- |
978-0596528120 fail |
||
978-1-86197-876-9 ok |
978-1-86197-876-9 ok |
||
978-2-74839-908-5 fail |
978-2-74839-908-5 fail |
||
978 1 86197 876 9 ok |
978 1 86197 876 9 ok |
||
</pre> |
|||
=={{header|PL/M}}== |
|||
<syntaxhighlight lang="plm">100H: |
|||
CHECK$ISBN13: PROCEDURE (PTR) BYTE; |
|||
DECLARE PTR ADDRESS, ISBN BASED PTR BYTE; |
|||
DECLARE (I, F, T) BYTE; |
|||
F = 1; |
|||
T = 0; |
|||
DO I = 0 TO 13; |
|||
IF I = 3 THEN DO; |
|||
/* THIRD CHAR SHOULD BE '-' */ |
|||
IF ISBN(I) <> '-' THEN RETURN 0; |
|||
END; |
|||
ELSE DO; |
|||
/* DIGITS MUST BE VALID */ |
|||
IF ISBN(I) < '0' OR ISBN(I) > '9' THEN RETURN 0; |
|||
T = T + (ISBN(I) - '0') * F; |
|||
F = 4 - F; /* MULTIPLY BY 1 AND 3 ALTERNATELY */ |
|||
END; |
|||
END; |
|||
RETURN (T MOD 10) = 0; |
|||
END CHECK$ISBN13; |
|||
/* CP/M BDOS CALL */ |
|||
BDOS: PROCEDURE (FUNC, ARG); |
|||
DECLARE FUNC BYTE, ARG ADDRESS; |
|||
GO TO 5; |
|||
END BDOS; |
|||
PRINT: PROCEDURE (STR); |
|||
DECLARE STR ADDRESS; |
|||
CALL BDOS(9, STR); |
|||
END PRINT; |
|||
/* TESTS */ |
|||
DECLARE TEST (4) ADDRESS; |
|||
TEST(0) = .'978-0596528126$'; |
|||
TEST(1) = .'978-0596528120$'; |
|||
TEST(2) = .'978-1788399081$'; |
|||
TEST(3) = .'978-1788399083$'; |
|||
DECLARE I BYTE; |
|||
DO I = 0 TO LAST(TEST); |
|||
CALL PRINT(TEST(I)); |
|||
CALL PRINT(.': $'); |
|||
IF CHECK$ISBN13(TEST(I)) THEN |
|||
CALL PRINT(.'GOOD$'); |
|||
ELSE |
|||
CALL PRINT(.'BAD$'); |
|||
CALL PRINT(.(13,10,'$')); |
|||
END; |
|||
CALL BDOS(0,0); |
|||
EOF</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126: GOOD |
|||
978-0596528120: BAD |
|||
978-1788399081: GOOD |
|||
978-1788399083: BAD</pre> |
|||
=={{header|PowerShell}}== |
|||
<syntaxhighlight lang="powershell"> |
|||
function Get-ISBN13 { |
|||
$codes = ( |
|||
"978-0596528126", |
|||
"978-0596528120", |
|||
"978-1788399081", |
|||
"978-1788399083" |
|||
) |
|||
foreach ($line in $codes) { |
|||
$sum = $null |
|||
$codeNoDash = $line.Replace("-","") |
|||
for ($i = 0; $i -lt $codeNoDash.length; $i++) { |
|||
if (($i % 2) -eq 1) { |
|||
$sum += [decimal]$codeNoDash[$i] * 3 |
|||
}else { |
|||
$sum += [decimal]$codeNoDash[$i] |
|||
} |
|||
} |
|||
if (($sum % 10) -eq 0) { |
|||
Write-Host "$line Good" |
|||
}else { |
|||
Write-Host "$line Bad" |
|||
} |
|||
} |
|||
} |
|||
Get-ISBN13 |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
978-0596528126 Good |
|||
978-0596528120 Bad |
|||
978-1788399081 Good |
|||
978-1788399083 Bad |
|||
</pre> |
|||
=={{header|PureBasic}}== |
|||
<syntaxhighlight lang="purebasic">Macro TestISBN13(X) |
|||
Print(X) |
|||
If IsISBN13(X) : PrintN(" good") : Else : PrintN(" bad") : EndIf |
|||
EndMacro |
|||
Procedure.b IsISBN13(ISBN13.s) |
|||
ISBN13=Left(ISBN13,3)+Mid(ISBN13,5) |
|||
If IsNAN(Val(ISBN13)) Or Len(ISBN13)<>13 : ProcedureReturn #False : EndIf |
|||
Dim ISBN.s{1}(12) : PokeS(@ISBN(),ISBN13) |
|||
For i=0 To 11 Step 2 : sum+Val(ISBN(i))+Val(ISBN(i+1))*3 : Next |
|||
sum+Val(ISBN(12)) |
|||
ProcedureReturn Bool(sum%10=0) |
|||
EndProcedure |
|||
If OpenConsole() |
|||
TestISBN13("978-0596528126") |
|||
TestISBN13("978-0596528120") |
|||
TestISBN13("978-1788399081") |
|||
TestISBN13("978-1788399083") |
|||
Input() |
|||
EndIf</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126 good |
|||
978-0596528120 bad |
|||
978-1788399081 good |
|||
978-1788399083 bad |
|||
</pre> |
</pre> |
||
=={{header|Python}}== |
=={{header|Python}}== |
||
< |
<syntaxhighlight lang="python">def is_isbn13(n): |
||
n = n.replace('-','').replace(' ', '') |
n = n.replace('-','').replace(' ', '') |
||
if len(n) != 13: |
if len(n) != 13: |
||
Line 702: | Line 2,666: | ||
if __name__ == '__main__': |
if __name__ == '__main__': |
||
tests = ''' |
tests = ''' |
||
978- |
978-0596528126 |
||
978- |
978-0596528120 |
||
978-1788399081 |
978-1788399081 |
||
978-1788399083'''.strip().split() |
978-1788399083'''.strip().split() |
||
for t in tests: |
for t in tests: |
||
print(f"ISBN13 {t} validates {is_isbn13(t)}")</ |
print(f"ISBN13 {t} validates {is_isbn13(t)}")</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>ISBN13 978- |
<pre>ISBN13 978-0596528126 validates True |
||
ISBN13 978- |
ISBN13 978-0596528120 validates False |
||
ISBN13 978-1788399081 validates True |
ISBN13 978-1788399081 validates True |
||
ISBN13 978-1788399083 validates False</pre> |
ISBN13 978-1788399083 validates False</pre> |
||
Line 717: | Line 2,681: | ||
Or, expressed in terms of ''itertools.cycle'' |
Or, expressed in terms of ''itertools.cycle'' |
||
< |
<syntaxhighlight lang="python">'''ISBN13 check digit''' |
||
from itertools import cycle |
from itertools import cycle |
||
from operator import mul |
|||
# isISBN13 :: String -> Bool |
# isISBN13 :: String -> Bool |
||
def isISBN13(s): |
def isISBN13(s): |
||
'''True if |
'''True if s is a valid ISBN13 string |
||
a valid ISBN-13 code. |
|||
''' |
''' |
||
digits = [int(c) for c in s if c.isdigit()] |
digits = [int(c) for c in s if c.isdigit()] |
||
return 13 == len(digits) and |
return 13 == len(digits) and ( |
||
map( |
0 == sum(map( |
||
lambda f, x: f(x), |
|||
cycle([ |
|||
lambda x: x, |
|||
lambda x: 3 * x |
|||
]), |
|||
digits |
|||
)) % 10 |
|||
) |
|||
# ------------------------- |
# ------------------------- TEST ------------------------- |
||
# main :: IO () |
# main :: IO () |
||
def main(): |
def main(): |
||
''' |
'''Test strings for ISBN-13 validity.''' |
||
print('\n'.join( |
|||
for s in ['978-1734314502', '978-1734314509', |
|||
repr((s, isISBN13(s))) for s |
|||
'978-1788399081', '978-1788399083']: |
|||
in ["978-0596528126", |
|||
"978-0596528120", |
|||
"978-1788399081", |
|||
"978-1788399083" |
|||
] |
|||
)) |
|||
# MAIN --- |
# MAIN --- |
||
if __name__ == '__main__': |
if __name__ == '__main__': |
||
main() |
main() |
||
</syntaxhighlight> |
|||
{{Out}} |
{{Out}} |
||
<pre>('978- |
<pre>('978-0596528126', True) |
||
('978- |
('978-0596528120', False) |
||
('978-1788399081', True) |
('978-1788399081', True) |
||
('978-1788399083', False)</pre> |
('978-1788399083', False)</pre> |
||
=={{header|Quackery}}== |
|||
<syntaxhighlight lang="quackery">[ char 0 char 9 1+ within ] is digit? ( c --> b ) |
|||
[ 1 & ] is odd? ( n --> b ) |
|||
[ [] swap ]'[ swap |
|||
witheach [ |
|||
dup nested |
|||
unrot over do |
|||
iff [ dip join ] |
|||
else nip |
|||
] drop ] is filter ( [ --> [ ) |
|||
[ 0 swap |
|||
witheach [ |
|||
char->n i^ odd? |
|||
iff [ 3 * + ] |
|||
else + |
|||
] ] is checksum ( $ --> n ) |
|||
[ filter digit? |
|||
dup size 13 = not |
|||
iff [ drop false ] done |
|||
checksum 10 mod 0 = ] is isbn ( $ --> b ) |
|||
[ dup echo$ say ": " isbn |
|||
iff [ say "Good" ] |
|||
else [ say "Bad" ] cr ] is isbn-test ( $ --> ) |
|||
$ '978-0596528126' isbn-test |
|||
$ '978-0596528120' isbn-test |
|||
$ '978-1788399081' isbn-test |
|||
$ '978-1788399083' isbn-test</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
978-0596528126: Good |
|||
978-0596528120: Bad |
|||
978-1788399081: Good |
|||
978-1788399083: Bad |
|||
</pre> |
|||
=={{header|Racket}}== |
|||
<syntaxhighlight lang="racket">#lang racket/base |
|||
(define (isbn13-check-digit-valid? s) |
|||
(zero? (modulo (for/sum ((i (in-naturals)) |
|||
(d (regexp-replace* #px"[^[:digit:]]" s ""))) |
|||
(* (- (char->integer d) (char->integer #\0)) |
|||
(if (even? i) 1 3))) |
|||
10))) |
|||
(module+ test |
|||
(require rackunit) |
|||
(check-true (isbn13-check-digit-valid? "978-0596528126")) |
|||
(check-false (isbn13-check-digit-valid? "978-0596528120")) |
|||
(check-true (isbn13-check-digit-valid? "978-1788399081")) |
|||
(check-false (isbn13-check-digit-valid? "978-1788399083")))</syntaxhighlight> |
|||
{{out}} |
|||
no output indicates tests pass |
|||
=={{header|Raku}}== |
=={{header|Raku}}== |
||
Line 762: | Line 2,796: | ||
Also test a value that has a zero check digit. |
Also test a value that has a zero check digit. |
||
<lang |
<syntaxhighlight lang="raku" line>sub check-digit ($isbn) { |
||
(10 - (sum (|$isbn.comb(/<[0..9]>/)) »*» (1,3)) % 10).substr: *-1 |
(10 - (sum (|$isbn.comb(/<[0..9]>/)) »*» (1,3)) % 10).substr: *-1 |
||
} |
} |
||
Line 773: | Line 2,807: | ||
"Bad check-digit $check; should be $check-digit" |
"Bad check-digit $check; should be $check-digit" |
||
} for words < |
} for words < |
||
978- |
978-0596528126 |
||
978- |
978-0596528120 |
||
978-1788399081 |
978-1788399081 |
||
978-1788399083 |
978-1788399083 |
||
978-2-74839-908-0 |
978-2-74839-908-0 |
||
978-2-74839-908-5 |
978-2-74839-908-5 |
||
>;</ |
>;</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>978- |
<pre>978-0596528126 : Good |
||
978- |
978-0596528120 : Bad check-digit 9; should be 2 |
||
978-1788399081 : Good |
978-1788399081 : Good |
||
978-1788399083 : Bad check-digit 3; should be 1 |
978-1788399083 : Bad check-digit 3; should be 1 |
||
Line 788: | Line 2,822: | ||
978-2-74839-908-5 : Bad check-digit 5; should be 0 |
978-2-74839-908-5 : Bad check-digit 5; should be 0 |
||
</pre> |
</pre> |
||
=={{header|Red}}== |
|||
<syntaxhighlight lang="red">check_valid_isbn13: function [str] [ |
|||
is_digit: charset [#"0" - #"9"] |
|||
remove-each i str [not pick is_digit i] ; remove non-digits |
|||
either 13 = length? str [ ; reject strings of incorrect length |
|||
sum: 0 |
|||
repeat i 13 [ |
|||
mul: either even? i [3] [1] ; multiplier for odd/even digits |
|||
sum: sum + (mul * to integer! to string! pick str i) |
|||
] |
|||
zero? (sum % 10) ; check if remainder mod 10 is zero |
|||
] [ |
|||
false |
|||
] |
|||
] |
|||
; check given examples |
|||
foreach [str] ["978-0596528126" "978-0596528120" "978-1788399081" "978-1788399083"] [ |
|||
prin str |
|||
prin " - " |
|||
print check_valid_isbn13 str |
|||
] |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
978-0596528126 - true |
|||
978-0596528120 - false |
|||
978-1788399081 - true |
|||
978-1788399083 - false |
|||
</pre> |
|||
=={{header|Refal}}== |
|||
<syntaxhighlight lang="refal">$ENTRY Go { |
|||
= <Test '978-0596528126'> |
|||
<Test '978-0596528120'> |
|||
<Test '978-1788399081'> |
|||
<Test '978-1788399083'> |
|||
}; |
|||
Test { |
|||
e.X = <Prout e.X ': ' <ISBN e.X>>; |
|||
}; |
|||
ISBN { |
|||
e.X, <Remove '-' e.X>: e.DS, |
|||
<CheckDigits e.DS>: { |
|||
False = Bad; |
|||
True = <ISBN1 e.DS>; |
|||
}; |
|||
}; |
|||
ISBN1 { |
|||
(13 s.Sum), <Mod s.Sum 10>: 0 = Good; |
|||
(13 s.Sum) e.X = Bad; |
|||
(s.N s.Sum) = Bad; |
|||
(s.N s.Sum) s.D e.X, |
|||
<+ s.N 1>: s.N1, |
|||
<Numb s.D>: s.V, |
|||
<Mod s.N 2>: { |
|||
0 = <ISBN1 (s.N1 <+ s.Sum s.V>) e.X>; |
|||
1 = <ISBN1 (s.N1 <+ s.Sum <* 3 s.V>>) e.X>; |
|||
}; |
|||
e.X = <ISBN1 (0 0) e.X>; |
|||
}; |
|||
Remove { |
|||
s.1 = ; |
|||
s.1 s.1 e.X = <Remove s.1 e.X>; |
|||
s.1 s.2 e.X = s.2 <Remove s.1 e.X>; |
|||
}; |
|||
CheckDigits { |
|||
= True; |
|||
s.D e.X, '0123456789': e.A s.D e.B = <CheckDigits e.X>; |
|||
e.X = False; |
|||
};</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126: Good |
|||
978-0596528120: Bad |
|||
978-1788399081: Good |
|||
978-1788399083: Bad</pre> |
|||
=={{header|REXX}}== |
=={{header|REXX}}== |
||
A couple of additional checks were made to verify a correct length, and also that the ISBN-13 code is all numerics (with optional minus signs). |
A couple of additional checks were made to verify a correct length, and also that the ISBN-13 code is all numerics (with optional minus signs). |
||
< |
<syntaxhighlight lang="rexx">/*REXX pgm validates the check digit of an ISBN─13 code (it may have embedded minuses).*/ |
||
parse arg $ /*obtain optional arguments from the CL*/ |
parse arg $ /*obtain optional arguments from the CL*/ |
||
if $='' | if $="," then $= '978- |
if $='' | if $="," then $= '978-0596528126 978-0596528120 978-1788399081 978-1788399083' |
||
@ISBN= "ISBN─13 code isn't" /*a literal used when displaying msgs. */ |
@ISBN= "ISBN─13 code isn't" /*a literal used when displaying msgs. */ |
||
/* [↓] remove all minuses from X code.*/ |
/* [↓] remove all minuses from X code.*/ |
||
Line 809: | Line 2,931: | ||
if right(sum, 1)==0 then say ' ISBN-13 code ' x " is valid." |
if right(sum, 1)==0 then say ' ISBN-13 code ' x " is valid." |
||
else say ' ISBN-13 code ' x " isn't valid." |
else say ' ISBN-13 code ' x " isn't valid." |
||
end /*j*/ /*stick a fork in it, we're all done. */</ |
end /*j*/ /*stick a fork in it, we're all done. */</syntaxhighlight> |
||
{{out|output|text= when using the four default inputs:}} |
{{out|output|text= when using the four default inputs:}} |
||
<pre> |
<pre> |
||
Line 817: | Line 2,939: | ||
ISBN-13 code 9781788399083 isn't valid. |
ISBN-13 code 9781788399083 isn't valid. |
||
</pre> |
</pre> |
||
=={{header|Ring}}== |
|||
<syntaxhighlight lang="ring"> |
|||
load "stdlib.ring" |
|||
isbn = ["978-0596528126","978-0596528120", "978-1788399081", "978-1788399083","978-2-74839-908-0","978-2-74839-908-5","978 1 86197 876 9"] |
|||
for n = 1 to len(isbn) |
|||
sum = 0 |
|||
isbnStr = isbn[n] |
|||
isbnStr = substr(isbnStr,"-","") |
|||
isbnStr = substr(isbnStr," ","") |
|||
for m = 1 to len(isbnStr) |
|||
if m%2 = 0 |
|||
num = 3*number(substr(isbnStr,m,1)) |
|||
sum = sum + num |
|||
else |
|||
num = number(substr(isbnStr,m,1)) |
|||
sum = sum + num |
|||
ok |
|||
next |
|||
if sum%10 = 0 |
|||
see "" + isbn[n] + ": true" + nl |
|||
else |
|||
see "" + isbn[n] + ": bad" + nl |
|||
ok |
|||
next |
|||
</syntaxhighlight> |
|||
Output: |
|||
<pre> |
|||
978-0596528126: true |
|||
978-0596528120: bad |
|||
978-1788399081: true |
|||
978-1788399083: bad |
|||
978-2-74839-908-0: true |
|||
978-2-74839-908-5: bad |
|||
978 1 86197 876 9: true |
|||
</pre> |
|||
=={{header|RPL}}== |
|||
{{works with|Halcyon Calc|4.2.7}} |
|||
≪ → isbn |
|||
≪ 0 1 CF |
|||
1 isbn SIZE FOR j |
|||
IF "0123456789" isbn j DUP SUB POS THEN |
|||
LAST 1 - |
|||
IF 1 FS?C THEN 3 * ELSE 1 SF END |
|||
+ |
|||
END |
|||
NEXT |
|||
≫ |
|||
10 MOD NOT |
|||
≫ |
|||
'ISBN?' STO |
|||
≪ { "978-0596528126" "978-0596528120" "978-1788399081" "978-1788399083" } → tests |
|||
≪ 1 tests SIZE FOR j tests GET ISBN? NEXT ≫ |
|||
≫ EVAL |
|||
{{out}} |
|||
<pre> |
|||
4: 1 |
|||
3: 0 |
|||
2: 1 |
|||
1: 0 |
|||
</pre> |
|||
=={{header|Ruby}}== |
|||
<syntaxhighlight lang="ruby">def validISBN13?(str) |
|||
cleaned = str.delete("^0-9").chars |
|||
return false unless cleaned.size == 13 |
|||
cleaned.each_slice(2).sum{|d1, d2| d1.to_i + 3*d2.to_i }.remainder(10) == 0 |
|||
end |
|||
isbns = ["978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"] |
|||
isbns.each{|isbn| puts "#{isbn}: #{validISBN13?(isbn)}" } |
|||
</syntaxhighlight>{{out}} |
|||
<pre>978-0596528126: true |
|||
978-0596528120: false |
|||
978-1788399081: true |
|||
978-1788399083: false |
|||
</pre> |
|||
=={{header|Rust}}== |
|||
<syntaxhighlight lang="rust">fn main() { |
|||
let isbns = ["978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"]; |
|||
isbns.iter().for_each(|isbn| println!("{}: {}", isbn, check_isbn(isbn))); |
|||
} |
|||
fn check_isbn(isbn: &str) -> bool { |
|||
if isbn.chars().filter(|c| c.is_digit(10)).count() != 13 { |
|||
return false; |
|||
} |
|||
let checksum = isbn.chars().filter_map(|c| c.to_digit(10)) |
|||
.zip([1, 3].iter().cycle()) |
|||
.fold(0, |acc, (val, fac)| acc + val * fac); |
|||
checksum % 10 == 0 |
|||
} |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
978-0596528126: true |
|||
978-0596528120: false |
|||
978-1788399081: true |
|||
978-1788399083: false |
|||
</pre> |
|||
=={{header|Seed7}}== |
|||
<syntaxhighlight lang="seed7">$ include "seed7_05.s7i"; |
|||
const func boolean: isISBN13 (in var string: input) is func |
|||
result |
|||
var boolean: isbn is FALSE; |
|||
local |
|||
var char: c is ' '; |
|||
var integer: digit is 0; |
|||
var integer: i is 1; |
|||
var integer: sum is 0; |
|||
begin |
|||
input := replace(input, " ", ""); |
|||
input := replace(input, "-", ""); |
|||
if length(input) = 13 then |
|||
for c range input do |
|||
digit := ord(c) - 48; |
|||
if not odd(i) then |
|||
digit *:= 3; |
|||
end if; |
|||
sum +:= digit; |
|||
incr(i); |
|||
end for; |
|||
isbn := sum rem 10 = 0; |
|||
end if; |
|||
end func; |
|||
const proc: main is func |
|||
local |
|||
var string: str is ""; |
|||
begin |
|||
for str range [] ("978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083") do |
|||
writeln(str <& ": " <& isISBN13(str)); |
|||
end for; |
|||
end func;</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
978-0596528126: TRUE |
|||
978-0596528120: FALSE |
|||
978-1788399081: TRUE |
|||
978-1788399083: FALSE |
|||
</pre> |
|||
=={{header|SETL}}== |
|||
<syntaxhighlight lang="setl">program isbn13; |
|||
loop for test in [ |
|||
"978-0596528126", "978-0596528120", |
|||
"978-1788399081", "978-1788399083" |
|||
] do |
|||
print(test + if valid_isbn13 test then ": good" else ": bad" end); |
|||
end loop; |
|||
op valid_isbn13(isbn); |
|||
if #isbn /= 14 |
|||
or isbn(4) /= '-' |
|||
or exists c in isbn | not c in "0123456789-" then |
|||
return false; |
|||
end if; |
|||
m := 3; |
|||
loop for ch in isbn | ch in "0123456789" do |
|||
s +:= val ch * (m := 4 - m); |
|||
end loop; |
|||
return s mod 10 = 0; |
|||
end op; |
|||
end program;</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126: good |
|||
978-0596528120: bad |
|||
978-1788399081: good |
|||
978-1788399083: bad</pre> |
|||
=={{header|Standard ML}}== |
|||
===Easy to read version=== |
|||
<syntaxhighlight lang="sml">(* these type decorations are optional, you could just as well put: |
|||
fun isValidISBN s = |
|||
*) |
|||
fun isValidISBN (s : string) : bool = |
|||
let |
|||
val digits = List.filter Char.isDigit (explode s) |
|||
val nums = map (fn x => ord x - ord #"0") digits |
|||
val len = length nums |
|||
fun sumISBN [] = raise Domain |
|||
| sumISBN [x] = x |
|||
| sumISBN (x1::x2::xs) = x1 + 3*x2 + sumISBN xs |
|||
in |
|||
len = 13 andalso sumISBN nums mod 10 = 0 |
|||
end |
|||
val test = ["978-1734314502", "978-1734314509", |
|||
"978-1788399081", "978-1788399083"] |
|||
fun printTest (s : string) : unit = |
|||
(print s; print (if isValidISBN s then ": good\n" else ": bad\n")) |
|||
fun main () = app printTest test</syntaxhighlight> |
|||
===Original version=== |
|||
<syntaxhighlight lang="sml">let |
|||
fun check (c, p as (m, n)) = |
|||
if Char.isDigit c then |
|||
(not m, (if m then 3 * (ord c - 48) else ord c - 48) + n) |
|||
else |
|||
p |
|||
in |
|||
fun checkISBN s = |
|||
Int.rem (#2 (CharVector.foldl check (false, 0) s), 10) = 0 |
|||
end |
|||
val test = ["978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"] |
|||
val () = (print |
|||
o concat |
|||
o map (fn s => s ^ (if checkISBN s then ": good\n" else ": bad\n"))) test</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126: good |
|||
978-0596528120: bad |
|||
978-1788399081: good |
|||
978-1788399083: bad</pre> |
|||
=={{header|Swift}}== |
=={{header|Swift}}== |
||
< |
<syntaxhighlight lang="swift">func checkISBN(isbn: String) -> Bool { |
||
guard !isbn.isEmpty else { |
guard !isbn.isEmpty else { |
||
return false |
return false |
||
Line 835: | Line 3,179: | ||
let cases = [ |
let cases = [ |
||
"978- |
"978-0596528126", |
||
"978- |
"978-0596528120", |
||
"978-1788399081", |
"978-1788399081", |
||
"978-1788399083" |
"978-1788399083" |
||
Line 843: | Line 3,187: | ||
for isbn in cases { |
for isbn in cases { |
||
print("\(isbn) => \(checkISBN(isbn: isbn) ? "good" : "bad")") |
print("\(isbn) => \(checkISBN(isbn: isbn) ? "good" : "bad")") |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>978- |
<pre>978-0596528126 => good |
||
978- |
978-0596528120 => bad |
||
978-1788399081 => good |
978-1788399081 => good |
||
978-1788399083 => bad</pre> |
978-1788399083 => bad</pre> |
||
=={{header|Tcl}}== |
|||
<syntaxhighlight lang="tcl"> |
|||
proc validISBN13 code { |
|||
regsub -all {\D} $code "" code ;# remove non-digits |
|||
if {[string length $code] == 13} { |
|||
set sum 0 |
|||
set fac 1 |
|||
foreach digit [split $code ""] { |
|||
set sum [expr {$sum + $digit * $fac}] |
|||
set fac [expr {$fac == 1? 3: 1}] |
|||
} |
|||
if {$sum % 10 == 0} {return true} |
|||
} |
|||
return false |
|||
} |
|||
foreach test { |
|||
978-0596528126 |
|||
978-0596528120 |
|||
978-1788399081 |
|||
978-1788399083 |
|||
} {puts $test:[validISBN13 $test]} |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126:true |
|||
978-0596528120:false |
|||
978-1788399081:true |
|||
978-1788399083:false</pre> |
|||
Simpler variant, using two loop vars and constant factors; same output: |
|||
<syntaxhighlight lang="tcl"> |
|||
proc validISBN13 code { |
|||
regsub -all {\D} $code "" code ;# remove non-digits |
|||
if {[string length $code] == 13} { |
|||
set sum 0 |
|||
foreach {d1 d2} [split $code ""] { |
|||
if {$d2 eq ""} {set d2 0} ;# last round |
|||
set sum [expr {$sum + $d1 + $d2 * 3}] |
|||
} |
|||
if {$sum % 10 == 0} {return true} |
|||
} |
|||
return false |
|||
} |
|||
</syntaxhighlight> |
|||
=={{header|uBasic/4tH}}== |
|||
{{works with|version 3.64.0}} |
|||
<syntaxhighlight lang="text">a := "978-0596528126" |
|||
Print Show(a), Show (Iif (Func(_IsISBN (a)), "good", "bad")) |
|||
a := "978-0596528120" |
|||
Print Show(a), Show (Iif (Func(_IsISBN (a)), "good", "bad")) |
|||
a := "978-1788399081" |
|||
Print Show(a), Show (Iif (Func(_IsISBN (a)), "good", "bad")) |
|||
a := "978-1788399083" |
|||
Print Show(a), Show (Iif (Func(_IsISBN (a)), "good", "bad")) |
|||
End |
|||
_IsISBN |
|||
Param (1) |
|||
Local (4) |
|||
c@ = 0 : e@ = 1 ' set sum and multiplier |
|||
For b@ = Len (a@) - 1 To 0 Step -1 ' scan string in reverse |
|||
d@ = Peek(a@, b@) - Ord ("0") ' get character |
|||
If ((d@ < 0) + (d@ > 9)) = 0 Then c@ = c@ + (d@ * e@) |
|||
e@ = (e@ * 3) % 8 ' evaluate character, change multiplier |
|||
Next |
|||
Return ((c@ % 10) = 0) ' modulus 10 must be zero</syntaxhighlight> |
|||
{{out}} |
|||
<pre>978-0596528126 good |
|||
978-0596528120 bad |
|||
978-1788399081 good |
|||
978-1788399083 bad |
|||
0 OK, 0:339</pre> |
|||
=={{header|UNIX Shell}}== |
|||
{{works with|Bourne Again Shell}} |
|||
<syntaxhighlight lang="sh">check_isbn13 () { |
|||
local i n t |
|||
n=${1//[^0-9]/} |
|||
t=0 |
|||
for ((i=0; i<${#n}; ++i )); do |
|||
(( t += ${n:i:1}*(1 + ((i&1)<<1)) )) |
|||
done |
|||
(( 0 == t % 10 )) |
|||
} |
|||
for isbn in 978-0596528126 978-0596528120 978-1788399081 978-1788399083; do |
|||
printf '%s: ' "$isbn" |
|||
if check_isbn13 "$isbn"; then |
|||
printf '%s\n' OK |
|||
else |
|||
printf '%s\n' 'NOT OK' |
|||
fi |
|||
done</syntaxhighlight> |
|||
{{Out}} |
|||
<pre>978-0596528126: OK |
|||
978-0596528120: NOT OK |
|||
978-1788399081: OK |
|||
978-1788399083: NOT OK</pre> |
|||
=={{header|Visual Basic .NET}}== |
|||
{{trans|C#}} |
|||
<syntaxhighlight lang="vbnet">Module Module1 |
|||
Function CheckISBN13(code As String) As Boolean |
|||
code = code.Replace("-", "").Replace(" ", "") |
|||
If code.Length <> 13 Then |
|||
Return False |
|||
End If |
|||
Dim sum = 0 |
|||
For Each i_d In code.Select(Function(digit, index) (index, digit)) |
|||
Dim index = i_d.index |
|||
Dim digit = i_d.digit |
|||
If Char.IsDigit(digit) Then |
|||
sum += (Asc(digit) - Asc("0")) * If(index Mod 2 = 0, 1, 3) |
|||
Else |
|||
Return False |
|||
End If |
|||
Next |
|||
Return sum Mod 10 = 0 |
|||
End Function |
|||
Sub Main() |
|||
Console.WriteLine(CheckISBN13("978-0596528126")) |
|||
Console.WriteLine(CheckISBN13("978-0596528120")) |
|||
Console.WriteLine(CheckISBN13("978-1788399081")) |
|||
Console.WriteLine(CheckISBN13("978-1788399083")) |
|||
End Sub |
|||
End Module</syntaxhighlight> |
|||
{{out}} |
|||
<pre>True |
|||
False |
|||
True |
|||
False</pre> |
|||
=={{header|V (Vlang)}}== |
|||
{{trans|Go}} |
|||
<syntaxhighlight lang="v (vlang)"> |
|||
fn check_isbn13(isbn13 string) bool { |
|||
// remove any hyphens or spaces |
|||
isbn := isbn13.replace('-','').replace(' ','') |
|||
// check length == 13 |
|||
le := isbn.len |
|||
if le != 13 { |
|||
return false |
|||
} |
|||
// check only contains digits and calculate weighted sum |
|||
mut sum := 0 |
|||
for i, c in isbn.split('') { |
|||
if c.int() < '0'.int() || c.int() > '9'.int() { |
|||
return false |
|||
} |
|||
if i%2 == 0 { |
|||
sum += c.int() - '0'.int() |
|||
} else { |
|||
sum += 3 * (c.int() - '0'.int()) |
|||
} |
|||
} |
|||
return sum%10 == 0 |
|||
} |
|||
fn main() { |
|||
isbns := ["978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"] |
|||
for isbn in isbns { |
|||
mut res := "bad" |
|||
if check_isbn13(isbn) { |
|||
res = "good" |
|||
} |
|||
println("$isbn: $res") |
|||
} |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
978-0596528126: good |
|||
978-0596528120: bad |
|||
978-1788399081: good |
|||
978-1788399083: bad |
|||
</pre> |
|||
=={{header|Wren}}== |
|||
<syntaxhighlight lang="wren">var isbn13 = Fn.new { |s| |
|||
var cps = s.codePoints |
|||
var digits = [] |
|||
// extract digits |
|||
for (i in 0...cps.count) { |
|||
var c = cps[i] |
|||
if (c >= 48 && c <= 57) digits.add(c) |
|||
} |
|||
// do calcs |
|||
var sum = 0 |
|||
for (i in 0...digits.count) { |
|||
var d = digits[i] - 48 |
|||
sum = sum + ((i%2 == 0) ? d : 3 * d) |
|||
} |
|||
return sum % 10 == 0 |
|||
} |
|||
var tests = ["978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"] |
|||
for (test in tests) { |
|||
System.print("%(test) -> %(isbn13.call(test) ? "good" : "bad")") |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
978-0596528126 -> good |
|||
978-0596528120 -> bad |
|||
978-1788399081 -> good |
|||
978-1788399083 -> bad |
|||
</pre> |
|||
=={{header|XPL0}}== |
=={{header|XPL0}}== |
||
< |
<syntaxhighlight lang="xpl0">include xpllib; \contains StrLen function |
||
proc ISBN13(Str); \Show if International Standard Book Number is good |
proc ISBN13(Str); \Show if International Standard Book Number is good |
||
Line 874: | Line 3,437: | ||
]; |
]; |
||
[ISBN13("978- |
[ISBN13("978-0596528126"); |
||
ISBN13("978- |
ISBN13("978-0596528120"); |
||
ISBN13("978-1788399081"); |
ISBN13("978-1788399081"); |
||
ISBN13("978-1788399083"); |
ISBN13("978-1788399083"); |
||
ISBN13("978-1-59327-220-3"); |
ISBN13("978-1-59327-220-3"); |
||
ISBN13("978-178839918"); |
ISBN13("978-178839918"); |
||
]</ |
]</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
978- |
978-0596528126: good |
||
978- |
978-0596528120: bad |
||
978-1788399081: good |
978-1788399081: good |
||
978-1788399083: bad |
978-1788399083: bad |
||
Line 893: | Line 3,456: | ||
=={{header|zkl}}== |
=={{header|zkl}}== |
||
< |
<syntaxhighlight lang="zkl">fcn ISBN13_check(isbn){ // "978-0596528126", throws on invalid digits |
||
var [const] one3=("13"*6 + 1).split("").apply("toInt"); // examine 13 digits |
var [const] one3=("13"*6 + 1).split("").apply("toInt"); // examine 13 digits |
||
// one3=("13"*6) if you want to calculate what the check digit should be |
// one3=("13"*6) if you want to calculate what the check digit should be |
||
one3.zipWith('*,isbn - " -").sum(0) % 10 == 0 |
one3.zipWith('*,isbn - " -").sum(0) % 10 == 0 |
||
}</ |
}</syntaxhighlight> |
||
< |
<syntaxhighlight lang="zkl">isbns:= |
||
#<<<" |
#<<<" |
||
978- |
978-0596528126 |
||
978- |
978-0596528120 |
||
978-1788399081 |
978-1788399081 |
||
978-1788399083 |
978-1788399083 |
||
Line 908: | Line 3,471: | ||
#<<< |
#<<< |
||
foreach isbn in (isbns) |
foreach isbn in (isbns) |
||
{ println(isbn.strip()," ",ISBN13_check(isbn) and " Good" or " Bad") }</ |
{ println(isbn.strip()," ",ISBN13_check(isbn) and " Good" or " Bad") }</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
978- |
978-0596528126 Good |
||
978- |
978-0596528120 Bad |
||
978-1788399081 Good |
978-1788399081 Good |
||
978-1788399083 Bad |
978-1788399083 Bad |
Latest revision as of 05:53, 26 June 2024
Validate the check digit of an ISBN-13 code:
You are encouraged to solve this task according to the task description, using any language you may know.
- Task
- Multiply every other digit by 3.
- Add these numbers and the other digits.
- Take the remainder of this number after division by 10.
- If it is 0, the ISBN-13 check digit is correct.
You might use the following codes for testing:
- 978-0596528126 (good)
- 978-0596528120 (bad)
- 978-1788399081 (good)
- 978-1788399083 (bad)
Show output here, on this page
- See also
-
- for details: 13-digit ISBN method of validation. (installs cookies.)
11l
F is_isbn13(=n)
n = n.replace(‘-’, ‘’).replace(‘ ’, ‘’)
I n.len != 13
R 0B
V product = sum(n[(0..).step(2)].map(ch -> Int(ch)))
+ sum(n[(1..).step(2)].map(ch -> Int(ch) * 3))
R product % 10 == 0
V tests = |‘978-0596528126
978-0596528120
978-1788399081
978-1788399083’.split("\n")
L(t) tests
print(‘ISBN13 ’t‘ validates ’is_isbn13(t))
- Output:
ISBN13 978-0596528126 validates 1B ISBN13 978-0596528120 validates 0B ISBN13 978-1788399081 validates 1B ISBN13 978-1788399083 validates 0B
8080 Assembly
org 100h
jmp demo
;;; ---------------------------------------------------------------
;;; Check if the string at BC is a valid ISBN-13 code.
;;; Carry set if true, clear if not.
isbn13: lxi h,0 ; HL = accumulator
mov d,h ; D = 0 (such that if E=A, DE=A).
call isbngc ; Get first character
rnc ; Carry clear = invalid
dad d ; Add to running total once
call isbngc ; Get second character
rnc ; Carry clear = invalid
dad d ; Add to running total thrice
dad d
dad d
call isbngc ; Get third character
rnc ; Carry clear = invalid
dad d ; Add to running total once
ldax b ; Fourth character should be a dash '-'
inx b
cpi '-'
stc ; Clear carry w/o touching other flags
cmc
rnz ; If not equal, invalid.
push h ; Keep loop counter on stack
mvi l,5 ; 5 times 2 characters
isbnlp: xthl ; Accumulator in HL
call isbngc ; Get even character
jnc isbnex ; If invalid, stop
dad d ; Add to running total thrice
dad d
dad d
call isbngc ; Get odd character
jnc isbnex ; If invalid, stop
dad d ; Add to running total once
xthl ; Loop counter in (H)L
dcr l ; Done yet?
jnz isbnlp ; If not, do next two characters
pop h ; Get accumulator
lxi d,-10 ; Trial division by ten
isbndv: dad d ; Subtract 10
jc isbndv ; Until zero passed
mov a,l ; Move low byte to A
adi 10 ; Add ten back (the mod loop went one step too far)
rz ; If zero, return (carry will have been set)
ana a ; Otherwise, make sure carry is clear
ret ; And then return
isbnex: pop h ; Test failed - throw away accumulator and return
ret
isbngc: ldax b ; Get character from [BC]
inx b ; Increment BC
sui '0' ; Subtract ASCII '0' to get digit value
cpi 10 ; If 10 or higher (unsigned), invalid digit.
mov e,a ; Set (D)E = value
ret
;;; ---------------------------------------------------------------
;;; Demo: see if the CP/M command line contains a valid ISBN13
;;; code.
demo: lxi b,82h ; Start of command line argument, skipping first space
call isbn13 ; Is it valid?
mvi c,9 ; CP/M print string
lxi d,good ; If carry is set, then yes
jc 5
lxi d,bad ; Otherwise, no.
jmp 5
good: db 'good$'
bad: db 'bad$'
- Output:
A>isbn13 978-0596528126 good A>isbn13 978-0596528120 bad A>isbn13 978-1788399081 good A>isbn13 978-1788399083 bad
8086 Assembly
cpu 8086
bits 16
org 100h
section .text
jmp demo
isbn13: ;;; ---------------------------------------------------------------
;;; Check if the string at DS:SI is a valid ISBN-13 code.
;;; Carry set if true, clear if false.
xor dx,dx ; DX = running total
xor ah,ah ; Set AH=0 so that AX=AL
call .digit ; Get first digit and add to DX
call .digit ; Get second digit and add to DX
add dx,ax ; Add to DX twice more
add dx,ax
call .digit ; Get third digit and add to DX
lodsb ; Fourth character should be a '-'
cmp al,'-'
jne .fail ; If not equal, fail
mov cx,5 ; Then loop 5 times for the next 10 digits
.loop: call .digit ; Get even digit and add to DX
add dx,ax ; Add to running total twice more
add dx,ax
call .digit ; Get odd digit and add to DX
loop .loop ; Do this 5 times
mov ax,dx ; Divide running total by 10
mov dl,10
div dl
test ah,ah ; Is the remainder zero?
jnz .out ; If not, stop (TEST clears carry)
stc ; Otherwise, set carry and return
ret
.digit: lodsb ; Get first character
sub al,'0' ; Subtract ASCII 0 to get digit value
cmp al,9
ja .dfail
add dx,ax ; Add to ASCII
ret
.dfail: pop dx ; Remove return pointer for .digit from stack
.fail: clc ; Failure - clear carry
.out: ret
;;; ---------------------------------------------------------------
;;; Demo: see if the MS-DOS command line contains a valid ISBN13
;;; code.
demo: mov si,82h ; Start of command line argument skipping space
call isbn13 ; Is it valid?
mov ah,9 ; MS-DOS print string
mov dx,good ; If carry is set, it is good
jc .print
mov dx,bad ; Otherwise, it is bad
.print: int 21h
ret
section .data
good: db 'good$'
bad: db 'bad$'
- Output:
C:\>isbn13 978-0596528126 good C:\>isbn13 978-0596528120 bad C:\>isbn13 978-1788399081 good C:\>isbn13 978-1788399083 bad
ABC
HOW TO REPORT valid.isbn13 str:
PUT {} IN digits
FOR d IN {0..9}: PUT d IN digits["`d`"]
IF #str <> 14 OR str item 4 <> '-': FAIL
PUT 1, 0 IN mul, sum
FOR c IN str|3 ^ str@5:
IF c not.in keys digits: FAIL
PUT sum + digits[c] * mul IN sum
PUT 4 - mul IN mul
REPORT sum mod 10 = 0
PUT {} IN tests
PUT "978-0596528126" IN tests[1]
PUT "978-0596528120" IN tests[2]
PUT "978-1788399081" IN tests[3]
PUT "978-1788399083" IN tests[4]
FOR test IN tests:
SELECT:
valid.isbn13 test: WRITE test^": good"/
ELSE: WRITE test^": bad"/
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
Action!
INCLUDE "D2:CHARTEST.ACT" ;from the Action! Tool Kit
BYTE FUNC CheckISBN13(CHAR ARRAY t)
BYTE i,index,sum,v
sum=0 index=0
FOR i=1 TO t(0)
DO
v=t(i)
IF IsDigit(v) THEN
v==-'0
IF index MOD 2=1 THEN
v==*3
FI
sum==+v
index==+1
ELSEIF v#' AND v#'- THEN
RETURN (0)
FI
OD
IF index#13 OR sum MOD 10#0 THEN
RETURN (0)
FI
RETURN (1)
PROC Test(CHAR ARRAY t)
BYTE correct
correct=CheckISBN13(t)
Print(t) Print(" is ")
IF correct THEN
PrintE("correct")
ELSE
PrintE("incorrect")
FI
RETURN
PROC Main()
Put(125) PutE() ;clear screen
Test("978-0596528126")
Test("978-0596528120")
Test("978-1788399081")
Test("978-1788399083")
RETURN
- Output:
Screenshot from Atari 8-bit computer
978-0596528126 is correct 978-0596528120 is incorrect 978-1788399081 is correct 978-1788399083 is incorrect
Ada
with Ada.Text_IO;
procedure ISBN_Check is
function Is_Valid (ISBN : String) return Boolean is
Odd : Boolean := True;
Sum : Integer := 0;
Value : Integer;
begin
for I in ISBN'Range loop
if ISBN (I) in '0' .. '9' then
Value := Character'Pos (ISBN (I)) - Character'Pos ('0');
if Odd then
Sum := Sum + Value;
else
Sum := Sum + 3 * Value;
end if;
Odd := not Odd;
end if;
end loop;
return Sum mod 10 = 0;
end Is_Valid;
procedure Show (ISBN : String) is
use Ada.Text_IO;
Valid : constant Boolean := Is_Valid (ISBN);
begin
Put (ISBN); Put (" ");
Put ((if Valid then "Good" else "Bad"));
New_Line;
end Show;
begin
Show ("978-0596528126");
Show ("978-0596528120");
Show ("978-1788399081");
Show ("978-1788399083");
end ISBN_Check;
- Output:
978-0596528126 Good 978-0596528120 Bad 978-1788399081 Good 978-1788399083 Bad
ALGOL 68
BEGIN # Check some IsBN13 check digits #
# returns TRUE if the alledged isbn13 has the correct check sum, #
# FALSE otherwise #
# non-digit characters are ignored and there must be 13 digits #
PROC check isbn13 = ( STRING isbn13 )BOOL:
BEGIN
INT sum := 0;
INT digits := 0;
BOOL other digit := FALSE;
FOR pos FROM LWB isbn13 TO UPB isbn13 DO
IF CHAR c = isbn13[ pos ];
c >= "0" AND c <= "9"
THEN
# have another digit #
digits +:= 1;
sum +:= ( ABS c - ABS "0" ) * IF other digit THEN 3 ELSE 1 FI;
other digit := NOT other digit
FI
OD;
digits = 13 AND sum MOD 10 = 0
END; # check isbn13 #
# task test cases #
[]STRING tests = ( "978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083" );
[]BOOL expected = ( TRUE, FALSE, TRUE, FALSE );
FOR pos FROM LWB tests TO UPB tests DO
BOOL result = check isbn13( tests[ pos ] );
print( ( tests[ pos ]
, ": "
, IF result THEN "good" ELSE "bad" FI
, IF result = expected[ pos ] THEN "" ELSE " NOT AS EXPECTED" FI
, newline
)
)
OD
END
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
APL
check_isbn13←{
13≠⍴n←(⍵∊⎕D)/⍵:0
0=10|(⍎¨n)+.×13⍴1 3
}
- Output:
check_isbn13¨ '978-0596528126' '978-0596528120' '978-1788399081' '978-1788399083' 1 0 1 0
AppleScript
Composition of pure functions
-------------------- ISBN13 CHECK DIGIT --------------------
-- isISBN13 :: String -> Bool
on isISBN13(s)
script digitValue
on |λ|(c)
if isDigit(c) then
{c as integer}
else
{}
end if
end |λ|
end script
set digits to concatMap(digitValue, characters of s)
13 = length of digits ¬
and 0 = sum(zipWith(my mul, digits, cycle({1, 3}))) mod 10
end isISBN13
--------------------------- TEST ---------------------------
on run
script test
on |λ|(s)
{s, isISBN13(s)}
end |λ|
end script
map(test, {"978-0596528126", "978-0596528120", ¬
"978-1788399081", "978-1788399083"})
end run
-------------------- GENERIC FUNCTIONS ---------------------
-- concatMap :: (a -> [b]) -> [a] -> [b]
on concatMap(f, xs)
set lng to length of xs
set acc to {}
tell mReturn(f)
repeat with i from 1 to lng
set acc to acc & (|λ|(item i of xs, i, xs))
end repeat
end tell
return acc
end concatMap
-- 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
-- isDigit :: Char -> Bool
on isDigit(c)
set n to (id of c)
48 ≤ n and 57 ≥ n
end isDigit
-- 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|
-- 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
-- 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
-- mul (*) :: Num a => a -> a -> a
on mul(a, b)
a * b
end mul
-- sum :: [Num] -> Num
on sum(xs)
script add
on |λ|(a, b)
a + b
end |λ|
end script
foldl(add, 0, xs)
end sum
-- 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:
{{"978-0596528126", true}, {"978-0596528120", false}, {"978-1788399081", true}, {"978-1788399083", false}}
Straightforward
This task can be tackled very simply by working through the numeric text two characters at a time:
on validateISBN13(ISBN13)
if (ISBN13's class is not text) then return false
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to {"-", space}
set ISBN13 to ISBN13's text items
set AppleScript's text item delimiters to ""
set ISBN13 to ISBN13 as text
set AppleScript's text item delimiters to astid
if (((count ISBN13) is not 13) or (ISBN13 contains ".") or (ISBN13 contains ",")) then return false
try
ISBN13 as number
on error
return false
end try
set sum to 0
repeat with i from 1 to 12 by 2
set sum to sum + (character i of ISBN13) + (character (i + 1) of ISBN13) * 3 -- Automatic text-to-number coercions.
end repeat
return ((sum + (character 13 of ISBN13)) mod 10 = 0)
end validateISBN13
-- Test:
set output to {}
set verdicts to {"bad", "good"}
repeat with thisISBN13 in {"978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"}
set isValid to validateISBN13(thisISBN13)
set end of output to thisISBN13 & ": " & item ((isValid as integer) + 1) of verdicts
end repeat
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to linefeed
set output to output as text
set AppleScript's text item delimiters to astid
return output
- Output:
"978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad"
Or it can be handled purely numerically. Since the "weights" alternate and are palindromic, it makes no difference whether the last digit or the first is treated as the check digit. In fact, if preferred, the repeat below can go round 7 times with the return line as simply: return (sum mod 10 = 0).
on validateISBN13(ISBN13)
if (ISBN13's class is not text) then return false
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to {"-", space}
set ISBN13 to ISBN13's text items
set AppleScript's text item delimiters to ""
set ISBN13 to ISBN13 as text
set AppleScript's text item delimiters to astid
if (((count ISBN13) is not 13) or (ISBN13 contains ".") or (ISBN13 contains ",")) then return false
try
set ISBN13 to ISBN13 as number
on error
return false
end try
set sum to 0
repeat 6 times
set sum to sum + ISBN13 mod 10 + ISBN13 mod 100 div 10 * 3
set ISBN13 to ISBN13 div 100
end repeat
return ((sum + ISBN13) mod 10 = 0)
end validateISBN13
Arturo
validISBN?: function [isbn][
currentCheck: to :integer to :string last isbn
isbn: map split chop replace isbn "-" "" 'd -> to :integer d
s: 0
loop.with:'i isbn 'n [
if? even? i -> s: s + n
else -> s: s + 3*n
]
checkDigit: 10 - s % 10
return currentCheck = checkDigit
]
tests: [
"978-0596528126" "978-0596528120"
"978-1788399081" "978-1788399083"
]
loop tests 'test [
print [test "=>" validISBN? test]
]
- Output:
978-0596528126 => true 978-0596528120 => false 978-1788399081 => true 978-1788399083 => false
AutoHotkey
ISBN13_check_digit(n){
for i, v in StrSplit(RegExReplace(n, "[^0-9]"))
sum += !Mod(i, 2) ? v*3 : v
return n "`t" (Mod(sum, 10) ? "(bad)" : "(good)")
}
Examples:
output := ""
nums := ["978-0596528126","978-0596528120","978-1788399081","978-1788399083"]
for i, n in nums
output .= ISBN13_check_digit(n) "`n"
MsgBox % output
return
- Output:
978-0596528126 (good) 978-0596528120 (bad) 978-1788399081 (good) 978-1788399083 (bad)
AWK
# syntax: GAWK -f ISBN13_CHECK_DIGIT.AWK
BEGIN {
arr[++n] = "978-0596528126"
arr[++n] = "978-0596528120"
arr[++n] = "978-1788399081"
arr[++n] = "978-1788399083"
arr[++n] = "9780820424521"
arr[++n] = "0820424528"
for (i=1; i<=n; i++) {
printf("%s %s\n",arr[i],isbn13(arr[i]))
}
exit(0)
}
function isbn13(isbn, check_digit,i,sum) {
gsub(/[ -]/,"",isbn)
if (length(isbn) != 13) { return("NG length") }
for (i=1; i<=12; i++) {
sum += substr(isbn,i,1) * (i % 2 == 1 ? 1 : 3)
}
check_digit = 10 - (sum % 10)
return(substr(isbn,13,1) == check_digit ? "OK" : sprintf("NG check digit S/B %d",check_digit))
}
- Output:
978-0596528126 OK 978-0596528120 NG check digit S/B 2 978-1788399081 OK 978-1788399083 NG check digit S/B 1 9780820424521 OK 0820424528 NG length
BASIC256
arraybase 1
dim isbn = {"978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083", "978-2-74839-908-0", "978-2-74839-908-5", "978 1 86197 876 9"}
for n = 1 to isbn[?]
sum = 0
isbnStr = isbn[n]
isbnStr = replace(isbnStr, "-", "")
isbnStr = replace(isbnStr, " ", "")
for m = 1 to length(isbnStr)
if m mod 2 = 0 then
num = 3 * int(mid(isbnStr, m, 1))
else
num = int(mid(isbnStr, m, 1))
end if
sum += num
next m
if sum mod 10 = 0 then
print isbn[n]; ": good"
else
print isbn[n]; ": bad"
end if
next n
BCPL
get "libhdr"
let checkISBN(s) = valof
$( let tally = 0
unless s%0 = 14 resultis false
unless s%4 = '-' resultis false
for i=1 to 3
$( let digit = s%i-'0'
test i rem 2 = 0
do tally := tally + 3 * digit
or tally := tally + digit
$)
for i=5 to 14
$( let digit = s%i-'0'
test i rem 2 = 0
do tally := tally + digit
or tally := tally + 3 * digit
$)
resultis tally rem 10 = 0
$)
let show(s) be
writef("%S: %S*N", s, checkISBN(s) -> "good", "bad")
let start() be
$( show("978-0596528126")
show("978-0596528120")
show("978-1788399081")
show("978-1788399083")
$)
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
C
#include <stdio.h>
int check_isbn13(const char *isbn) {
int ch = *isbn, count = 0, sum = 0;
/* check isbn contains 13 digits and calculate weighted sum */
for ( ; ch != 0; ch = *++isbn, ++count) {
/* skip hyphens or spaces */
if (ch == ' ' || ch == '-') {
--count;
continue;
}
if (ch < '0' || ch > '9') {
return 0;
}
if (count & 1) {
sum += 3 * (ch - '0');
} else {
sum += ch - '0';
}
}
if (count != 13) return 0;
return !(sum%10);
}
int main() {
int i;
const char* isbns[] = {"978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"};
for (i = 0; i < 4; ++i) {
printf("%s: %s\n", isbns[i], check_isbn13(isbns[i]) ? "good" : "bad");
}
return 0;
}
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
C++
#include <iostream>
bool check_isbn13(std::string isbn) {
int count = 0;
int sum = 0;
for (auto ch : isbn) {
/* skip hyphens or spaces */
if (ch == ' ' || ch == '-') {
continue;
}
if (ch < '0' || ch > '9') {
return false;
}
if (count & 1) {
sum += 3 * (ch - '0');
} else {
sum += ch - '0';
}
count++;
}
if (count != 13) {
return false;
}
return sum % 10 == 0;
}
int main() {
auto isbns = { "978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083" };
for (auto isbn : isbns) {
std::cout << isbn << ": " << (check_isbn13(isbn) ? "good" : "bad") << '\n';
}
return 0;
}
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
C#
using System;
using System.Linq;
public class Program
{
public static void Main() {
Console.WriteLine(CheckISBN13("978-0596528126"));
Console.WriteLine(CheckISBN13("978-0596528120"));
Console.WriteLine(CheckISBN13("978-1788399081"));
Console.WriteLine(CheckISBN13("978-1788399083"));
static bool CheckISBN13(string code) {
code = code.Replace("-", "").Replace(" ", "");
if (code.Length != 13) return false;
int sum = 0;
foreach (var (index, digit) in code.Select((digit, index) => (index, digit))) {
if (char.IsDigit(digit)) sum += (digit - '0') * (index % 2 == 0 ? 1 : 3);
else return false;
}
return sum % 10 == 0;
}
}
}
- Output:
True False True False
CLU
isbn13_check = proc (s: string) returns (bool)
if string$size(s) ~= 14 then return(false) end
if s[4] ~= '-' then return(false) end
begin
check: int := 0
for i: int in int$from_to(1, 14) do
if i=4 then continue end
d: int := int$parse(string$c2s(s[i]))
if i=2 cor (i>4 cand i//2=1) then d := d*3 end
check := check + d
end
return(check//10 = 0)
end except when bad_format:
return(false)
end
end isbn13_check
start_up = proc ()
po: stream := stream$primary_output()
tests: array[string] := array[string]$
["978-0596528126",
"978-0596528120",
"978-1788399081",
"978-1788399083"]
for test: string in array[string]$elements(tests) do
stream$puts(po, test)
stream$puts(po, ": ")
if isbn13_check(test)
then stream$putl(po, "good")
else stream$putl(po, "bad")
end
end
end start_up
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
COBOL
******************************************************************
* Author: Jay Moseley
* Date: November 10, 2019
* Purpose: Testing various subprograms/ functions.
* Tectonics: cobc -xj testSubs.cbl
******************************************************************
IDENTIFICATION DIVISION.
PROGRAM-ID. testSubs.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
FUNCTION ALL INTRINSIC
FUNCTION validISBN13.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
DATA DIVISION.
FILE SECTION.
WORKING-STORAGE SECTION.
01 IX PIC S9(4) COMP.
01 TEST-ISBNS.
02 FILLER PIC X(14) VALUE '978-0596528126'.
02 FILLER PIC X(14) VALUE '978-0596528120'.
02 FILLER PIC X(14) VALUE '978-1788399081'.
02 FILLER PIC X(14) VALUE '978-1788399083'.
01 TEST-ISBN REDEFINES TEST-ISBNS
OCCURS 4 TIMES
PIC X(14).
PROCEDURE DIVISION.
MAIN-PROCEDURE.
PERFORM
VARYING IX
FROM 1
BY 1
UNTIL IX > 4
DISPLAY TEST-ISBN (IX) ' ' WITH NO ADVANCING
END-DISPLAY
IF validISBN13(TEST-ISBN (IX)) = -1
DISPLAY '(bad)'
ELSE
DISPLAY '(good)'
END-IF
END-PERFORM.
GOBACK.
END PROGRAM testSubs.
******************************************************************
* Author: Jay Moseley
* Date: May 19, 2016
* Purpose: validate ISBN-13 (International Standard
* Book Number).
******************************************************************
IDENTIFICATION DIVISION.
FUNCTION-ID. validISBN13.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
FUNCTION ALL INTRINSIC.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
DATA DIVISION.
FILE SECTION.
WORKING-STORAGE SECTION.
01 PASSED-SIZE PIC S9(6) COMP-5.
01 IX PIC S9(4) COMP.
01 WORK-FIELDS.
02 WF-DIGIT PIC X.
02 WF-COUNT PIC 9(2).
88 WEIGHT-1 VALUE 1, 3, 5, 7, 9, 11, 13.
88 WEIGHT-3 VALUE 2, 4, 6, 8, 10, 12.
02 WF-SUM PIC S9(8) COMP.
LINKAGE SECTION.
01 PASSED-ISBN PIC X ANY LENGTH.
01 RETURN-VALUE PIC S9.
PROCEDURE DIVISION USING PASSED-ISBN
RETURNING RETURN-VALUE.
CALL 'C$PARAMSIZE'
USING 1
GIVING PASSED-SIZE
END-CALL.
COMPUTE-CKDIGIT.
INITIALIZE WORK-FIELDS.
PERFORM
VARYING IX
FROM 1
BY 1
UNTIL IX GREATER THAN PASSED-SIZE
MOVE PASSED-ISBN (IX:1) TO WF-DIGIT
IF WF-DIGIT IS NUMERIC
ADD 1 TO WF-COUNT
IF WEIGHT-1
ADD NUMVAL(WF-DIGIT) TO WF-SUM
ELSE
COMPUTE WF-SUM = WF-SUM +
(NUMVAL(WF-DIGIT) * 3)
END-COMPUTE
END-IF
END-IF
END-PERFORM.
IF MOD(WF-SUM, 10) = 0
MOVE +0 TO RETURN-VALUE
ELSE
MOVE -1 TO RETURN-VALUE
END-IF.
GOBACK.
* - - - - - - - - - - - - - - - - - - - - - - PROGRAM EXIT POINT
END FUNCTION validISBN13.
- Output:
978-0596528126 (good) 978-0596528120 (bad) 978-1788399081 (good) 978-1788399083 (bad)
Cowgol
include "cowgol.coh";
sub check_isbn13(isbn: [uint8]): (r: uint8) is
var n: uint8 := 0;
r := 0;
loop
var c := [isbn];
isbn := @next isbn;
if c == 0 then break; end if;
c := c - '0';
if c <= 9 then
r := r + c;
n := n + 1;
if (n & 1) == 0 then
r := r + c * 2;
end if;
end if;
end loop;
if n == 13 and r%10 == 0 then
r := 1;
else
r := 0;
end if;
end sub;
var isbns: [uint8][] := {
"978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"
};
var result: [uint8][] := {": bad\n", ": good\n"};
var n: uint8 := 0;
while n < @sizeof isbns loop
print(isbns[n]);
print(result[check_isbn13(isbns[n])]);
n := n + 1;
end loop;
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
D
import std.stdio;
bool isValidISBN13(string text) {
int sum, i;
foreach (c; text) {
if ('0' <= c && c <= '9') {
int value = c - '0';
if (i % 2 == 0) {
sum += value;
} else {
sum += 3 * value;
}
i++;
}
}
return i == 13 && 0 == sum % 10;
}
unittest {
assert(isValidISBN13("978-0596528126"));
assert(!isValidISBN13("978-0596528120"));
assert(isValidISBN13("978-1788399081"));
assert(!isValidISBN13("978-1788399083"));
}
Delphi
function ValidateISBN(ISBN: string): boolean;
{Validate an ISBN number}
var I,N,Sum: integer;
begin
Sum:=0;
{Go througha chars, ignoring non-digits}
for I:=1 to Length(ISBN) do
if ISBN[I] in ['0'..'9'] then
begin
N:=StrToInt(ISBN[I]);
{Every other digit multiplied by 3}
if (I and 1)=1 then N:=N*3;
{Sum digits}
Sum:=Sum+N;
end;
{The sum must be an even multiple of 10}
Result:=(Sum mod 10)=0;
end;
procedure ValidateAndShow(Memo: TMemo; ISBN: string);
{Validate ISBN number and show the result}
var S: string;
begin
S:=ISBN;
if ValidateISBN(ISBN) then S:=S+' (Good)'
else S:=S+' (Bad)';
Memo.Lines.Add(S);
end;
procedure TestISBNSet(Memo: TMemo);
{Test supplied set of ISBN numbers}
begin
ValidateAndShow(Memo,'978-0596528126'); //(good)
ValidateAndShow(Memo,'978-0596528120'); //(bad)
ValidateAndShow(Memo,'978-1788399081'); //(good)
ValidateAndShow(Memo,'978-1788399083'); //(bad)
end;
- Output:
978-0596528126 (Good) 978-0596528120 (Bad) 978-1788399081 (Good) 978-1788399083 (Bad)
Draco
proc nonrec isbn13_check(*char isbn) bool:
byte n, check, d;
char cur;
bool ok;
n := 0;
check := 0;
ok := true;
while
cur := isbn*;
isbn := isbn + 1;
ok and cur ~= '\e'
do
if n=3 then
if cur ~= '-' then ok := false fi
elif cur<'0' or cur>'9' then
ok := false
else
d := cur - '0';
if n=1 or (n>3 and n&1 = 0) then
d := d * 3;
fi;
check := check + d
fi;
n := n + 1
od;
ok and n = 14 and check%10 = 0
corp
proc nonrec test(*char isbn) void:
writeln(isbn, ": ",
if isbn13_check(isbn) then "good" else "bad" fi)
corp
proc nonrec main() void:
test("978-0596528126");
test("978-0596528120");
test("978-1788399081");
test("978-1788399083")
corp
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
EasyLang
func ISBN13check isbn$ .
for c$ in strchars isbn$
if c$ <> "-"
ndigs += 1
.
dig = number c$
if ndigs mod 2 = 0
dig *= 3
.
sum += dig
.
if sum mod 10 <> 0
return 0
.
return 1
.
codes$[] = [ "978-0596528126" "978-0596528120" "978-1788399081" "978-1788399083" ]
for code$ in codes$[]
if ISBN13check code$ = 1
print code$ & " is a valid ISBN"
else
print code$ & " is not a valid ISBN"
.
.
- Output:
978-0596528126 is a valid ISBN 978-0596528120 is not a valid ISBN 978-1788399081 is a valid ISBN 978-1788399083 is not a valid ISBN
Excel
LAMBDA
Binding the name ISBN13Check to the following lambda expression in the Name Manager of the Excel WorkBook:
(See LAMBDA: The ultimate Excel worksheet function)
ISBN13Check
=LAMBDA(s,
LET(
ns, FILTERP(
LAMBDA(v,
NOT(ISERROR(v))
)
)(
VALUE(CHARSROW(s))
),
ixs, SEQUENCE(
1, COLUMNS(ns),
1, 1
),
0 = MOD(
SUM(
IF(0 <> MOD(ixs, 2),
INDEX(ns, ixs),
3 * INDEX(ns, ixs)
)
),
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
)
)
FILTERP
=LAMBDA(p,
LAMBDA(xs,
FILTER(xs, p(xs))
)
)
ISDIGIT
=LAMBDA(c,
LET(
ic, CODE(c),
AND(47 < ic, 58 > ic)
)
)
- Output:
fx | =ISBN13Check(A2) | ||
---|---|---|---|
A | B | ||
1 | Candidate string | ISBN13 checked | |
2 | 978-0596528126 | TRUE | |
3 | 978-0596528120 | FALSE | |
4 | 978-1788399081 | TRUE | |
5 | 978-1788399083 | FALSE |
Factor
USING: combinators.short-circuit formatting kernel math
math.functions math.parser math.vectors qw sequences
sequences.extras sets unicode ;
: (isbn13?) ( str -- ? )
string>digits
[ <evens> sum ] [ <odds> 3 v*n sum + ] bi 10 divisor? ;
: isbn13? ( str -- ? )
"- " without
{ [ length 13 = ] [ [ digit? ] all? ] [ (isbn13?) ] } 1&& ;
qw{ 978-0596528126 978-0596528120 978-1788399081 978-1788399083 }
[ dup isbn13? "good" "bad" ? "%s: %s\n" printf ] each
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
Forth
The following word not only identifies correct 13 digit ISBN (and EAN) codes, but also 8 digit EAN and GTIN codes.
: is-digit [char] 0 - 10 u< ;
: isbn? ( a n -- f)
1- chars over + 2>r 0 1 2r> ?do
dup i c@ dup is-digit \ get length and factor, setup loop
if [char] 0 - * rot + swap 3 * 8 mod else drop drop then
-1 chars +loop drop 10 mod 0= \ now calculate checksum
;
- Output:
In Forth, a "true" value is indicated by "-1".
s" 978-0596528126" isbn? . -1 ok s" 978-0596528120" isbn? . 0 ok s" 978-1788399081" isbn? . -1 ok s" 978-1788399083" isbn? . 0 ok
Fortran
program isbn13
implicit none
character(len=14), dimension(4), parameter :: isbns=["978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"]
integer :: i
do i = 1, ubound(isbns, 1)
if (check_isbn13(isbns(i))) then
print*, isbns(i), " : ", "good"
else
print*, isbns(i), " : ", "bad"
end if
end do
contains
pure function check_isbn13(isbn)
character(len=*), intent(in) :: isbn
logical :: check_isbn13
integer :: summ, counter, i, digit
check_isbn13 = .false.
counter = 0
summ = 0
do i = 1, len(isbn)
if (isbn(i:i) == ' ' .or. isbn(i:i) == '-') cycle
counter = counter + 1
read(isbn(i:i), '(I1)') digit
if (modulo(counter, 2) == 0) then
summ = summ + 3*digit
else
summ = summ + digit
end if
end do
if (counter == 13 .and. modulo(summ, 10) == 0) check_isbn13 = .true.
end function check_isbn13
end program isbn13
- Output:
978-0596528126 : good 978-0596528120 : bad 978-1788399081 : good 978-1788399083 : bad
FreeBASIC
#define ZEROC asc("0")
function is_num( byval c as string ) as boolean
if asc(c) >= ZEROC andalso asc(c)<ZEROC+10 then return true
return false
end function
function is_good_isbn( isbn as string ) as boolean
dim as uinteger charno = 0, digitno = 0, sum = 0
dim as string*1 currchar
while charno <= len(isbn)
currchar = mid(isbn,charno,1)
if is_num(currchar) then
if digitno mod 2 = 1 then
sum += 2*(asc(currchar)-ZEROC)
end if
sum += asc(currchar)-ZEROC
digitno += 1
end if
charno += 1
wend
if sum mod 10 = 0 then
return true
else
return false
end if
end function
dim as string isbns(0 to 3) = { "978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083" }
dim as uinteger i
for i = 0 to 3
if is_good_isbn( isbns(i) ) then
print isbns(i)+": good"
else
print isbns(i)+": bad"
end if
next i
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
F#
// ISBN13 Check Digit
open System
let parseInput (input: string) =
Seq.map(fun c -> c |> string) input |> Seq.toList |> List.map(fun x -> Int32.Parse x)
[<EntryPoint>]
let main argv =
let isbnnum = parseInput (String.filter (fun x -> x <> '-') argv.[0])
// Multiply every other digit by 3
let everyOther = List.mapi (fun i x -> if i % 2 = 0 then x * 3 else x) isbnnum
// Sum the *3 list with the original list
let sum = List.sum everyOther + List.sum isbnnum
// If the remainder of sum / 10 is 0 then it's a valid ISBN13
if sum % 10 = 0 then
printfn "%s Valid ISBN13" argv.[0]
else
printfn "%s Invalid ISBN13" argv.[0]
0
- Output:
978-0596528126 Valid ISBN13 978-0596528120 Inalid ISBN13 978-1788399081 Valid ISBN13 978-1788399083 Inalid ISBN13
Fōrmulæ
Fōrmulæ programs are not textual, visualization/edition of programs is done showing/manipulating structures but not text. Moreover, there can be multiple visual representations of the same program. Even though it is possible to have textual representation —i.e. XML, JSON— they are intended for storage and transfer purposes more than visualization and edition.
Programs in Fōrmulæ are created/edited online in its website.
In this page you can see and run the program(s) related to this task and their results. You can also change either the programs or the parameters they are called with, for experimentation, but remember that these programs were created with the main purpose of showing a clear solution of the task, and they generally lack any kind of validation.
Solution
Test cases
Gambas
Public Sub Main()
Dim isbn As String[] = ["978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083", "978-2-74839-908-0", "978-2-74839-908-5", "978 1 86197 876 9"]
For n As Integer = 1 To isbn.Count
Dim sum As Integer = 0, num As Integer
Dim isbnStr As String = isbn[n]
isbnStr = Replace(isbnStr, "-", "")
isbnStr = Replace(isbnStr, " ", "")
For m As Integer = 1 To Len(isbnStr)
If m Mod 2 = 0 Then
num = 3 * CInt(Mid(isbnStr, m, 1))
Else
num = CInt(Mid(isbnStr, m, 1))
End If
sum += num
Next
If sum Mod 10 = 0 Then
Print isbn[n]; ": good"
Else
Print isbn[n]; ": bad"
End If
Next
End
Go
package main
import (
"fmt"
"strings"
"unicode/utf8"
)
func checkIsbn13(isbn string) bool {
// remove any hyphens or spaces
isbn = strings.ReplaceAll(strings.ReplaceAll(isbn, "-", ""), " ", "")
// check length == 13
le := utf8.RuneCountInString(isbn)
if le != 13 {
return false
}
// check only contains digits and calculate weighted sum
sum := int32(0)
for i, c := range isbn {
if c < '0' || c > '9' {
return false
}
if i%2 == 0 {
sum += c - '0'
} else {
sum += 3 * (c - '0')
}
}
return sum%10 == 0
}
func main() {
isbns := []string{"978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"}
for _, isbn := range isbns {
res := "bad"
if checkIsbn13(isbn) {
res = "good"
}
fmt.Printf("%s: %s\n", isbn, res)
}
}
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
Haskell
import Data.Char (digitToInt, isDigit)
import Text.Printf (printf)
pair :: Num a => [a] -> [(a, a)]
pair [] = []
pair xs = p (take 2 xs) : pair (drop 2 xs)
where
p ps = case ps of
(x : y : zs) -> (x, y)
(x : zs) -> (x, 0)
validIsbn13 :: String -> Bool
validIsbn13 isbn
| length (digits isbn) /= 13 = False
| otherwise = calc isbn `rem` 10 == 0
where
digits = map digitToInt . filter isDigit
calc = sum . map (\(x, y) -> x + y * 3) . pair . digits
main :: IO ()
main =
mapM_
(printf "%s: Valid: %s\n" <*> (show . validIsbn13))
[ "978-0596528126",
"978-0596528120",
"978-1788399081",
"978-1788399083"
]
- Output:
978-0596528126: Valid: True 978-0596528120: Valid: False 978-1788399081: Valid: True 978-1788399083: Valid: False
Or, expressed in terms of cycle:
import Data.Char (digitToInt, isDigit)
isISBN13 :: String -> Bool
isISBN13 =
(0 ==)
. flip rem 10
. sum
. flip
(zipWith ((*) . digitToInt) . filter isDigit)
(cycle [1, 3])
main :: IO ()
main =
mapM_
(print . ((,) <*> isISBN13))
[ "978-0596528126",
"978-0596528120",
"978-1788399081",
"978-1788399083"
]
- Output:
("978-0596528126",True) ("978-0596528120",False) ("978-1788399081",True) ("978-1788399083",False)
IS-BASIC
100 PROGRAM "ISBN13.bas"
110 DO
120 PRINT :INPUT PROMPT "ISBN-13 code: ":IS$
130 IF IS$="" THEN EXIT DO
140 IF ISBN(IS$) THEN
150 PRINT "Ok."
160 ELSE
170 PRINT "CRC error."
180 END IF
190 LOOP
200 DEF TRIM$(S$)
210 LET T$=""
220 FOR I=1 TO LEN(S$)
230 IF S$(I)>CHR$(47) AND S$(I)<CHR$(58) THEN LET T$=T$&S$(I)
240 NEXT
250 LET TRIM$=T$
260 END DEF
270 DEF ISBN(S$)
280 LET SUM,ISBN=0:LET ISBN$=TRIM$(IS$)
290 IF LEN(ISBN$)<>13 THEN PRINT "Invalid length.":EXIT DEF
300 FOR I=1 TO 11 STEP 2
310 LET SUM=SUM+VAL(ISBN$(I))+VAL(ISBN$(I+1))*3
320 NEXT
330 LET SUM=SUM+VAL(ISBN$(13))
340 IF MOD(SUM,10)=0 THEN LET ISBN=-1
350 END DEF
J
D =: '0123456789'
isbn13c =: D&([ check@:i. clean)
check =: 0 = 10 | lc
lc =: [ +/@:* weight
weight =: 1 3 $~ #
clean =: ] -. a. -. [
- Output:
isbn13c;._1 ' 978-0596528126 978-0596528120 978-1788399081 978-1788399083'
1 0 1 0
Java
public static void main(String[] args) {
String[] isbn13s = {
"978-0596528126",
"978-0596528120",
"978-1788399081",
"978-1788399083"
};
for (String isbn13 : isbn13s)
System.out.printf("%s %b%n", isbn13, validateISBN13(isbn13));
}
static boolean validateISBN13(String string) {
int[] digits = digits(string.strip().replace("-", ""));
return digits[12] == checksum(digits);
}
static int[] digits(String string) {
int[] digits = new int[13];
int index = 0;
for (char character : string.toCharArray()) {
if (character < '0' || character > '9')
throw new IllegalArgumentException("Invalid ISBN-13");
/* convert ascii to integer */
digits[index++] = Character.digit(character, 10);
}
return digits;
}
static int checksum(int[] digits) {
int total = 0;
int index = 0;
for (int digit : digits) {
if (index == 12) break;
if (index++ % 2 == 1) digit *= 3;
total += digit;
}
return 10 - (total % 10);
}
978-0596528126 true 978-0596528120 false 978-1788399081 true 978-1788399083 false
An alternate demonstration
public static void main(){
System.out.println(isISBN13("978-0596528126"));
System.out.println(isISBN13("978-0596528120"));
System.out.println(isISBN13("978-1788399081"));
System.out.println(isISBN13("978-1788399083"));
}
public static boolean isISBN13(String in){
int pre = Integer.parseInt(in.substring(0,3));
if (pre!=978)return false;
String postStr = in.substring(4);
if (postStr.length()!=10)return false;
int post = Integer.parseInt(postStr);
int sum = 38;
for(int x = 0; x<10;x+=2)
sum += (postStr.charAt(x)-48)*3 + ((postStr.charAt(x+1)-48));
if(sum%10==0) return true;
return false;
}
- Output:
true false true false
jq
Works with gojq, the Go implementation of jq
def isbn_check:
def digits: tostring | explode | map( select(. >= 48 and . <= 57) | [.] | implode | tonumber);
def sum(s): reduce s as $x (null; . + $x);
digits
| . as $digits
| sum(range(0;length;2) | $digits[.]) as $one
| (3 * sum(range(1;length;2) | $digits[.])) as $two
| (($one+$two) % 10) == 0;
def testingcodes:
["978-0596528126", "978-0596528120",
"978-1788399081", "978-1788399083"];
testingcodes[]
| "\(.): \(if isbn_check then "good" else "bad" end)"
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
Julia
function isbncheck(str)
return sum(iseven(i) ? 3 * parse(Int, ch) : parse(Int, ch)
for (i, ch) in enumerate(replace(str, r"\D" => ""))) % 10 == 0
end
const testingcodes = ["978-0596528126", "978-0596528120",
"978-1788399081", "978-1788399083"]
for code in testingcodes
println(code, ": ", isbncheck(code) ? "good" : "bad")
end
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
K
digits: {x[&(x>"/")&x<":"]-"0"}
isbn13c: 0=10!+/{x*(#x)#1 3} digits@
isbn13c "978-0596528126"
1
isbn13c "978-0596528120"
0
isbn13c " 978-1788399081"
1
isbn13c "978-1788399083"
0
Kotlin
fun isValidISBN13(text: String): Boolean {
val isbn = text.replace(Regex("[- ]"), "")
return isbn.length == 13 && isbn.map { it - '0' }
.mapIndexed { index, value -> when (index % 2) { 0 -> value else -> 3 * value } }
.sum() % 10 == 0
}
Tested using Spek
describe("ISBN Utilities") {
mapOf(
"978-0596528126" to true,
"978-0596528120" to false,
"978-1788399081" to true,
"978-1788399083" to false
).forEach { (input, expected) ->
it("returns $expected for $input") {
println("$input: ${when(isValidISBN13(input)) {
true -> "good"
else -> "bad"
}}")
assert(isValidISBN13(input) == expected)
}
}
}
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
langur
In this example, we map to multiple functions (actually 1 no-op).
val isbn13checkdigit = fn(var s) {
s = replace(s, RE/[\- ]/)
s -> re/^[0-9]{13}$/ and
fold(fn{+}, map([_, fn{*3}], s2n(s))) div 10
}
val tests = {
"978-0596528126": true,
"978-0596528120": false,
"978-1788399081": true,
"978-1788399083": false,
}
for key of tests {
val pass = isbn13checkdigit(key)
write key, ": ", if(pass: "good"; "bad")
writeln if(pass == tests[key]: ""; " (ISBN-13 CHECK DIGIT TEST FAILED)")
}
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
Lua
function checkIsbn13(isbn)
local count = 0
local sum = 0
for c in isbn:gmatch"." do
if c == ' ' or c == '-' then
-- skip
elseif c < '0' or '9' < c then
return false
else
local digit = c - '0'
if (count % 2) > 0 then
sum = sum + 3 * digit
else
sum = sum + digit
end
count = count + 1
end
end
if count ~= 13 then
return false
end
return (sum % 10) == 0
end
function test(isbn)
if checkIsbn13(isbn) then
print(isbn .. ": good")
else
print(isbn .. ": bad")
end
end
function main()
test("978-0596528126")
test("978-0596528120")
test("978-1788399081")
test("978-1788399083")
end
main()
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
Mathematica / Wolfram Language
ClearAll[ValidISBNQ]
ValidISBNQ[iban_String] := Module[{i},
i = StringReplace[iban, {" " -> "", "-" -> ""}];
If[StringMatchQ[i, Repeated[DigitCharacter]],
i = ToExpression /@ Characters[i];
i[[2 ;; ;; 2]] *= 3;
Mod[Total[i], 10] == 0
,
False
]
]
ValidISBNQ["978-0596528126"]
ValidISBNQ["978-0596528120"]
ValidISBNQ["978-1788399081"]
ValidISBNQ["978-1788399083"]
- Output:
True False True False
MiniScript
This GUI implementation is for use with Mini Micro.
isISBN13 = function(n)
n = n.replace("-","").replace(" ","")
s = 0
for i in range(0, n.len-1,2)
s += n[i].val
end for
for i in range(1, n.len-1,2)
s += n[i].val * 3
end for
return not (s % 10)
end function
testValues = "978-0596528126 978-0596528120 978-1788399081 978-1788399083".split(" ")
for val in testValues
print val + " " + ["bad", "good"][isISBN13(val)]
end for
- Output:
978-0596528126 good 978-0596528120 bad 978-1788399081 good 978-1788399083 bad
Miranda
main :: [sys_message]
main = [Stdout (lay (map test tests))]
test :: [char]->[char]
test isbn = isbn ++ ": good", if isbn13 isbn
= isbn ++ ": bad", otherwise
tests :: [[char]]
tests = ["978-0596528126",
"978-0596528120",
"978-1788399081",
"978-1788399083"]
isbn13 :: [char]->bool
isbn13 str = False, if #isbn ~= 13 \/ ~and (map digit isbn)
= check mod 10 = 0, otherwise
where isbn = filter (~= '-') str
digits = map (numval.(:[])) isbn
check = sum (zipwith (*) digits (concat (repeat [1,3])))
uncurry :: (*->**->***)->(*,**)->***
uncurry f (a,b) = f a b
zipwith :: (*->**->***)->[*]->[**]->[***]
zipwith f a b = map (uncurry f) (zip2 a b)
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
Modula-2
MODULE ISBN;
FROM InOut IMPORT WriteString, WriteLn;
FROM Strings IMPORT Length;
PROCEDURE validISBN(s: ARRAY OF CHAR): BOOLEAN;
VAR total, i, length: CARDINAL;
BEGIN
total := 0;
length := Length(s);
IF (length # 14) OR (s[3] # '-') THEN
RETURN FALSE;
END;
FOR i := 0 TO length-1 DO
IF i # 3 THEN
IF (s[i] < '0') OR (s[i] > '9') THEN
RETURN FALSE;
ELSIF (i < 3) AND (i MOD 2 = 1) OR (i > 3) AND (i MOD 2 = 0) THEN
total := total + 3 * (ORD(s[i]) - ORD('0'));
ELSE
total := total + ORD(s[i]) - ORD('0');
END;
END;
END;
RETURN total MOD 10 = 0;
END validISBN;
PROCEDURE check(s: ARRAY OF CHAR);
BEGIN
WriteString(s);
WriteString(': ');
IF validISBN(s) THEN
WriteString('good');
ELSE
WriteString('bad');
END;
WriteLn;
END check;
BEGIN
check('978-0596528126');
check('978-0596528120');
check('978-1788399081');
check('978-1788399083');
END ISBN.
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
Nanoquery
def checkIsbn13(isbn)
// remove any hyphens or spaces
isbn = str(isbn).replace("-","").replace(" ","")
// check length = 13
if len(isbn) != 13
return false
end
// check only contains digits and calculate weighted sum
sum = 0
for i in range(0, len(isbn) - 1)
c = isbn[i]
if (ord(c) < ord("0")) or (ord(c) > ord("9"))
return false
end
if (i % 2) = 0
sum += ord(c) - ord("0")
else
sum += 3 * (ord(c) - ord("0"))
end
end
return (sum % 10) = 0
end
isbns = {"978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"}
for isbn in isbns
res = "bad"
if checkIsbn13(isbn)
res = "good"
end
print format("%s: %s\n", isbn, res)
end
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
Nim
import strutils, strformat
func is_isbn*(s: string): bool =
var sum, len: int
for c in s:
if is_digit(c):
sum += (ord(c) - ord('0')) * (if len mod 2 == 0: 1 else: 3)
len += 1
elif c != ' ' and c != '-':
return false
return (len == 13) and (sum mod 10 == 0)
when is_main_module:
let isbns = [ "978-0596528126", "978-0596528120",
"978-1788399081", "978-1788399083" ]
for isbn in isbns:
var quality: string = if is_isbn(isbn): "good" else: "bad"
echo &"{isbn}: {quality}"
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
OmniMark
define switch function isbn (value stream vs-s) as
local integer li-total initial {0}
local integer li-check
repeat scan vs-s
; Any digits in an ISBN-13 may be separated by a hyphen or space
match (digit => lpv-1 ('-' | space)? digit => lpv-3 ('-' | space)? ) => p
; Add the first digit to the total and add the second multiplied by 3
increment li-total by (lpv-1 + (lpv-3 * 3))
match any => lpv-c
; This is always the thirteenth digit, the 'check' digit
set li-check to lpv-c
again
do when 10 - li-total modulo 10 = li-check
return true
else when li-total modulo 10 = 0 and li-check = 0
return true
else
return false
done
process
; These are the first four test ISBN-13s from the problem description, with
; the first and third being valid and the second and fourth invalid.
submit "978-0596528126"
submit "978-0596528120"
submit "978-1788399081"
submit "978-1788399083"
; This ISBN-13 is from https://isbn-information.com/the-13-digit-isbn.html
submit "978-1-86197-876-9"
submit "978 1 86197 876 9"
; These ISBNs are from https://en.wikipedia.org/wiki/ISBN
submit "978-3-16-148410-0"
submit "978 3 16 148410 9" ; But this one has had the 'check' digit changed
submit "This is valid: 978-0-306-40615-7^" ; Adding some extra complexity :)
submit "978-03-0640-615 7." ; Formatted as "EAN-Group-Publisher-Title Check"
; These strings show how non-ISBN-13 numbers must be (and are) ignored.
submit "This is a 14-digit number - 555-30640610555 - so it is ignored.%n"
submit "This is an ISBN-10, so it is ignored too: 0-306-40615-2.%n"
; This find rule will only find a string that is an ISBN-13 (digit/-/space)
; combination. It has been split into three lines to make it easier to see
; what is being done.
; 1. The string must either be preceded by any character other than a digit
; or start the stream, and
; 2. It must then have 12 digits (with optional hyphens or spaces
; intermingled) and must conclude with a digit, and
; 3. It either must not be followed by a another digit (making it more
; than 13 digits, so not an ISBN-13) or the stream ends.
find ([any \ digit] | value-start) => lpv-s
((digit ('-' | space)?){12} digit) => lpv-n
([any \ digit] | value-end) => lpv-e
output lpv-s || lpv-n || lpv-e
do when isbn(lpv-n) = true
output ' (good)%n'
else
output ' (bad)%n'
done
; output all other characters verbatim
find any => lpv-char
output lpv-char
- Output:
978-0596528126 (good) 978-0596528120 (bad) 978-1788399081 (good) 978-1788399083 (bad) 978-1-86197-876-9 (good) 978 1 86197 876 9 (good) 978-3-16-148410-0 (good) 978 3 16 148410 9 (bad) This is valid: 978-0-306-40615-7^ (good) 978-03-0640-615 7. (good) This is a 14-digit number - 555-30640610555 - so it is ignored. This is an ISBN-10, so it is ignored too: 0-306-40615-2.
Pascal
program ISBNChecksum(output);
const
codeIndexMaximum = 17;
ISBNIndexMinimum = 1;
ISBNIndexMaximum = 13;
ISBNIndexRange = ISBNIndexMaximum - ISBNIndexMinimum + 1;
type
code = string(codeIndexMaximum);
codeIndex = 1..codeIndexMaximum value 1;
decimalDigit = '0'..'9';
decimalValue = 0..9;
ISBNIndex = ISBNIndexMinimum..ISBNIndexMaximum;
ISBN = array[ISBNIndex] of decimalDigit;
{ returns the integer value represented by a character }
function numericValue(protected c: decimalDigit): decimalValue;
begin
{ in Pascal result variable bears the same name as the function }
numericValue := ord(c) - ord('0')
end;
{ determines whether an ISBN is technically valid (checksum correct) }
function isValidISBN(protected n: ISBN): Boolean;
var
sum: 0..225 value 0;
i: ISBNIndex;
begin
{ NB: in Pascal for-loop-limits are _inclusive_ }
for i := ISBNIndexMinimum to ISBNIndexMaximum do
begin
{ alternating scale factor 3^0, 3^1 based on Boolean }
sum := sum + numericValue(n[i]) * 3 pow ord(not odd(i))
end;
isValidISBN := sum mod 10 = 0
end;
{ transform '978-0-387-97649-5' into '9780387976495' }
function digits(n: code): code;
var
sourceIndex, destinationIndex: codeIndex;
begin
for sourceIndex := 1 to length(n) do
begin
if n[sourceIndex] in ['0'..'9'] then
begin
n[destinationIndex] := n[sourceIndex];
destinationIndex := destinationIndex + 1
end
end;
{ to alter a string’s length you need overwrite it completely }
digits := subStr(n, 1, destinationIndex - 1)
end;
{ data type coercion }
function asISBN(protected n: code): ISBN;
var
result: ISBN;
begin
unpack(n[1..length(n)], result, 1);
asISBN := result
end;
{ tells whether a string value contains a proper ISBN representation }
function isValidISBNString(protected hyphenatedForm: code): Boolean;
var
digitOnlyForm: code;
begin
digitOnlyForm := digits(hyphenatedForm);
{ The Extended Pascal `and_then` Boolean operator allows us }
{ to first check the length before invoking `isValidISBN`. }
isValidISBNString := (length(digitOnlyForm) = ISBNIndexRange)
and_then isValidISBN(asISBN(digitOnlyForm))
end;
{ === MAIN ============================================================= }
begin
writeLn(isValidISBNString('978-0596528126'));
writeLn(isValidISBNString('978-0596528120'));
writeLn(isValidISBNString('978-1788399081'));
writeLn(isValidISBNString('978-1788399083'))
end.
- Output:
True False True False
Perl
use strict;
use warnings;
use feature 'say';
sub check_digit {
my($isbn) = @_; my($sum);
$sum += (1,3)[$_%2] * (split '', join '', split /\D/, $isbn)[$_] for 0..11;
(10 - $sum % 10) % 10;
}
for (<978-0596528126 978-0596528120 978-1788399081 978-1788399083 978-2-74839-908-0 978-2-74839-908-5>) {
my($isbn,$check) = /(.*)(.)/;
my $check_d = check_digit($isbn);
say "$_ : " . ($check == $check_d ? 'Good' : "Bad check-digit $check; should be $check_d")
}
- Output:
978-0596528126 : Good 978-0596528120 : Bad check-digit 9; should be 2 978-1788399081 : Good 978-1788399083 : Bad check-digit 3; should be 1 978-2-74839-908-0 : Good 978-2-74839-908-5 : Bad check-digit 5; should be 0
Phix
with javascript_semantics procedure check_isbn13(string isbn) integer digits = 0, checksum = 0, w = 1 for i=1 to length(isbn) do integer ch = isbn[i] if ch!=' ' and ch!='-' then ch -= '0' if ch<0 or ch>9 then checksum = 9 exit end if checksum += ch*w digits += 1 w = 4-w end if end for checksum = remainder(checksum,10) string gb = iff(digits=13 and checksum=0 ? "good" : "bad") printf(1,"%s: %s\n",{isbn,gb}) end procedure constant isbns = {"978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083", "978-2-74839-908-0","978-2-74839-908-5","978 1 86197 876 9"} for i=1 to length(isbns) do check_isbn13(isbns[i]) end for
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad 978-2-74839-908-0: good 978-2-74839-908-5: bad 978 1 86197 876 9: good
PicoLisp
(de isbn13? (S)
(let L
(make
(for N (chop S)
(and (format N) (link @)) ) )
(and
(= 13 (length L))
(=0 (% (sum * L (circ 1 3)) 10)) ) ) )
(mapc
'((A)
(tab
(-19 1)
A
(if (isbn13? A) 'ok 'fail) ) )
(quote
"978-0596528126"
"978-0596528120"
"978-1-86197-876-9"
"978-2-74839-908-5"
"978 1 86197 876 9" ) )
- Output:
978-0596528126 ok 978-0596528120 fail 978-1-86197-876-9 ok 978-2-74839-908-5 fail 978 1 86197 876 9 ok
PL/M
100H:
CHECK$ISBN13: PROCEDURE (PTR) BYTE;
DECLARE PTR ADDRESS, ISBN BASED PTR BYTE;
DECLARE (I, F, T) BYTE;
F = 1;
T = 0;
DO I = 0 TO 13;
IF I = 3 THEN DO;
/* THIRD CHAR SHOULD BE '-' */
IF ISBN(I) <> '-' THEN RETURN 0;
END;
ELSE DO;
/* DIGITS MUST BE VALID */
IF ISBN(I) < '0' OR ISBN(I) > '9' THEN RETURN 0;
T = T + (ISBN(I) - '0') * F;
F = 4 - F; /* MULTIPLY BY 1 AND 3 ALTERNATELY */
END;
END;
RETURN (T MOD 10) = 0;
END CHECK$ISBN13;
/* CP/M BDOS CALL */
BDOS: PROCEDURE (FUNC, ARG);
DECLARE FUNC BYTE, ARG ADDRESS;
GO TO 5;
END BDOS;
PRINT: PROCEDURE (STR);
DECLARE STR ADDRESS;
CALL BDOS(9, STR);
END PRINT;
/* TESTS */
DECLARE TEST (4) ADDRESS;
TEST(0) = .'978-0596528126$';
TEST(1) = .'978-0596528120$';
TEST(2) = .'978-1788399081$';
TEST(3) = .'978-1788399083$';
DECLARE I BYTE;
DO I = 0 TO LAST(TEST);
CALL PRINT(TEST(I));
CALL PRINT(.': $');
IF CHECK$ISBN13(TEST(I)) THEN
CALL PRINT(.'GOOD$');
ELSE
CALL PRINT(.'BAD$');
CALL PRINT(.(13,10,'$'));
END;
CALL BDOS(0,0);
EOF
- Output:
978-0596528126: GOOD 978-0596528120: BAD 978-1788399081: GOOD 978-1788399083: BAD
PowerShell
function Get-ISBN13 {
$codes = (
"978-0596528126",
"978-0596528120",
"978-1788399081",
"978-1788399083"
)
foreach ($line in $codes) {
$sum = $null
$codeNoDash = $line.Replace("-","")
for ($i = 0; $i -lt $codeNoDash.length; $i++) {
if (($i % 2) -eq 1) {
$sum += [decimal]$codeNoDash[$i] * 3
}else {
$sum += [decimal]$codeNoDash[$i]
}
}
if (($sum % 10) -eq 0) {
Write-Host "$line Good"
}else {
Write-Host "$line Bad"
}
}
}
Get-ISBN13
- Output:
978-0596528126 Good 978-0596528120 Bad 978-1788399081 Good 978-1788399083 Bad
PureBasic
Macro TestISBN13(X)
Print(X)
If IsISBN13(X) : PrintN(" good") : Else : PrintN(" bad") : EndIf
EndMacro
Procedure.b IsISBN13(ISBN13.s)
ISBN13=Left(ISBN13,3)+Mid(ISBN13,5)
If IsNAN(Val(ISBN13)) Or Len(ISBN13)<>13 : ProcedureReturn #False : EndIf
Dim ISBN.s{1}(12) : PokeS(@ISBN(),ISBN13)
For i=0 To 11 Step 2 : sum+Val(ISBN(i))+Val(ISBN(i+1))*3 : Next
sum+Val(ISBN(12))
ProcedureReturn Bool(sum%10=0)
EndProcedure
If OpenConsole()
TestISBN13("978-0596528126")
TestISBN13("978-0596528120")
TestISBN13("978-1788399081")
TestISBN13("978-1788399083")
Input()
EndIf
- Output:
978-0596528126 good 978-0596528120 bad 978-1788399081 good 978-1788399083 bad
Python
def is_isbn13(n):
n = n.replace('-','').replace(' ', '')
if len(n) != 13:
return False
product = (sum(int(ch) for ch in n[::2])
+ sum(int(ch) * 3 for ch in n[1::2]))
return product % 10 == 0
if __name__ == '__main__':
tests = '''
978-0596528126
978-0596528120
978-1788399081
978-1788399083'''.strip().split()
for t in tests:
print(f"ISBN13 {t} validates {is_isbn13(t)}")
- Output:
ISBN13 978-0596528126 validates True ISBN13 978-0596528120 validates False ISBN13 978-1788399081 validates True ISBN13 978-1788399083 validates False
Or, expressed in terms of itertools.cycle
'''ISBN13 check digit'''
from itertools import cycle
# isISBN13 :: String -> Bool
def isISBN13(s):
'''True if s is a valid ISBN13 string
'''
digits = [int(c) for c in s if c.isdigit()]
return 13 == len(digits) and (
0 == sum(map(
lambda f, x: f(x),
cycle([
lambda x: x,
lambda x: 3 * x
]),
digits
)) % 10
)
# ------------------------- TEST -------------------------
# main :: IO ()
def main():
'''Test strings for ISBN-13 validity.'''
print('\n'.join(
repr((s, isISBN13(s))) for s
in ["978-0596528126",
"978-0596528120",
"978-1788399081",
"978-1788399083"
]
))
# MAIN ---
if __name__ == '__main__':
main()
- Output:
('978-0596528126', True) ('978-0596528120', False) ('978-1788399081', True) ('978-1788399083', False)
Quackery
[ char 0 char 9 1+ within ] is digit? ( c --> b )
[ 1 & ] is odd? ( n --> b )
[ [] swap ]'[ swap
witheach [
dup nested
unrot over do
iff [ dip join ]
else nip
] drop ] is filter ( [ --> [ )
[ 0 swap
witheach [
char->n i^ odd?
iff [ 3 * + ]
else +
] ] is checksum ( $ --> n )
[ filter digit?
dup size 13 = not
iff [ drop false ] done
checksum 10 mod 0 = ] is isbn ( $ --> b )
[ dup echo$ say ": " isbn
iff [ say "Good" ]
else [ say "Bad" ] cr ] is isbn-test ( $ --> )
$ '978-0596528126' isbn-test
$ '978-0596528120' isbn-test
$ '978-1788399081' isbn-test
$ '978-1788399083' isbn-test
- Output:
978-0596528126: Good 978-0596528120: Bad 978-1788399081: Good 978-1788399083: Bad
Racket
#lang racket/base
(define (isbn13-check-digit-valid? s)
(zero? (modulo (for/sum ((i (in-naturals))
(d (regexp-replace* #px"[^[:digit:]]" s "")))
(* (- (char->integer d) (char->integer #\0))
(if (even? i) 1 3)))
10)))
(module+ test
(require rackunit)
(check-true (isbn13-check-digit-valid? "978-0596528126"))
(check-false (isbn13-check-digit-valid? "978-0596528120"))
(check-true (isbn13-check-digit-valid? "978-1788399081"))
(check-false (isbn13-check-digit-valid? "978-1788399083")))
- Output:
no output indicates tests pass
Raku
(formerly Perl 6)
Also test a value that has a zero check digit.
sub check-digit ($isbn) {
(10 - (sum (|$isbn.comb(/<[0..9]>/)) »*» (1,3)) % 10).substr: *-1
}
{
my $check = .substr(*-1);
my $check-digit = check-digit .chop;
say "$_ : ", $check == $check-digit ??
'Good' !!
"Bad check-digit $check; should be $check-digit"
} for words <
978-0596528126
978-0596528120
978-1788399081
978-1788399083
978-2-74839-908-0
978-2-74839-908-5
>;
- Output:
978-0596528126 : Good 978-0596528120 : Bad check-digit 9; should be 2 978-1788399081 : Good 978-1788399083 : Bad check-digit 3; should be 1 978-2-74839-908-0 : Good 978-2-74839-908-5 : Bad check-digit 5; should be 0
Red
check_valid_isbn13: function [str] [
is_digit: charset [#"0" - #"9"]
remove-each i str [not pick is_digit i] ; remove non-digits
either 13 = length? str [ ; reject strings of incorrect length
sum: 0
repeat i 13 [
mul: either even? i [3] [1] ; multiplier for odd/even digits
sum: sum + (mul * to integer! to string! pick str i)
]
zero? (sum % 10) ; check if remainder mod 10 is zero
] [
false
]
]
; check given examples
foreach [str] ["978-0596528126" "978-0596528120" "978-1788399081" "978-1788399083"] [
prin str
prin " - "
print check_valid_isbn13 str
]
- Output:
978-0596528126 - true 978-0596528120 - false 978-1788399081 - true 978-1788399083 - false
Refal
$ENTRY Go {
= <Test '978-0596528126'>
<Test '978-0596528120'>
<Test '978-1788399081'>
<Test '978-1788399083'>
};
Test {
e.X = <Prout e.X ': ' <ISBN e.X>>;
};
ISBN {
e.X, <Remove '-' e.X>: e.DS,
<CheckDigits e.DS>: {
False = Bad;
True = <ISBN1 e.DS>;
};
};
ISBN1 {
(13 s.Sum), <Mod s.Sum 10>: 0 = Good;
(13 s.Sum) e.X = Bad;
(s.N s.Sum) = Bad;
(s.N s.Sum) s.D e.X,
<+ s.N 1>: s.N1,
<Numb s.D>: s.V,
<Mod s.N 2>: {
0 = <ISBN1 (s.N1 <+ s.Sum s.V>) e.X>;
1 = <ISBN1 (s.N1 <+ s.Sum <* 3 s.V>>) e.X>;
};
e.X = <ISBN1 (0 0) e.X>;
};
Remove {
s.1 = ;
s.1 s.1 e.X = <Remove s.1 e.X>;
s.1 s.2 e.X = s.2 <Remove s.1 e.X>;
};
CheckDigits {
= True;
s.D e.X, '0123456789': e.A s.D e.B = <CheckDigits e.X>;
e.X = False;
};
- Output:
978-0596528126: Good 978-0596528120: Bad 978-1788399081: Good 978-1788399083: Bad
REXX
A couple of additional checks were made to verify a correct length, and also that the ISBN-13 code is all numerics (with optional minus signs).
/*REXX pgm validates the check digit of an ISBN─13 code (it may have embedded minuses).*/
parse arg $ /*obtain optional arguments from the CL*/
if $='' | if $="," then $= '978-0596528126 978-0596528120 978-1788399081 978-1788399083'
@ISBN= "ISBN─13 code isn't" /*a literal used when displaying msgs. */
/* [↓] remove all minuses from X code.*/
do j=1 for words($); y= word($,j) /*obtain an ISBN─13 code from $ list.*/
x= space( translate(y, , '-'), 0) /*remove all minus signs from the code.*/
L= length(x) /*obtain the length of the ISBN-13 code*/
if L \== 13 then do; say @ISBN '13 characters: ' x; exit 13; end
if verify(x, 9876543210)\==0 then do; say @ISBN 'numeric: ' x; exit 10; end
sum= 0
do k=1 for L; #= substr(x, k, 1) /*get a decimal digit from the X code. */
if \(k//2) then #= # * 3 /*multiply every other digit by three. */
sum= sum + # /*add the digit (or product) to the SUM*/
end /*k*/
if right(sum, 1)==0 then say ' ISBN-13 code ' x " is valid."
else say ' ISBN-13 code ' x " isn't valid."
end /*j*/ /*stick a fork in it, we're all done. */
- output when using the four default inputs:
ISBN-13 code 9781734314502 is valid. ISBN-13 code 9781734314509 isn't valid. ISBN-13 code 9781788399081 is valid. ISBN-13 code 9781788399083 isn't valid.
Ring
load "stdlib.ring"
isbn = ["978-0596528126","978-0596528120", "978-1788399081", "978-1788399083","978-2-74839-908-0","978-2-74839-908-5","978 1 86197 876 9"]
for n = 1 to len(isbn)
sum = 0
isbnStr = isbn[n]
isbnStr = substr(isbnStr,"-","")
isbnStr = substr(isbnStr," ","")
for m = 1 to len(isbnStr)
if m%2 = 0
num = 3*number(substr(isbnStr,m,1))
sum = sum + num
else
num = number(substr(isbnStr,m,1))
sum = sum + num
ok
next
if sum%10 = 0
see "" + isbn[n] + ": true" + nl
else
see "" + isbn[n] + ": bad" + nl
ok
next
Output:
978-0596528126: true 978-0596528120: bad 978-1788399081: true 978-1788399083: bad 978-2-74839-908-0: true 978-2-74839-908-5: bad 978 1 86197 876 9: true
RPL
≪ → isbn ≪ 0 1 CF 1 isbn SIZE FOR j IF "0123456789" isbn j DUP SUB POS THEN LAST 1 - IF 1 FS?C THEN 3 * ELSE 1 SF END + END NEXT ≫ 10 MOD NOT ≫ 'ISBN?' STO
≪ { "978-0596528126" "978-0596528120" "978-1788399081" "978-1788399083" } → tests ≪ 1 tests SIZE FOR j tests GET ISBN? NEXT ≫ ≫ EVAL
- Output:
4: 1 3: 0 2: 1 1: 0
Ruby
def validISBN13?(str)
cleaned = str.delete("^0-9").chars
return false unless cleaned.size == 13
cleaned.each_slice(2).sum{|d1, d2| d1.to_i + 3*d2.to_i }.remainder(10) == 0
end
isbns = ["978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"]
isbns.each{|isbn| puts "#{isbn}: #{validISBN13?(isbn)}" }
- Output:
978-0596528126: true 978-0596528120: false 978-1788399081: true 978-1788399083: false
Rust
fn main() {
let isbns = ["978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"];
isbns.iter().for_each(|isbn| println!("{}: {}", isbn, check_isbn(isbn)));
}
fn check_isbn(isbn: &str) -> bool {
if isbn.chars().filter(|c| c.is_digit(10)).count() != 13 {
return false;
}
let checksum = isbn.chars().filter_map(|c| c.to_digit(10))
.zip([1, 3].iter().cycle())
.fold(0, |acc, (val, fac)| acc + val * fac);
checksum % 10 == 0
}
- Output:
978-0596528126: true 978-0596528120: false 978-1788399081: true 978-1788399083: false
Seed7
$ include "seed7_05.s7i";
const func boolean: isISBN13 (in var string: input) is func
result
var boolean: isbn is FALSE;
local
var char: c is ' ';
var integer: digit is 0;
var integer: i is 1;
var integer: sum is 0;
begin
input := replace(input, " ", "");
input := replace(input, "-", "");
if length(input) = 13 then
for c range input do
digit := ord(c) - 48;
if not odd(i) then
digit *:= 3;
end if;
sum +:= digit;
incr(i);
end for;
isbn := sum rem 10 = 0;
end if;
end func;
const proc: main is func
local
var string: str is "";
begin
for str range [] ("978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083") do
writeln(str <& ": " <& isISBN13(str));
end for;
end func;
- Output:
978-0596528126: TRUE 978-0596528120: FALSE 978-1788399081: TRUE 978-1788399083: FALSE
SETL
program isbn13;
loop for test in [
"978-0596528126", "978-0596528120",
"978-1788399081", "978-1788399083"
] do
print(test + if valid_isbn13 test then ": good" else ": bad" end);
end loop;
op valid_isbn13(isbn);
if #isbn /= 14
or isbn(4) /= '-'
or exists c in isbn | not c in "0123456789-" then
return false;
end if;
m := 3;
loop for ch in isbn | ch in "0123456789" do
s +:= val ch * (m := 4 - m);
end loop;
return s mod 10 = 0;
end op;
end program;
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
Standard ML
Easy to read version
(* these type decorations are optional, you could just as well put:
fun isValidISBN s =
*)
fun isValidISBN (s : string) : bool =
let
val digits = List.filter Char.isDigit (explode s)
val nums = map (fn x => ord x - ord #"0") digits
val len = length nums
fun sumISBN [] = raise Domain
| sumISBN [x] = x
| sumISBN (x1::x2::xs) = x1 + 3*x2 + sumISBN xs
in
len = 13 andalso sumISBN nums mod 10 = 0
end
val test = ["978-1734314502", "978-1734314509",
"978-1788399081", "978-1788399083"]
fun printTest (s : string) : unit =
(print s; print (if isValidISBN s then ": good\n" else ": bad\n"))
fun main () = app printTest test
Original version
let
fun check (c, p as (m, n)) =
if Char.isDigit c then
(not m, (if m then 3 * (ord c - 48) else ord c - 48) + n)
else
p
in
fun checkISBN s =
Int.rem (#2 (CharVector.foldl check (false, 0) s), 10) = 0
end
val test = ["978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"]
val () = (print
o concat
o map (fn s => s ^ (if checkISBN s then ": good\n" else ": bad\n"))) test
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
Swift
func checkISBN(isbn: String) -> Bool {
guard !isbn.isEmpty else {
return false
}
let sum = isbn
.compactMap({ $0.wholeNumberValue })
.enumerated()
.map({ $0.offset & 1 == 1 ? 3 * $0.element : $0.element })
.reduce(0, +)
return sum % 10 == 0
}
let cases = [
"978-0596528126",
"978-0596528120",
"978-1788399081",
"978-1788399083"
]
for isbn in cases {
print("\(isbn) => \(checkISBN(isbn: isbn) ? "good" : "bad")")
}
- Output:
978-0596528126 => good 978-0596528120 => bad 978-1788399081 => good 978-1788399083 => bad
Tcl
proc validISBN13 code {
regsub -all {\D} $code "" code ;# remove non-digits
if {[string length $code] == 13} {
set sum 0
set fac 1
foreach digit [split $code ""] {
set sum [expr {$sum + $digit * $fac}]
set fac [expr {$fac == 1? 3: 1}]
}
if {$sum % 10 == 0} {return true}
}
return false
}
foreach test {
978-0596528126
978-0596528120
978-1788399081
978-1788399083
} {puts $test:[validISBN13 $test]}
- Output:
978-0596528126:true 978-0596528120:false 978-1788399081:true 978-1788399083:false
Simpler variant, using two loop vars and constant factors; same output:
proc validISBN13 code {
regsub -all {\D} $code "" code ;# remove non-digits
if {[string length $code] == 13} {
set sum 0
foreach {d1 d2} [split $code ""] {
if {$d2 eq ""} {set d2 0} ;# last round
set sum [expr {$sum + $d1 + $d2 * 3}]
}
if {$sum % 10 == 0} {return true}
}
return false
}
uBasic/4tH
a := "978-0596528126"
Print Show(a), Show (Iif (Func(_IsISBN (a)), "good", "bad"))
a := "978-0596528120"
Print Show(a), Show (Iif (Func(_IsISBN (a)), "good", "bad"))
a := "978-1788399081"
Print Show(a), Show (Iif (Func(_IsISBN (a)), "good", "bad"))
a := "978-1788399083"
Print Show(a), Show (Iif (Func(_IsISBN (a)), "good", "bad"))
End
_IsISBN
Param (1)
Local (4)
c@ = 0 : e@ = 1 ' set sum and multiplier
For b@ = Len (a@) - 1 To 0 Step -1 ' scan string in reverse
d@ = Peek(a@, b@) - Ord ("0") ' get character
If ((d@ < 0) + (d@ > 9)) = 0 Then c@ = c@ + (d@ * e@)
e@ = (e@ * 3) % 8 ' evaluate character, change multiplier
Next
Return ((c@ % 10) = 0) ' modulus 10 must be zero
- Output:
978-0596528126 good 978-0596528120 bad 978-1788399081 good 978-1788399083 bad 0 OK, 0:339
UNIX Shell
check_isbn13 () {
local i n t
n=${1//[^0-9]/}
t=0
for ((i=0; i<${#n}; ++i )); do
(( t += ${n:i:1}*(1 + ((i&1)<<1)) ))
done
(( 0 == t % 10 ))
}
for isbn in 978-0596528126 978-0596528120 978-1788399081 978-1788399083; do
printf '%s: ' "$isbn"
if check_isbn13 "$isbn"; then
printf '%s\n' OK
else
printf '%s\n' 'NOT OK'
fi
done
- Output:
978-0596528126: OK 978-0596528120: NOT OK 978-1788399081: OK 978-1788399083: NOT OK
Visual Basic .NET
Module Module1
Function CheckISBN13(code As String) As Boolean
code = code.Replace("-", "").Replace(" ", "")
If code.Length <> 13 Then
Return False
End If
Dim sum = 0
For Each i_d In code.Select(Function(digit, index) (index, digit))
Dim index = i_d.index
Dim digit = i_d.digit
If Char.IsDigit(digit) Then
sum += (Asc(digit) - Asc("0")) * If(index Mod 2 = 0, 1, 3)
Else
Return False
End If
Next
Return sum Mod 10 = 0
End Function
Sub Main()
Console.WriteLine(CheckISBN13("978-0596528126"))
Console.WriteLine(CheckISBN13("978-0596528120"))
Console.WriteLine(CheckISBN13("978-1788399081"))
Console.WriteLine(CheckISBN13("978-1788399083"))
End Sub
End Module
- Output:
True False True False
V (Vlang)
fn check_isbn13(isbn13 string) bool {
// remove any hyphens or spaces
isbn := isbn13.replace('-','').replace(' ','')
// check length == 13
le := isbn.len
if le != 13 {
return false
}
// check only contains digits and calculate weighted sum
mut sum := 0
for i, c in isbn.split('') {
if c.int() < '0'.int() || c.int() > '9'.int() {
return false
}
if i%2 == 0 {
sum += c.int() - '0'.int()
} else {
sum += 3 * (c.int() - '0'.int())
}
}
return sum%10 == 0
}
fn main() {
isbns := ["978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"]
for isbn in isbns {
mut res := "bad"
if check_isbn13(isbn) {
res = "good"
}
println("$isbn: $res")
}
}
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad
Wren
var isbn13 = Fn.new { |s|
var cps = s.codePoints
var digits = []
// extract digits
for (i in 0...cps.count) {
var c = cps[i]
if (c >= 48 && c <= 57) digits.add(c)
}
// do calcs
var sum = 0
for (i in 0...digits.count) {
var d = digits[i] - 48
sum = sum + ((i%2 == 0) ? d : 3 * d)
}
return sum % 10 == 0
}
var tests = ["978-0596528126", "978-0596528120", "978-1788399081", "978-1788399083"]
for (test in tests) {
System.print("%(test) -> %(isbn13.call(test) ? "good" : "bad")")
}
- Output:
978-0596528126 -> good 978-0596528120 -> bad 978-1788399081 -> good 978-1788399083 -> bad
XPL0
include xpllib; \contains StrLen function
proc ISBN13(Str); \Show if International Standard Book Number is good
char Str;
int Sum, Cnt, Dig, I;
[Sum:= 0; Cnt:= 0;
for I:= 0 to StrLen(Str)-1 do
[Dig:= Str(I) - ^0;
if Dig>=0 & Dig<=9 then
[Sum:= Sum + Dig;
Cnt:= Cnt + 1;
if (Cnt&1) = 0 then
Sum:= Sum + Dig + Dig;
];
];
Text(0, Str);
Text(0, if rem(Sum/10)=0 & Cnt=13 then ": good" else ": bad");
CrLf(0);
];
[ISBN13("978-0596528126");
ISBN13("978-0596528120");
ISBN13("978-1788399081");
ISBN13("978-1788399083");
ISBN13("978-1-59327-220-3");
ISBN13("978-178839918");
]
- Output:
978-0596528126: good 978-0596528120: bad 978-1788399081: good 978-1788399083: bad 978-1-59327-220-3: good 978-178839918: bad
zkl
fcn ISBN13_check(isbn){ // "978-0596528126", throws on invalid digits
var [const] one3=("13"*6 + 1).split("").apply("toInt"); // examine 13 digits
// one3=("13"*6) if you want to calculate what the check digit should be
one3.zipWith('*,isbn - " -").sum(0) % 10 == 0
}
isbns:=
#<<<"
978-0596528126
978-0596528120
978-1788399081
978-1788399083
978-2-74839-908-0
978-2-74839-908-5".split("\n");
#<<<
foreach isbn in (isbns)
{ println(isbn.strip()," ",ISBN13_check(isbn) and " Good" or " Bad") }
- Output:
978-0596528126 Good 978-0596528120 Bad 978-1788399081 Good 978-1788399083 Bad 978-2-74839-908-0 Good 978-2-74839-908-5 Bad