Module:Gapnum
local p = {}
 
 local getArgs
 
 function p.main(frame)
 	if not getArgs then
 		getArgs = require('Module:Arguments').getArgs
 	end
 
 	local args = getArgs(frame, {wrappers = 'Template:Gapnum'})
 	local n = args[1]
 
 	if not n then
 		error('Parameter 1 is required')
 	elseif not tonumber(n) and not tonumber(n, 36) then -- Validates any number with base ≤ 36
 		error('Unable to convert &quot;' .. args[1] .. '&quot; to a number')
 	end
 
 	local gap = args.gap
 	local precision = tonumber(args.prec)
 
 	return p.gaps(n,{gap=gap,prec=precision})
 end
 
 -- Not named p._main so that it has a better function name when required by Module:Val
 function p.gaps(n,tbl)
 	local nstr = tostring(n)
 	if not tbl then
 		tbl = {}
 	end
 	local gap = tbl.gap or '.25em'
 
 	local int_part, frac_part = p.groups(n,tbl.prec)
 
 	local ret = mw.html.create('span')
 							:css('white-space','nowrap')
 							-- No gap necessary on first group
 							:wikitext(table.remove(int_part,1))
 
 	-- Build int part
 	for _, v in ipairs(int_part) do
 		ret:tag('span')
 				:css('margin-left',gap)
 				:wikitext(v)
 	end
 
 	if frac_part then
 		-- The first group after the decimal shouldn't have a gap
 		ret:wikitext('.' .. table.remove(frac_part,1))
 		-- Build frac part
 		for _, v in ipairs(frac_part) do
 			ret:tag('span')
 					:css('margin-left',gap)
 					:wikitext(v)
 		end
 	end
 
 	return ret
 end
 
 -- Creates tables where each element is a different group of the number
 function p.groups(num,precision)
 	local nstr = tostring(num)
 	if not precision then
 		precision = -1
 	end
 
 	local decimalloc = nstr:find('.', 1, true)
 	local int_part, frac_part
 	if decimalloc == nil then
 		int_part = nstr
 	else
 		int_part = nstr:sub(1, decimalloc-1)
 		frac_part = nstr:sub(decimalloc + 1)
 	end
 	-- only define ret_i as an empty table, let ret_d stay nil
 	local ret_i,ret_d = {}
 	-- Loop to handle most of the groupings; from right to left, so that if a group has less than 3 members, it will be the first group
 	while int_part:len() &gt; 3 do
 		-- Insert in first spot, since we're moving backwards
 		table.insert(ret_i,1,int_part:sub(-3))
 		int_part = int_part:sub(1,-4)
 	end
 	-- handle any left over numbers
 	if int_part:len() &gt; 0 then
 		table.insert(ret_i,1,int_part)
 	end
 
 	if precision ~= 0 and frac_part then
 		ret_d = {}
 		if precision == -1 then
 			precision = frac_part:len()
 		end
 		-- Reduce the length of the string if required precision is less than actual precision
 		-- OR
 		-- Increase it (by adding 0s) if the required precision is more than actual
 		local offset = precision - frac_part:len()
 		if offset &lt; 0 then
 			frac_part = frac_part:sub(1,precision)
 		elseif offset &gt; 0 then
 			frac_part = frac_part .. string.rep('0', offset)
 		end
 
 		-- Allow groups of 3 or 2 (3 first)
 		for v in string.gmatch(frac_part,'%d%d%d?') do
 			table.insert(ret_d,v)
 		end
 		-- Preference for groups of 4 instead of groups of 1 at the end
 		if #frac_part % 3 == 1 then
 			if frac_part:len() == 1 then
 				ret_d = {frac_part}
 			else
 				local last_g = ret_d[#ret_d] or ''
 				last_g = last_g..frac_part:sub(-1)
 				ret_d[#ret_d] = last_g
 			end
 		end
 	end
 
 	return ret_i,ret_d
 end