Module:Number systems

--

-- Functions to convert to and from various number systems. -- -- @TODO: -- * Custom symbols? -- * Roman numerals? -- * Other crazy systems??

local p = {} local checkType = require("libraryUtil").checkType

-- used for error messages local moduleName = "Dev:Number systems"

-- Helper functions

-- makes a sequence "mirrored", such that s[s[i]] == i local function mirror(t) for i, v in ipairs(t) do       t[v] = i    end

return t end

-- integer division with remainder local function intDiv(dividend, divisor) local quotient = math.floor(dividend / divisor) local remainder = dividend % divisor

return quotient, remainder end

-- Symbol lookup tables

local regularSymbols = mirror{ -- numbers [0-9] "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",

-- capital letters [A-Z] "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",

-- lowercase letters [a-z] "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",

-- other symbols [+/] "+", "/" }

local bijectiveSymbols = mirror{ -- capital letters [A-Z] "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",

-- lowercase letters [a-z] "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",

-- numbers [0-9] "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",

-- other symbols [+/] "+", "/" }

-- Returns a string representation of a given number in a given base.

function p.number2base(n, base) local functionName = moduleName .. ".number2base"

checkType(functionName, 1, n, "number") checkType(functionName, 2, base, "number")

if n % 1 ~= 0 or n < 0 then error("bad argument #1 to '" .. functionName .. "' (number out of range)") elseif base #regularSymbols then error("bad argument #2 to '" .. functionName .. "' (base out of range)") end

local nextNumber = n   local digits = ""

repeat local quotient, remainder = intDiv(nextNumber, base)

nextNumber = quotient digits = regularSymbols[remainder + 1] .. digits until nextNumber == 0

return digits end

-- Parses a string representation of a given number in a given base.

function p.base2number(str, base) local functionName = moduleName .. ".base2number"

checkType(functionName, 1, str, "string") checkType(functionName, 2, base, "number")

if str:find("[^" .. table.concat(regularSymbols, "") .. "]") then error("bad argument #1 to '" .. functionName .. "' (unexpected character)") elseif base #regularSymbols then error("bad argument #2 to '" .. functionName .. "' (base out of range)") end

local n = #str local sum = 0

for digit in mw.text.gsplit(str, "") do       local value = regularSymbols[digit] - 1

n = n - 1 sum = sum + value * base ^ n   end

return sum end

-- Returns a string representation of a given number in a given bijective (i.e. -- zeroless) base. -- -- @see 

function p.number2bijective(n, base) local functionName = moduleName .. ".number2bijective"

checkType(functionName, 1, n, "number") checkType(functionName, 2, base, "number")

if n % 1 ~= 0 or n < 1 then error("bad argument #1 to '" .. functionName .. "' (number out of range)") elseif base #bijectiveSymbols then error("bad argument #2 to '" .. functionName .. "' (base out of range)") end

local nextNumber = n   local digits = ""

repeat local quotient, remainder = intDiv(nextNumber - 1, base)

nextNumber = quotient digits = bijectiveSymbols[remainder + 1] .. digits until nextNumber == 0

return digits end

-- Parses a string representation of a given number in a given bijective (i.e. -- zeroless) base. -- -- @see 

function p.bijective2number(str, base) local functionName = moduleName .. ".bijective2number"

checkType(functionName, 1, str, "string") checkType(functionName, 2, base, "number")

if str:find("[^" .. table.concat(bijectiveSymbols, "") .. "]") then error("bad argument #1 to '" .. functionName .. "' (unexpected character)") elseif base #bijectiveSymbols then error("bad argument #2 to '" .. functionName .. "' (base out of range)") end

local n = #str local sum = 0

for digit in mw.text.gsplit(str, "") do       local value = bijectiveSymbols[digit]

n = n - 1 sum = sum + value * base ^ n   end

return sum end

return p -- -- (Add categories here.)