Jump to content

Module:Calculator widget

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Bawolff (talk | contribs) at 10:28, 2 January 2025. The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

local p = {}

-- Make a calculator widget using {{calculator}}
-- As a work around to the fact that buttons can only affect one field, store everything in a single number

-- layout (status = bits 0-2)
-- 9 bits mem, 2 bits memexp, 17 bits x reg, 2 bits exp, 17 bits y reg, 2 bits exp, 1 bit decimal flag, 2 bit status.
-- Negative means show Y reg instead of X reg.
-- status:
-- 0 = Add
-- 1 = sub
-- 2 = mul
-- 3 = div

local ALL_START = 0
local STATUS_START = 0
local STATUS_END = 2
local DECIMAL_START = 2
local DECIMAL_END = 3
local Y_EXP_START = 3
local Y_EXP_END = 5
local Y_START = 5
local Y_END = 22
local X_EXP_START = 22
local X_EXP_END = 24
local X_START = 24
local X_END = 41
local MEM_EXP_START = 41
local MEM_EXP_END = 43
local MEM_START = 43
local MEM_END = 52
local ALL_END = 52

-- These functions return {{calculator}} formulas
-- that are evaled client side.

-- Get value of packed variable
local function unpack(startIndex, endIndex)
	local s = 2^startIndex
	local e = 2^(endIndex-startIndex)
	return "floor((reg/"..s..")%"..e..")"
end

local function pack( value, offset, endOffset )
	local maxLen = 2^(endOffset-offset)
	offsetMul = 2^offset
	return 'ifgreaterorequal( value, ' .. maxLen .. ',NaN,' .. offsetMul .. '*' .. value .. ')'
end

-- select some bits with rest set to 0.
local function selectBits(startIndex, endIndex)
	return '('..unpack(startIndex,endIndex) .. '*' .. (2^startIndex).. ')'
end

local function get(reg)
	local main_start, main_end, exp_start, exp_end
	if reg == 'X' then
		main_start = X_START
		main_end = X_END
		exp_start = X_EXP_START
		exp_end = X_EXP_END
	else
		error( "unknown register" )
	end
	return '(' .. unpack( X_START, X_END ) ..
		'*(pow(10,' .. unpack( X_EXP_START, X_EXP_END ) .. ')))'

end

local function regFull(x)
	if x ~= 'X' then
		error( "unimplemented" )
	end

	return 'or('
		.. 'ifgreaterorequal('
			.. unpack( X_START, X_END ) .. ','
			.. ( 2^(X_END-X_START) - 1 )
		.. '),'
		.. 'ifgreater('
			.. unpack( X_EXP_START, X_EXP_END ) .. ','
			.. ( 2^(X_EXP_END-X_EXP_START) - 1 )
		.. ')'
	.. ')'

end

-- User presses a number.
-- If status 
local function pressNumber(x)
	return selectBits( ALL_START, X_EXP_START ) .. '+' ..
		selectBits( X_END, ALL_END ) .. '+' ..
		'ifzero( '
			.. regFull("X") .. ',' ..
			-- we are not full, so add something
			pack(
				unpack( X_START, X_END ) .. '*10+' .. x,
				X_START,
				X_END
			) .. '+' ..
			pack(
				unpack( X_EXP_START, X_EXP_END )
					.. '+' .. unpack( DECIMAL_START, DECIMAL_END ),
				X_EXP_START,
				X_EXP_END
			) ..
			',' ..
			-- Overflow, do not allow additional digits
			selectBits( X_EXP_START, X_END )
		.. ')'
end

function p.getWidget( frame )
	local buttons = {
		{ "MC", "" },
		{ "MR", "" },
		{ "M−", "" },
		{ "M+", "" },
		{ "C", "" },
		{ "±", "" },
		{ "%", "" },
		{ "√", "" },
		{ "7", pressNumber(7) },
		{ "8", pressNumber(8) },
		{ "9", pressNumber(9) },
		{ "÷", "" },
		{ "4", pressNumber(4) },
		{ "5", pressNumber(5) },
		{ "6", pressNumber(6) },
		{ "×", "" },
		{ "1", pressNumber(1) },
		{ "2", pressNumber(2) },
		{ "3", pressNumber(3) },
		{ "−", "" },
		{ "0", pressNumber(0) },
		{ ".", "" },
		{ "=", "" },
		{ "+", "" },
	}

	local calc = '<div class="calculator" style="display:grid;grid-template-columns:repeat(4, 1fr);grid-gap:5px;min-width:256px;width:20ch;border:thin solid gray"><div style="grid-column:1/5;">{{Calculator codex text|default=0|id=ans|style=text-align:right|readonly=1}}</div>'
	for i, v in ipairs(buttons) do
		local type = v[1] == 'C' and 'destructive' or 'default'
		-- maybe make "=" be progressive instead of default. Perhaps weight = primary?
		calc = calc .. '{{calculator button|type='.. type ..'|weight=normal|for=reg|formula=' .. v[2] .. '|contents='.. v[1] .. '}}'
	end
	calc = calc .. '</div>'
	return frame:preprocess(calc)
end

return p