Module:Sandbox/trappist the monk/check digit

require('strict');

local function err_msg_make (msg)
	return '<span class="error" style="font-size:100%">error: ' .. msg .. '</span>'
end


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


local function check_digit_calc (id_str)
	local temp=0;
	
	id_str = { id_str:byte(1, 12) };											-- make a table of byte values '0' → 0x30 .. '9' → 0x39
	for i, v in ipairs( id_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
	temp = 10 - (temp % 10);													-- subtract sum modulo 10 from 10 to get the check digit
	return (10 == temp) and 0 or temp;											-- if the result is 10, return 0; return result else
end


local function main (frame)
	local id = frame.args[1];													-- get the id string
	local id_str = id;															-- copy that we will modify to do check digit calculations
	local check_digit;
	
	if id_str:match ('%D') then													-- look for characters that are not digits
		return err_msg_make ('id has non-digit characters: ' .. id_str);
	end
	
	if '' == id_str then														-- make sure id is not the empty string
		return err_msg_make ('id is empty');
	end
	
	if 12 < id_str:len() then													-- make sure that the id is no longer than 12 digits
		return err_msg_make ('id has too many characters (' .. id_str:len() .. '): ' .. id_str);
	end

	if 0 == tonumber(id_str) then												-- make sure that the id is not 0
		return err_msg_make ('id may not be zero');
	end

	id_str = string.rep ('0', 12-id:len()) .. id;								-- left fill with 0s to a string length of 12
	check_digit = check_digit_calc (id_str);									-- calculate the check digit
	if is_valid_isxn_13 (id_str .. check_digit) then							-- append check digit to 12-digit id and validate
		return id .. '.' .. check_digit;										-- return original id with dot-separated check digit
	else
		return err_msg_make ('calculation failure: ' .. check_digit)							-- did not validate
	end
end

return {main = main}