বিষয়বস্তুতে চলুন

মডিউল:Gapnum

উইকিপিডিয়া, মুক্ত বিশ্বকোষ থেকে

local p = {}

local getArgs

function p.main(frame)
	if not getArgs then
		getArgs = require('Module:Arguments').getArgs
	end

	local args = getArgs(frame, {wrappers = 'টেমপ্লেট: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

local default_method = 2  -- 2 or 3 for default grouping method
local default_gaps3 = false  -- if true, group only in threes after decimal mark; example: "1.234 5" rather than "1.2345"
function p.groups_val(text, method)
	-- Parameter text is a number as a string with an optional '.'.
	-- Parameter method is a number or nil:
	--   3 for 3-digit grouping, or
	--   2 for 3-then-2 grouping (for digits before decimal mark).
	-- Return one or two lists of digit groups.
	-- The first list has the groups before any decimal mark.
	-- If there is a decimal mark, the second list has the groups after the mark.
	method = method or default_method
	local len_right  -- nil or number of digits after decimal mark (0 or more)
	local len_left = text:find('.', 1, true)
	if len_left then
		len_right = #text - len_left
		len_left = len_left - 1
	else
		len_left = #text
	end
	local n3, n2, nR  -- number of three-groups, two-groups, remainder digits
	-- Handle left-hand side (digits before decimal mark).
	if method == 3 then
		n3 = math.floor(len_left / 3)
		n2 = 0
		nR = math.fmod(len_left, 3)
	elseif len_left < 3 then
		n3 = 0
		n2 = math.floor(len_left / 2)
		nR = math.fmod(len_left, 2)
	else
		n3 = 1
		n2 = math.floor((len_left - 3) / 2)
		nR = math.fmod((len_left - 3), 2)
	end
	local l_groups = {}
	if nR > 0 then
		table.insert(l_groups, text:sub(1, nR))
	end
	local pos = nR + 1
	for _ = 1, n2 do
		table.insert(l_groups, text:sub(pos, pos + 1))
		pos = pos + 2
	end
	for _ = 1, n3 do
		table.insert(l_groups, text:sub(pos, pos + 2))
		pos = pos + 3
	end
	-- Handle right-hand side (digits after decimal mark).
	-- Methods 2 and 3 are the same (group by three or possibly four for last group).
	local r_groups
	if len_right then
		n3 = math.floor(len_right / 3)
		nR = math.fmod(len_right, 3)
		pos = len_left + 2
		local extra
		if n3 > 0 and nR == 1 and not default_gaps3 then
			extra = true  -- append extra digit to last group
			nR = 0
		end
		r_groups = {}
		for i = 1, n3 do
			local x = (extra and i == n3) and 1 or 0
			table.insert(r_groups, text:sub(pos, pos + 2 + x))
			pos = pos + 3 + x
		end
		if nR > 0 then
			table.insert(r_groups, text:sub(pos, pos + nR - 1))
		end
	end
	return l_groups, r_groups
end

return p