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 "' .. args[1] .. '" 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() > 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() > 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 < 0 then

frac_part = frac_part:sub(1,precision)

elseif offset > 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

return p