I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

# ISBN13 check digit

ISBN13 check digit
You are encouraged to solve this task according to the task description, using any language you may know.

Validate the check digit of an ISBN-13 code:

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

Use the following codes for testing:

•   978-1734314502       (good)
•   978-1788399081       (good)

## 11l

Translation of: Python
`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-1734314502            978-1734314509            978-1788399081            978-1788399083’.split("\n") L(t) tests   print(‘ISBN13 ’t‘ validates ’is_isbn13(t))`
Output:
```ISBN13 978-1734314502 validates 1B
ISBN13 978-1734314509 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 charactersisbnlp:	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 tenisbndv:	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	retisbngc:	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	5good:	db	'good\$'bad:	db	'bad\$'`
Output:
```A>isbn13 978-1734314502
good
A>isbn13 978-1734314509
A>isbn13 978-1788399081
good
A>isbn13 978-1788399083

## 8086 Assembly

`	cpu	8086	bits	16	org	100hsection	.text	jmp	demoisbn13:	;;;	---------------------------------------------------------------	;;;	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	retsection	.datagood:	db	'good\$'bad: 	db	'bad\$'`
Output:
```C:\>isbn13 978-1734314502
good
C:\>isbn13 978-1734314509
C:\>isbn13 978-1788399081
good
C:\>isbn13 978-1788399083

## 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)  FIRETURN (1) PROC Test(CHAR ARRAY t)  BYTE correct   correct=CheckISBN13(t)  Print(t) Print(" is ")  IF correct THEN    PrintE("correct")  ELSE    PrintE("incorrect")  FIRETURN PROC Main()  Put(125) PutE() ;clear screen   Test("978-1734314502")  Test("978-1734314509")  Test("978-1788399081")  Test("978-1788399083")RETURN`
Output:
```978-1734314502 is correct
978-1734314509 is incorrect
978-1788399081 is correct
978-1788399083 is incorrect
```

