local module = {} --[[--------------------------< IS _ V A L I D _ I S X N >----------------------------------------------------- ISBN-10 and ISSN validator code calculates checksum across all isbn/issn digits including the check digit. ISBN-13 is checked in check_isbn(). If the number is valid the result will be 0. Before calling this function, issbn/issn must be checked for length and stripped of dashes, spaces and other non-isxn characters. ]] local function is_valid_isxn (isxn_str, len) local temp = 0; isxn_str = { isxn_str:byte(1, len) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39, 'X' → 0x58 len = len+1; -- adjust to be a loop counter for i, v in ipairs( isxn_str ) do -- loop through all of the bytes and calculate the checksum if v == string.byte( "X" ) then -- if checkdigit is X (compares the byte value of 'X' which is 0x58) temp = temp + 10*( len - i ); -- it represents 10 decimal else temp = temp + tonumber( string.char(v) )*(len-i); end end return temp % 11 == 0; -- returns true if calculation result is zero end --[[--------------------------< IS _ V A L I D _ I S X N _ 1 3 >---------------------------------------------- ISBN-13 and ISMN validator code calculates checksum across all 13 isbn/ismn digits including the check digit. If the number is valid, the result will be 0. Before calling this function, isbn-13/ismn must be checked for length and stripped of dashes, spaces and other non-isxn-13 characters. ]] local function is_valid_isxn_13 (isxn_str) local temp = 0; isxn_str = { isxn_str:byte(1, 13) }; -- make a table of byte values '0' → 0x30 .. '9' → 0x39 for i, v in ipairs( isxn_str ) do temp = temp + (3 - 2*(i % 2)) * tonumber( string.char(v) ); -- multiply odd index digits by 1, even index digits by 3 and sum; includes check digit end return temp % 10 == 0; -- sum modulo 10 is zero when isbn-13/ismn is correct end --[[--------------------------< C H E C K _ I S B N >------------------------------------------------------------ Determines whether an ISBN string is valid ]] function module.check_isbn( frame ) local isbn_str = mw.text.trim(frame.args[1] or '') if nil ~= isbn_str:match("[^%s-0-9X]") then return false; end -- fail if isbn_str contains anything but digits, hyphens, or the uppercase X isbn_str = isbn_str:gsub( "-", "" ):gsub( " ", "" ); -- remove hyphens and spaces local len = isbn_str:len(); if len ~= 10 and len ~= 13 then return false; end if len == 10 then if isbn_str:match( "^%d*X?$" ) == nil then return false; end return is_valid_isxn(isbn_str, 10); else local temp = 0; if isbn_str:match( "^97[89]%d*$" ) == nil then return false; end -- isbn13 begins with 978 or 979; ismn begins with 979 return is_valid_isxn_13 (isbn_str); end end return module