Module:Sandbox/Gnosygnu

--This Module is for Lua experimentation. No other pages refer to it.

local p = {}

function p.siteinfo(frame)

return mw.site.siteName .. '--' .. '(' .. mw.site.server .. ')'

end

function p.getEntity(frame)

local v = mw.wikibase.getEntity();

if v == nil then

return 'nil'

else

return v.id

end

end

--[[

Test with:

  • Debug console
  • Code:

:: =p.ustring_find({args={[1]='abab', [2]='b', [3]=3, [4]='true'}})

  • Returns -> 4 (2nd b, not 1st b)
  • https://en.wikipedia.org/wiki/Project:Sandbox
  • Code:

:: {{#invoke:Sandbox/Gnosygnu|ustring_find|abab|b|3|true}}

  • Returns -> 4 (2nd b, not 1st b)

--]]

function p.ustring_find(frame)

local args = frame.args;

local rslt = {mw.ustring.find(args[1], args[2], tonumber(args[3]), args[4] == 'true')};

local rv = '';

local rslt_len = #rslt;

for i=1,rslt_len do

if i ~= 1 then

rv = rv .. ';'

end

rv = rv .. rslt[i]

end

return rv;

end

function p.ustring_gsub(frame)

local args = frame.args;

local rslt = {mw.ustring.gsub(args[1], args[2], args[3])};

local rv = '';

local rslt_len = #rslt;

for i=1,rslt_len do

if i ~= 1 then

rv = rv .. ';'

end

rv = rv .. rslt[i]

end

return rv;

end

function p.test18(frame)

return mw.language.fetchLanguageName(frame.args[1], 'en');

-- return mw.language.fetchLanguageName(frame.args[1]);

end

--=p.resolvePropertyId({args={[1]='P123'}})

function p.resolvePropertyId(frame)

return mw.wikibase.resolvePropertyId(frame.args[1]);

end

function p.getParentTitle(frame)

return frame:getParent():getTitle()

end

function p.getCurrentTitle(frame)

return frame:getTitle()

end

function p.wikiencode( s )

local ret = string.gsub( s, '([^a-zA-Z0-9!$()*,./:;@~_-])', function ( c )

if c == ' ' then

return '_'

else

return string.len(c) .. ":" .. string.byte( c, 1, 1 ) .. ";"

-- return string.format( '%%%02X', string.byte( c, 1, 1 ) )

end

end );

return ret

end

function p.test19()

return string.gsub("aæb", '([^a-zA-Z0-9!$()*,./:;@~_-])', "x")

end

function p.test_726()

-- mw.ustring.gsub("仰", "()[仰]+()", function(pos1, pos2)

mw.ustring.gsub("a", "()a()", function(pos1, pos2)

mw.log(pos1);

mw.log(pos2);

end)

end

function p.formatNum()

local lang = mw.getContentLanguage();

local formatted_num = lang:formatNum(7.68e-000005);

return formatted_num;

end

function p.test_732()

mw.log('should not print mw.log');

mw.ustring.gsub("a", "(a)(%f[%s])", function(pos1, pos2)

mw.log('match', "`" .. pos1 .. "`", "`" .. pos2 .. "`");

end)

end

function p.test_732_frontier()

return mw.ustring.gsub("a b c", "%f[%W]", "()")

end

function p.test_726_anypos()

mw.ustring.gsub("abcd", "a(bc)d", function(arg1)

mw.log('basic', arg1, arg2);

end)

mw.ustring.gsub("abcd", "a()bcd", function(arg1)

mw.log('empty', arg1, arg2);

end)

end

function p.test_727(id2)

-- local id2 = frame.args[1];

--P1362's format regex: \p{Lu}[\p{L}\d_',\.\-\(\)\*/–]{3,59} (e.g. Abcd)

-- local class = "[%a%d_',%.%-%(%)%*/–]"

local class = "[%a]"

-- local regex = "^%u"..string.rep(class, 3)..string.rep(class.."?", 56).."$"

local regex = "^%u"..string.rep(class, 3)..string.rep(class.."?", 56).."$"

-- dbg(id2);

-- dbg(regex)

if not mw.ustring.match( id2, regex ) then

return "match:n"

end

return 'match:y'

end

function p.test_775(s)

return tonumber(s)

end

--

-- This module implements Template:Percentage

--

local math_module = require( "Module:Math" )

local precision = math_module._precision

local sortkey = require( "Module:Sortkey" )

local function rnd(num, digits)

-- This function implements {{rnd}}

mw.log('a11', num, digits);

local x = p._precision_format(tostring(num), digits)

mw.log('a12', x);

return x;

end

local function oom(num)

-- This function implements {{order of magnitude}}

return math_module._order(tostring(num))

end

function _nonscinote(num)

-- This function undoes scientific notation

mw.log(num);

if mw.ustring.match(num or '', '^%s*(%d)%.(%d+)]*>×10([%-−]*)(%d)%s*$') then

local a,b,c,d = mw.ustring.match(num or '', '^%s*(%d)%.(%d+)]*>×10([%-−]*)(%d)%s*$')

mw.log('x', a, b, c, d);

d = tonumber(d) or 1

if c ~= '' then

return '0.' .. mw.ustring.rep('0', d - 1) .. a .. b

else

return a .. mw.ustring.sub(b .. mw.ustring.rep('0', d ), 1, d)

end

end

return num

end

local function fmtout(num,snote)

if snote then

return _nonscinote(num)

else

return num

end

end

function p._percentage(n1, n2, prec, suffix, pad, sigfig, sn)

local pct = 100*n1/n2

skey = ''

-- prec = math.floor(prec)

if sigfig ~= '' then

if pct ~= 0 then

mw.log('a1', rnd(pct, tonumber(sigfig) - oom(pct) - 1));

mw.log('b1', fmtout(rnd(pct, tonumber(sigfig) - oom(pct) - 1), sn));

return skey .. fmtout(rnd(pct, tonumber(sigfig) - oom(pct) - 1), sn) .. suffix

else

mw.log('b');

return skey .. fmtout(rnd(pct, tonumber(sigfig) - 3), sn) .. suffix

end

end

if pad ~= '' then

mw.log('c');

return skey .. fmtout(rnd(pct, prec), sn) .. suffix

end

prec = (prec < 0) and 0 or prec

if pct ~= 0 then

pct = ((pct < 0) and -1 or 1)*math.floor(math.abs(pct * 10^prec) + 0.5) / 10^prec

end

mw.log('d');

return skey .. fmtout(pct, sn) .. suffix

end

local yesno, getArgs -- lazily initialized

local wrap = {} -- Holds wrapper functions that process arguments from #invoke. These act as intemediary between functions meant for #invoke and functions meant for Lua.

--[[

Helper functions used to avoid redundant code.

]]

local function err(msg)

-- Generates wikitext error messages.

return mw.ustring.format('Formatting error: %s', msg)

end

local function unpackNumberArgs(args)

-- Returns an unpacked list of arguments specified with numerical keys.

local ret = {}

for k, v in pairs(args) do

if type(k) == 'number' then

table.insert(ret, v)

end

end

return unpack(ret)

end

local function makeArgArray(...)

-- Makes an array of arguments from a list of arguments that might include nils.

local args = {...} -- Table of arguments. It might contain nils or non-number values, so we can't use ipairs.

local nums = {} -- Stores the numbers of valid numerical arguments.

local ret = {}

for k, v in pairs(args) do

v = p._cleanNumber(v)

if v then

nums[#nums + 1] = k

args[k] = v

end

end

table.sort(nums)

for i, num in ipairs(nums) do

ret[#ret + 1] = args[num]

end

return ret

end

local function fold(func, ...)

-- Use a function on all supplied arguments, and return the result. The function must accept two numbers as parameters,

-- and must return a number as an output. This number is then supplied as input to the next function call.

local vals = makeArgArray(...)

local count = #vals -- The number of valid arguments

if count == 0 then return

-- Exit if we have no valid args, otherwise removing the first arg would cause an error.

nil, 0

end

local ret = table.remove(vals, 1)

for _, val in ipairs(vals) do

ret = func(ret, val)

end

return ret, count

end

--[[

Fold arguments by selectively choosing values (func should return when to choose the current "dominant" value).

]]

local function binary_fold(func, ...)

local value = fold((function(a, b) if func(a, b) then return a else return b end end), ...)

return value

end

--[[

random

Generate a random number

Usage:

{{#invoke: Math | random }}

{{#invoke: Math | random | maximum value }}

{{#invoke: Math | random | minimum value | maximum value }}

]]

function wrap.random(args)

local first = p._cleanNumber(args[1])

local second = p._cleanNumber(args[2])

return p._random(first, second)

end

function p._random(first, second)

math.randomseed(mw.site.stats.edits + mw.site.stats.pages + os.time() + math.floor(os.clock() * 1000000000))

-- math.random will throw an error if given an explicit nil parameter, so we need to use if statements to check the params.

if first and second then

if first <= second then -- math.random doesn't allow the first number to be greater than the second.

return math.random(first, second)

end

elseif first then

return math.random(first)

else

return math.random()

end

end

--[[

order

Determine order of magnitude of a number

Usage:

{{#invoke: Math | order | value }}

]]

function wrap.order(args)

local input_string = (args[1] or args.x or '0');

local input_number = p._cleanNumber(input_string);

if input_number == nil then

return err('order of magnitude input appears non-numeric')

else

return p._order(input_number)

end

end

function p._order(x)

if x == 0 then return 0 end

return math.floor(math.log10(math.abs(x)))

end

--[[

precision

Detemines the precision of a number using the string representation

Usage:

{{ #invoke: Math | precision | value }}

]]

function wrap.precision(args)

local input_string = (args[1] or args.x or '0');

local trap_fraction = args.check_fraction;

local input_number;

if not yesno then

yesno = require('Module:Yesno')

end

if yesno(trap_fraction, true) then -- Returns true for all input except nil, false, "no", "n", "0" and a few others. See Module:Yesno.

local pos = string.find(input_string, '/', 1, true);

if pos ~= nil then

if string.find(input_string, '/', pos + 1, true) == nil then

local denominator = string.sub(input_string, pos+1, -1);

local denom_value = tonumber(denominator);

if denom_value ~= nil then

return math.log10(denom_value);

end

end

end

end

input_number, input_string = p._cleanNumber(input_string);

if input_string == nil then

return err('precision input appears non-numeric')

else

return p._precision(input_string)

end

end

function p._precision(x)

if type(x) == 'number' then

x = tostring(x)

end

x = string.upper(x)

local decimal = x:find('%.')

local exponent_pos = x:find('E')

local result = 0;

if exponent_pos ~= nil then

local exponent = string.sub(x, exponent_pos + 1)

x = string.sub(x, 1, exponent_pos - 1)

result = result - tonumber(exponent)

end

if decimal ~= nil then

result = result + string.len(x) - decimal

return result

end

local pos = string.len(x);

while x:byte(pos) == string.byte('0') do

pos = pos - 1

result = result - 1

if pos <= 0 then

return 0

end

end

return result

end

--[[

max

Finds the maximum argument

Usage:

{{#invoke:Math| max | value1 | value2 | ... }}

Note, any values that do not evaluate to numbers are ignored.

]]

function wrap.max(args)

return p._max(unpackNumberArgs(args))

end

function p._max(...)

local max_value = binary_fold((function(a, b) return a > b end), ...)

if max_value then

return max_value

end

end

--[[

median

Find the median of set of numbers

Usage:

{{#invoke:Math | median | number1 | number2 | ...}}

OR

{{#invoke:Math | median }}

]]

function wrap.median(args)

return p._median(unpackNumberArgs(args))

end

function p._median(...)

local vals = makeArgArray(...)

local count = #vals

table.sort(vals)

if count == 0 then

return 0

end

if p._mod(count, 2) == 0 then

return (vals[count/2] + vals[count/2+1])/2

else

return vals[math.ceil(count/2)]

end

end

--[[

min

Finds the minimum argument

Usage:

{{#invoke:Math| min | value1 | value2 | ... }}

OR

{{#invoke:Math| min }}

When used with no arguments, it takes its input from the parent

frame. Note, any values that do not evaluate to numbers are ignored.

]]

function wrap.min(args)

return p._min(unpackNumberArgs(args))

end

function p._min(...)

local min_value = binary_fold((function(a, b) return a < b end), ...)

if min_value then

return min_value

end

end

--[[

sum

Finds the sum

Usage:

{{#invoke:Math| sum | value1 | value2 | ... }}

OR

{{#invoke:Math| sum }}

Note, any values that do not evaluate to numbers are ignored.

]]

function wrap.sum(args)

return p._sum(unpackNumberArgs(args))

end

function p._sum(...)

local sums, count = fold((function(a, b) return a + b end), ...)

if not sums then

return 0

else

return sums

end

end

--[[

average

Finds the average

Usage:

{{#invoke:Math| average | value1 | value2 | ... }}

OR

{{#invoke:Math| average }}

Note, any values that do not evaluate to numbers are ignored.

]]

function wrap.average(args)

return p._average(unpackNumberArgs(args))

end

function p._average(...)

local sum, count = fold((function(a, b) return a + b end), ...)

if not sum then

return 0

else

return sum / count

end

end

--[[

round

Rounds a number to specified precision

Usage:

{{#invoke:Math | round | value | precision }}

--]]

function wrap.round(args)

local value = p._cleanNumber(args[1] or args.value or 0)

local precision = p._cleanNumber(args[2] or args.precision or 0)

if value == nil or precision == nil then

return err('round input appears non-numeric')

else

return p._round(value, precision)

end

end

function p._round(value, precision)

local rescale = math.pow(10, precision or 0);

return math.floor(value * rescale + 0.5) / rescale;

end

--[[

log10

returns the log (base 10) of a number

Usage:

{{#invoke:Math | log10 | x }}

]]

function wrap.log10(args)

return math.log10(args[1])

end

--[[

mod

Implements the modulo operator

Usage:

{{#invoke:Math | mod | x | y }}

--]]

function wrap.mod(args)

local x = p._cleanNumber(args[1])

local y = p._cleanNumber(args[2])

if not x then

return err('first argument to mod appears non-numeric')

elseif not y then

return err('second argument to mod appears non-numeric')

else

return p._mod(x, y)

end

end

function p._mod(x, y)

local ret = x % y

if not (0 <= ret and ret < y) then

ret = 0

end

return ret

end

--[[

gcd

Calculates the greatest common divisor of multiple numbers

Usage:

{{#invoke:Math | gcd | value 1 | value 2 | value 3 | ... }}

--]]

function wrap.gcd(args)

return p._gcd(unpackNumberArgs(args))

end

function p._gcd(...)

local function findGcd(a, b)

local r = b

local oldr = a

while r ~= 0 do

local quotient = math.floor(oldr / r)

oldr, r = r, oldr - quotient * r

end

if oldr < 0 then

oldr = oldr * -1

end

return oldr

end

local result, count = fold(findGcd, ...)

return result

end

--[[

precision_format

Rounds a number to the specified precision and formats according to rules

originally used for {{template:Rnd}}. Output is a string.

Usage:

{{#invoke: Math | precision_format | number | precision }}

]]

function wrap.precision_format(args)

local value_string = args[1] or 0

local precision = args[2] or 0

return p._precision_format(value_string, precision)

end

function p._precision_format(value_string, precision)

-- For access to Mediawiki built-in formatter.

local lang = mw.getContentLanguage();

mw.log('e');

local value

value, value_string = p._cleanNumber(value_string)

precision = p._cleanNumber(precision)

-- Check for non-numeric input

if value == nil or precision == nil then

return err('invalid input when rounding')

end

local current_precision = p._precision(value)

local order = p._order(value)

-- Due to round-off effects it is neccesary to limit the returned precision under

-- some circumstances because the terminal digits will be inaccurately reported.

if order + precision >= 14 then

if order + p._precision(value_string) >= 14 then

precision = 13 - order;

end

end

-- If rounding off, truncate extra digits

if precision < current_precision then

value = p._round(value, precision)

current_precision = p._precision(value)

end

mw.log('d10', value);

mw.log('d10a', math.abs(value));

local formatted_num = lang:formatNum(math.abs(value))

mw.log('d11', formatted_num);

local sign

-- Use proper unary minus sign rather than ASCII default

if value < 0 then

sign = '−'

else

sign = ''

end

-- Handle cases requiring scientific notation

if string.find(formatted_num, 'E', 1, true) ~= nil or math.abs(order) >= 9 then

value = value * math.pow(10, -order)

current_precision = current_precision + order

precision = precision + order

formatted_num = lang:formatNum(math.abs(value))

mw.log('d', formatted_num);

else

order = 0;

end

formatted_num = sign .. formatted_num

-- Pad with zeros, if needed

if current_precision < precision then

local padding

if current_precision <= 0 then

if precision > 0 then

local zero_sep = lang:formatNum(1.1)

formatted_num = formatted_num .. zero_sep:sub(2,2)

padding = precision

if padding > 20 then

padding = 20

end

formatted_num = formatted_num .. string.rep('0', padding)

end

else

padding = precision - current_precision

if padding > 20 then

padding = 20

end

formatted_num = formatted_num .. string.rep('0', padding)

end

end

-- Add exponential notation, if necessary.

if order ~= 0 then

-- Use proper unary minus sign rather than ASCII default

if order < 0 then

order = '−' .. lang:formatNum(math.abs(order))

else

order = lang:formatNum(order)

end

formatted_num = formatted_num .. '×10' .. order .. ''

end

return formatted_num

end

--[[

Helper function that interprets the input numerically. If the

input does not appear to be a number, attempts evaluating it as

a parser functions expression.

]]

function p._cleanNumber(number_string)

if type(number_string) == 'number' then

-- We were passed a number, so we don't need to do any processing.

return number_string, tostring(number_string)

elseif type(number_string) ~= 'string' or not number_string:find('%S') then

-- We were passed a non-string or a blank string, so exit.

return nil, nil;

end

-- Attempt basic conversion

local number = tonumber(number_string)

-- If failed, attempt to evaluate input as an expression

if number == nil then

local success, result = pcall(mw.ext.ParserFunctions.expr, number_string)

if success then

number = tonumber(result)

number_string = tostring(number)

else

number = nil

number_string = nil

end

else

number_string = number_string:match("^%s*(.-)%s*$") -- String is valid but may contain padding, clean it.

number_string = number_string:match("^%+(.*)$") or number_string -- Trim any leading + signs.

if number_string:find('^%-?0[xX]') then

-- Number is using 0xnnn notation to indicate base 16; use the number that Lua detected instead.

number_string = tostring(number)

end

end

return number, number_string

end

--[[

Wrapper function that does basic argument processing. This ensures that all functions from #invoke can use either the current

frame or the parent frame, and it also trims whitespace for all arguments and removes blank arguments.

]]

local mt = { __index = function(t, k)

return function(frame)

if not getArgs then

getArgs = require('Module:Arguments').getArgs

end

return wrap[k](getArgs(frame)) -- Argument processing is left to Module:Arguments. Whitespace is trimmed and blank arguments are removed.

end

end }

return p