local p = {} local convert = require('মডিউল:সংখ্যা রূপান্তরকারী').convert  local function en(a) 	return tonumber(convert('en', tostring(a))) end  local function bn(a) 	return convert('bn', tostring(a)) end  local function roundUpToNextDigit(x)     if x <= 0 then return 0 end     local log10 = math.log(x) / math.log(10)     local pow10 = math.floor(log10)     local base = math.floor(x / (10 ^ pow10))     return (base + 1) * (10 ^ pow10) end  local function maxValue(args)     local max = 0     for k, v in pairs(args) do         local n = tonumber(k)         local val = tonumber(v)         if n and n % 2 == 0 and val and val > max then             max = val         end     end     return roundUpToNextDigit(max) end  function p.main(frame)     local args = frame.args     local out = {}     local alignment = args.alignment or args.position or 'center'     local styleMap = {         left = 'float:left; clear:left; margin-right:1em;',         ['left-stack'] = 'float:left; margin-right:1em;',         right = 'float:right; clear:right; margin-left:1em;',         ['right-stack'] = 'float:right; margin-left:1em;',         center = 'float:none; margin-left:auto; margin-right:auto;',         none =     }     local style = styleMap[alignment] or styleMap.center      local last     local lastIdx      for k, v in pairs(args) do         local n = tonumber(k)         if n and n % 2 == 1 and (not lastIdx or n > lastIdx) then             lastIdx = n             last = v         end     end         -- Header     local chartTitle = args.full_name or          ('Vertical bar chart ' .. (args.type or ) .. ' of ' ..         (args.name or frame:preprocess('')) ..          ' between ' .. (args["1"] or ) .. ' and ' .. (last or args["1"] or ))      table.insert(out, '<table class="wikitable" style="' .. style .. '">')     table.insert(out, '' .. chartTitle .. '')     table.insert(out, '')      -- Get max value     local max = maxValue(args)     local period = max      -- Prepare scale values     local large_scale = args.large_scale or ( 10 ^ math.floor(math.log(tonumber(max)) / math.log(10)) )     local small_scale = args.small_scale or ( math.ceil((10 ^ math.floor(math.log(max) / math.log(10))) / 5) )      local timeline = {}     local color = args.color or 'rgb(0.6,0.7,1)'     local height = args.height or '300'     local width = args.width or 'auto barincrement:35'      -- Basic setup     table.insert(timeline, 'Colors=')     table.insert(timeline, '  id:a value:gray(0.9)')     table.insert(timeline, '  id:b value:gray(0.7)')     table.insert(timeline, '  id:c value:rgb(1,1,1)')     table.insert(timeline, '  id:d value:' .. color)      table.insert(timeline, ('ImageSize = width:%s height:%s'):format(width, height))     table.insert(timeline, 'PlotArea  = left:50 bottom:30 top:30 right:30')     table.insert(timeline, 'DateFormat = x.y')     table.insert(timeline, 'Period   = from:0 till:' .. period)     table.insert(timeline, 'TimeAxis  = orientation:vertical')     table.insert(timeline, 'AlignBars = justify')     table.insert(timeline, 'ScaleMajor = gridcolor:b increment:' .. large_scale .. ' start:0')     table.insert(timeline, 'ScaleMinor = gridcolor:a increment:' .. small_scale .. ' start:0')     table.insert(timeline, 'BackgroundColors = canvas:c')      -- Optional text     if args.text then         table.insert(timeline, 'TextData = tabs:(65-center,165-center)')         table.insert(timeline, '  pos:(10,295) textcolor:black fontsize:S text:' .. args.text)         table.insert(timeline, '  pos:(30,297) textcolor:black fontsize:M')     end      -- Bar data: numeric bar IDs with label text quoted     table.insert(timeline, 'BarData=')     for i = 1, 99, 2 do         local label = args[i]         if label then             table.insert(timeline, ('  bar:%d text:"%s"'):format(en(label), bn(label)))         end     end      -- Plot data: bars with values and optional colors     table.insert(timeline, 'PlotData=')     table.insert(timeline, '  color:d width:20 align:left') 	for i = 1, 99, 2 do 	    local bar = tonumber(args[i]) 	    local value = tonumber(args[tostring(i + 1)]) 	    local j = math.ceil(i / 2) 	    local colorKey = args['color_' .. tostring(j)] 	    if value then 	        if colorKey then 	            table.insert(timeline, ('  bar:%d from:0 till:%s color:%s'):format(en(bar), value, colorKey)) 	        else 	            table.insert(timeline, ('  bar:%d from:0 till:%s'):format(en(bar), value)) 	        end 	    end 	end     -- Value labels on bars     table.insert(timeline, 'PlotData=')     for i = 1, 99, 2 do         local value = tonumber(args[tostring(i + 1)])         local label = args[i]         if label and value then             local textVal = mw.getContentLanguage():formatNum(value)             table.insert(timeline, ('  bar:%d at:%s fontsize:s text:%s shift:(-10,5)'):format(en(label), value, textVal))         end     end      local timelineText = ''      if args.test == "yes" then         -- Show raw timeline wikitext escaped for debugging         table.insert(out, '' .. timelineText .. '')     else         -- Normal rendering using parser function         local parsed = frame:callParserFunction{             name = '#tag',             args = { 'timeline', table.concat(timeline, '\n') }         }         table.insert(out, parsed)     end      -- Optional note row     if args.note then         table.insert(out, '' .. args.note .. '')     end      table.insert(out, '</td></tr></table>')     return table.concat(out, '\n') end  return p