`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-1734314502");   Show ("978-1734314509");   Show ("978-1788399081");   Show ("978-1788399083");end ISBN_Check;`
Output:
```978-1734314502  Good
978-1788399081  Good

## ALGOL 68

Works with: ALGOL 68G version Any - tested with release 2.8.3.win32
`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-1734314502", "978-1734314509", "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               )             )    ODEND`
Output:
```978-1734314502: good
978-1788399081: good
```

## APL

Works with: Dyalog APL
`check_isbn13←{    13≠⍴n←(⍵∊⎕D)/⍵:0    0=10|(⍎¨n)+.×13⍴1 3}`
Output:
```      check_isbn13¨ '978-1734314502' '978-1734314509' '978-1788399081' '978-1788399083'
1 0 1 0```

## AppleScript

### Composition of pure functions

`-------------------- ISBN13 CHECK DIGIT -------------------- -- isISBN13 :: String -> Boolon 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 10end isISBN13  --------------------------- TEST ---------------------------on run    script test        on |λ|(s)            {s, isISBN13(s)}        end |λ|    end script     map(test, {"978-1734314502", "978-1734314509", ¬        "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 accend 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 scriptend cycle  -- foldl :: (a -> b -> a) -> a -> [b] -> aon 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 tellend foldl  -- isDigit :: Char -> Boolon isDigit(c)    set n to (id of c)    48 ≤ n and 57 ≥ nend isDigit  -- length :: [a] -> Inton |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 ifend |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 tellend map  -- min :: Ord a => a -> a -> aon min(x, y)    if y < x then        y    else        x    end ifend 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 ifend mReturn  -- mul (*) :: Num a => a -> a -> aon mul(a, b)    a * bend mul  -- sum :: [Num] -> Numon 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 -> Stringon 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 ifend 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 tellend zipWith`
Output:
`{{"978-1734314502", true}, {"978-1734314509", 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-1734314502", "978-1734314509", "978-1788399081", "978-1788399083"}    set isValid to validateISBN13(thisISBN13)    set end of output to thisISBN13 & ": " & item ((isValid as integer) + 1) of verdictsend repeat set astid to AppleScript's text item delimitersset AppleScript's text item delimiters to linefeedset output to output as textset AppleScript's text item delimiters to astidreturn output`
Output:
```"978-1734314502: good
978-1788399081: good

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-1734314502" "978-1734314509"    "978-1788399081" "978-1788399083"] loop tests 'test [    print [test "=>" validISBN? test]]`
Output:
```978-1734314502 => true
978-1734314509 => 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-1734314502","978-1734314509","978-1788399081","978-1788399083"]for i, n in nums	output .= ISBN13_check_digit(n) "`n"MsgBox % outputreturn`
Output:
```978-1734314502	(good)
978-1788399081	(good)

## AWK

` # syntax: GAWK -f ISBN13_CHECK_DIGIT.AWKBEGIN {    arr[++n] = "978-1734314502"    arr[++n] = "978-1734314509"    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-1734314502 OK
978-1734314509 NG check digit S/B 2
978-1788399081 OK
978-1788399083 NG check digit S/B 1
9780820424521 OK
0820424528 NG length
```

## 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-1734314502")    show("978-1734314509")    show("978-1788399081")    show("978-1788399083")\$)`
Output:
```978-1734314502: good
978-1788399081: good

## 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-1734314502", "978-1734314509", "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-1734314502: good
978-1788399081: good
```

## C++

Translation of: 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-1734314502", "978-1734314509", "978-1788399081", "978-1788399083" };    for (auto isbn : isbns) {        std::cout << isbn << ": " << (check_isbn13(isbn) ? "good" : "bad") << '\n';    }     return 0;}`
Output:
```978-1734314502: good
978-1788399081: good

## C#

`using System;using System.Linq; public class Program{    public static void Main() {        Console.WriteLine(CheckISBN13("978-1734314502"));        Console.WriteLine(CheckISBN13("978-1734314509"));        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)    endend isbn13_check start_up = proc ()    po: stream := stream\$primary_output()    tests: array[string] := array[string]\$       ["978-1734314502",        "978-1734314509",        "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    endend start_up`
Output:
```978-1734314502: good
978-1788399081: good

## COBOL

Works with: GnuCOBOL
`      ******************************************************************      * 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-1734314502'.           02  FILLER                  PIC X(14) VALUE '978-1734314509'.           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-1734314502   (good)
978-1788399081   (good)
```

## 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-1734314502", "978-1734314509", "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-1734314502: good
978-1788399081: good

## D

Translation of: Kotlin
`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-1734314502"));    assert(!isValidISBN13("978-1734314509"));    assert(isValidISBN13("978-1788399081"));    assert(!isValidISBN13("978-1788399083"));}`

## 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 = 0corp proc nonrec test(*char isbn) void:    writeln(isbn, ": ",             if isbn13_check(isbn) then "good" else "bad" fi)corp proc nonrec main() void:    test("978-1734314502");    test("978-1734314509");    test("978-1788399081");    test("978-1788399083")corp`
Output:
```978-1734314502: good
978-1788399081: good

## Excel

### LAMBDA

Binding the name ISBN13Check to the following lambda expression in the Name Manager of the Excel WorkBook:

`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:
 =ISBN13Check(A2) fx A B 1 Candidate string ISBN13 checked 2 978-1734314502 TRUE 3 978-1734314509 FALSE 4 978-1788399081 TRUE 5 978-1788399083 FALSE

## Factor

`USING: combinators.short-circuit formatting kernel mathmath.functions math.parser math.vectors qw sequencessequences.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-1734314502 978-1734314509 978-1788399081 978-1788399083 }[ dup isbn13? "good" "bad" ? "%s: %s\n" printf ] each`
Output:
```978-1734314502: good
978-1788399081: good
```

## 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 [email protected] 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-1734314502" isbn? .  -1  ok
s" 978-1734314509" 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-1734314502", "978-1734314509", "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 docontains    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_isbn13end program isbn13 `
Output:
``` 978-1734314502 : good
978-1788399081 : good
```

## 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 falseend 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 ifend function dim as string isbns(0 to 3) = { "978-1734314502", "978-1734314509", "978-1788399081", "978-1788399083" }dim as uinteger ifor i = 0 to 3    if is_good_isbn( isbns(i) ) then        print isbns(i)+": good"    else        print isbns(i)+": bad"    end ifnext i`
Output:
```978-1734314502: good
978-1788399081: good
```

## F#

`// ISBN13 Check Digitopen 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-1734314502 Valid ISBN13
978-1734314509 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, However they run on execution servers. By default remote servers are used, but they are limited in memory and processing power, since they are intended for demonstration and casual use. A local server can be downloaded and installed, it has no limitations (it runs in your own computer). Because of that, example programs can be fully visualized and edited, but some of them will not run if they require a moderate or heavy computation/memory resources, and no local server is being used.

## 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-1734314502", "978-1734314509", "978-1788399081", "978-1788399083"}    for _, isbn := range isbns {        res := "bad"        if checkIsbn13(isbn) {            res = "good"        }        fmt.Printf("%s: %s\n", isbn, res)    }}`
Output:
```978-1734314502: good
978-1788399081: good
```

`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 -> BoolvalidIsbn13 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-1734314502",      "978-1734314509",      "978-1788399081",      "978-1788399083"    ]`
Output:
```978-1734314502: Valid: True
978-1734314509: Valid: False
978-1788399081: Valid: True
978-1788399083: Valid: False```

Or, expressed in terms of cycle:

`import Data.Char (digitToInt, isDigit) isISBN13 :: String -> BoolisISBN13 =  (0 ==)    . flip rem 10    . sum    . flip      (zipWith ((*) . digitToInt) . filter isDigit)      (cycle [1, 3]) main :: IO ()main =  mapM_    (print . ((,) <*> isISBN13))    [ "978-1734314502",      "978-1734314509",      "978-1788399081",      "978-1788399083"    ]`
Output:
```("978-1734314502",True)
("978-1734314509",False)
("978-1788399081",True)
("978-1788399083",False)```

## IS-BASIC

`100 PROGRAM "ISBN13.bas"110 DO120   PRINT :INPUT PROMPT "ISBN-13 code: ":IS\$130   IF IS\$="" THEN EXIT DO140   IF ISBN(IS\$) THEN150     PRINT "Ok."160   ELSE170     PRINT "CRC error."180   END IF190 LOOP200 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   NEXT250   LET TRIM\$=T\$260 END DEF270 DEF ISBN(S\$)280   LET SUM,ISBN=0:LET ISBN\$=TRIM\$(IS\$)290   IF LEN(ISBN\$)<>13 THEN PRINT "Invalid length.":EXIT DEF300   FOR I=1 TO 11 STEP 2310     LET SUM=SUM+VAL(ISBN\$(I))+VAL(ISBN\$(I+1))*3320   NEXT330   LET SUM=SUM+VAL(ISBN\$(13))340   IF MOD(SUM,10)=0 THEN LET ISBN=-1350 END DEF`

## J

`   D            =:  '0123456789'    isbn13c      =:  D&([ [email protected]:i. clean)     check      =:  0 = 10 | lc       lc       =:  [ +/@:* weight         weight =:  1 3 \$~ #     clean      =:  ] -. a. -. [`
Output:
`   isbn13c;._1 ' 978-1734314502 978-1734314509 978-1788399081 978-1788399083'1 0 1 0`

## Java

`public static void main(){        System.out.println(isISBN13("978-1734314502"));        System.out.println(isISBN13("978-1734314509"));        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: 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-1734314502", "978-1734314509",  "978-1788399081", "978-1788399083"]; testingcodes[]| "\(.): \(if isbn_check then "good" else "bad" end)" `
Output:
```978-1734314502: good
978-1788399081: good
```

## 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 == 0end const testingcodes = ["978-1734314502", "978-1734314509",                      "978-1788399081", "978-1788399083"] for code in testingcodes    println(code, ": ", isbncheck(code) ? "good" : "bad")end `
Output:
```978-1734314502: good
978-1788399081: good
```

## 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-1734314502" to true,        "978-1734314509" 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-1734314502: good
978-1788399081: good
```

## langur

Works with: langur version 0.8.11

In this example, we map to multiple functions (actually 1 no-op).

`val .isbn13checkdigit = f(var .s) {    .s = replace(.s, RE/[\- ]/)    matching(re/^[0-9]{13}\$/, .s) and        fold(f{+}, map [_, f{x 3}], s2n .s) div 10} val .tests = h{    "978-1734314502": true,    "978-1734314509": 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)")}`
Works with: langur version 0.9.0

In this example, we set a for loop value as it progresses.

`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} val .tests = h{    "978-1734314502": true,    "978-1734314509": 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-1734314502: good
978-1788399081: good

## Lua

Translation of: C
`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) == 0end function test(isbn)    if checkIsbn13(isbn) then        print(isbn .. ": good")    else        print(isbn .. ": bad")    endend function main()    test("978-1734314502")    test("978-1734314509")    test("978-1788399081")    test("978-1788399083")end main()`
Output:
```978-1734314502: good
978-1788399081: good

## 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-1734314502"]ValidISBNQ["978-1734314509"]ValidISBNQ["978-1788399081"]ValidISBNQ["978-1788399083"]`
Output:
```True
False
True
False```

## 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-1734314502');    check('978-1734314509');    check('978-1788399081');    check('978-1788399083');END ISBN.`
Output:
```978-1734314502: good
978-1788399081: good

## Nanoquery

Translation of: Go
`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) = 0end isbns = {"978-1734314502", "978-1734314509", "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-1734314502: good
978-1788399081: good

## 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-1734314502", "978-1734314509",                "978-1788399081", "978-1788399083" ]  for isbn in isbns:    var quality: string = if is_isbn(isbn): "good" else: "bad"    echo &"{isbn}: {quality}"`
Output:
```978-1734314502: good
978-1788399081: good
```

## Pascal

Works with: Extended 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 = 0end; { 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 := resultend; { 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-1734314502'));	writeLn(isValidISBNString('978-1734314509'));	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-1734314502 978-1734314509 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-1734314502 : Good
978-1734314509 : 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-1734314502", "978-1734314509", "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-1734314502: good
978-1788399081: good
978-2-74839-908-0: good
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-1734314502"      "978-1734314509"      "978-1-86197-876-9"      "978-2-74839-908-5"      "978 1 86197 876 9" ) )`
Output:
```978-1734314502     ok
978-1734314509     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-1734314502\$';TEST(1) = .'978-1734314509\$';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-1734314502: GOOD
978-1788399081: GOOD

## PowerShell

` function Get-ISBN13 {    \$codes = (        "978-1734314502",        "978-1734314509",        "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-1734314502 Good
978-1788399081 Good
```

## 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-1734314502")  TestISBN13("978-1734314509")  TestISBN13("978-1788399081")  TestISBN13("978-1788399083")  Input()EndIf`
Output:
```978-1734314502 good
978-1788399081 good
```

## 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-1734314502978-1734314509978-1788399081978-1788399083'''.strip().split()    for t in tests:        print(f"ISBN13 {t} validates {is_isbn13(t)}")`
Output:
```ISBN13 978-1734314502 validates True
ISBN13 978-1734314509 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 -> Booldef 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-1734314502",            "978-1734314509",            "978-1788399081",            "978-1788399083"            ]    ))  # MAIN ---if __name__ == '__main__':    main() `
Output:
```('978-1734314502', True)
('978-1734314509', 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-1734314502' isbn-test\$ '978-1734314509' isbn-test\$ '978-1788399081' isbn-test\$ '978-1788399083' isbn-test`
Output:
```978-1734314502: Good
978-1788399081: Good
```

## 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-1734314502"))  (check-false (isbn13-check-digit-valid? "978-1734314509"))  (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)

Works with: Rakudo version 2019.11

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-1734314502    978-1734314509    978-1788399081    978-1788399083    978-2-74839-908-0    978-2-74839-908-5>;`
Output:
```978-1734314502 : Good
978-1734314509 : 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 examplesforeach [str] ["978-1734314502" "978-1734314509" "978-1788399081" "978-1788399083"] [  prin str  prin " - "  print check_valid_isbn13 str] `
Output:
```978-1734314502 - true
978-1734314509 - false
978-1788399081 - true
978-1788399083 - false
```

## 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-1734314502 978-1734314509 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-1734314502","978-1734314509", "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    oknext  `

Output:

```978-1734314502: true
978-1788399081: true
978-2-74839-908-0: true
978 1 86197 876 9: true
```

## 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) == 0end isbns = ["978-1734314502", "978-1734314509", "978-1788399081", "978-1788399083"]isbns.each{|isbn| puts "#{isbn}: #{validISBN13?(isbn)}" } `
Output:
```978-1734314502: true
978-1734314509: false
978-1788399081: true
978-1788399083: false
```

## Rust

`fn main() {    let isbns = ["978-1734314502", "978-1734314509", "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-1734314502: true
978-1734314509: 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-1734314502", "978-1734314509", "978-1788399081", "978-1788399083") do      writeln(str <& ": " <& isISBN13(str));    end for;  end func;`
Output:
```978-1734314502: TRUE
978-1734314509: FALSE
978-1788399081: TRUE
978-1788399083: FALSE
```

## Standard ML

`local  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      pin  fun checkISBN s =    Int.rem (#2 (CharVector.foldl check (false, 0) s), 10) = 0end val test = ["978-1734314502", "978-1734314509", "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-1734314502: good
978-1788399081: good

## 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-1734314502",  "978-1734314509",  "978-1788399081",  "978-1788399083"] for isbn in cases {  print("\(isbn) => \(checkISBN(isbn: isbn) ? "good" : "bad")")}`

Output:
```978-1734314502 => good
978-1788399081 => good

## 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-1734314502   978-1734314509   978-1788399081   978-1788399083} {puts \$test:[validISBN13 \$test]} `
Output:
```978-1734314502:true
978-1734314509: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

Works with: version 3.64.0
`a := "978-1734314502" Print Show(a), Show (Iif (Func(_IsISBN (a)), "good", "bad")) a := "978-1734314509"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)   [email protected] = 0 : [email protected] = 1                      ' set sum and multiplier   For [email protected] = Len ([email protected]) - 1 To 0 Step -1   ' scan string in reverse    [email protected] = Peek([email protected], [email protected]) - Ord ("0")      ' get character    If (([email protected] < 0) + ([email protected] > 9)) = 0 Then [email protected] = [email protected] + ([email protected] * [email protected])    [email protected] = ([email protected] * 3) % 8                  ' evaluate character, change multiplier  Next Return (([email protected] % 10) = 0)                 ' modulus 10 must be zero`
Output:
```978-1734314502  good
978-1788399081  good

0 OK, 0:339```

## UNIX Shell

Works with: Bourne Again 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-1734314502 978-1734314509 978-1788399081 978-1788399083; do  printf '%s: ' "\$isbn"  if check_isbn13 "\$isbn"; then    printf '%s\n' OK  else    printf '%s\n' 'NOT OK'  fidone`
Output:
```978-1734314502: OK
978-1734314509: NOT OK
978-1788399081: OK
978-1788399083: NOT OK```

## Visual Basic .NET

Translation of: C#
`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-1734314502"))        Console.WriteLine(CheckISBN13("978-1734314509"))        Console.WriteLine(CheckISBN13("978-1788399081"))        Console.WriteLine(CheckISBN13("978-1788399083"))    End Sub End Module`
Output:
```True
False
True
False```

## Vlang

Translation of: go
`fn check_isbn13(isbn13 string) bool {    // remove any hyphens or spacesisbn := 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-1734314502", "978-1734314509", "978-1788399081", "978-1788399083"]    for isbn in isbns {        mut res := "bad"        if check_isbn13(isbn) {            res = "good"        }        println("\$isbn: \$res")    }}`
Output:
```978-1734314502: good
978-1788399081: good
```

## 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-1734314502", "978-1734314509", "978-1788399081", "978-1788399083"]for (test in tests) {    System.print("%(test) -> %(isbn13.call(test) ? "good" : "bad")")}`
Output:
```978-1734314502 -> good
978-1788399081 -> good
```

## XPL0

`include xpllib;         \contains StrLen function proc ISBN13(Str);       \Show if International Standard Book Number is goodchar 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-1734314502"); ISBN13("978-1734314509"); ISBN13("978-1788399081"); ISBN13("978-1788399083"); ISBN13("978-1-59327-220-3"); ISBN13("978-178839918");]`
Output:
```978-1734314502: good
978-1788399081: good
978-1-59327-220-3: good
```

## zkl

`fcn ISBN13_check(isbn){  // "978-1734314502", 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-1734314502    978-1734314509    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-1734314502   Good