„Modul:Expr“ – Versionsunterschied
Erscheinungsbild
[gesichtete Version] | [gesichtete Version] |
Inhalt gelöscht Inhalt hinzugefügt
KKeine Bearbeitungszusammenfassung |
KKeine Bearbeitungszusammenfassung Markierung: Manuelle Zurücksetzung |
||
(11 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
local Expr = { suite = "Expr", |
|||
--[=[ 2016-05-26 |
|||
serial = "2022-09-12", |
|||
item = 54991461 } |
|||
--[==[ |
|||
Expr |
Expr |
||
* average |
|||
* base62 |
|||
* crossTotal |
|||
* decimal2minsec |
|||
* figure |
|||
* max |
* max |
||
* min |
* min |
||
* minsec2decimal |
|||
* modulo |
|||
* percent |
|||
* Ramanujan |
|||
* random |
|||
* sum |
|||
* TemplateAverage |
|||
* TemplateBooland |
|||
* TemplateBoolor |
|||
* TemplateMax |
* TemplateMax |
||
* TemplateMin |
* TemplateMin |
||
* TemplateSum |
|||
* booland |
|||
]=] |
]==] |
||
local Failsafe = Expr |
|||
Expr.messagePrefix = "lua-module-Expr-" |
|||
Expr.l10nDef = {} |
|||
l10nDef[ "en" ] = { |
Expr.l10nDef[ "en" ] = { |
||
ErrorExpr = "Error in mathematical expression, function#parameter" |
ErrorExpr = "Error in mathematical expression, function#parameter" |
||
} |
} |
||
l10nDef[ "de" ] = { |
Expr.l10nDef[ "de" ] = { |
||
ErrorExpr = "Fehler in mathematischem Ausdruck, Funktion#Parameter" |
ErrorExpr = "Fehler in mathematischem Ausdruck, Funktion#Parameter" |
||
} |
} |
||
Expr.breakFigures = { [","] = ",", |
|||
["."] = "%.", |
|||
["'"] = "'", |
|||
["',"] = "[',]", |
|||
["'."] = "['%.]", |
|||
["U+20"] = " ", |
|||
["U+A0"] = mw.ustring.char( 0xA0 ), |
|||
["U+202F"] = mw.ustring.char( 0x202F ), |
|||
["%s"] = mw.ustring.char( 91, 0x20, |
|||
0xA0, |
|||
0x2009, |
|||
0x202F, 93 ), |
|||
[".%s"] = mw.ustring.char( 91, 0x20, |
|||
0x2E, |
|||
0xA0, |
|||
0x2009, |
|||
0x202F, 93 ), |
|||
["'%s"] = mw.ustring.char( 91, 0x20, |
|||
0x27, |
|||
0xA0, |
|||
0x2009, |
|||
0x202F, 93 ), |
|||
["'.%s"] = mw.ustring.char( 91, 0x20, |
|||
0x27, |
|||
0x2E, |
|||
0xA0, |
|||
0x2009, |
|||
0x202F, 93 ) |
|||
} |
|||
Expr.signMinus = mw.ustring.char( 0x2212 ) |
|||
Zeile 23: | Zeile 72: | ||
-- Retrieve localized message string in content language |
-- Retrieve localized message string in content language |
||
-- Precondition: |
-- Precondition: |
||
-- say -- string |
-- say -- string, message ID |
||
-- Postcondition: |
-- Postcondition: |
||
-- Return some message string |
-- Return some message string |
||
-- Uses: |
-- Uses: |
||
-- > messagePrefix |
-- > Expr.messagePrefix |
||
-- > l10nDef |
-- > Expr.l10nDef |
||
-- mw.language.getContentLanguage() |
-- mw.language.getContentLanguage() |
||
-- mw.message.new() |
-- mw.message.new() |
||
local |
local slang = mw.language.getContentLanguage():getCode() |
||
local |
local msg = mw.message.new( Expr.messagePrefix .. say ) |
||
local r = false |
local r = false |
||
if |
if msg:isBlank() then |
||
local |
local def = Expr.l10nDef[ slang ] |
||
if not |
if not def then |
||
def = Expr.l10nDef[ "en" ] |
|||
end |
end |
||
r = |
r = def[ say ] |
||
else |
else |
||
msg:inLanguage( slang ) |
|||
r = |
r = msg:plain() |
||
end |
end |
||
if not r then |
if not r then |
||
Zeile 50: | Zeile 99: | ||
end -- factory() |
end -- factory() |
||
local function eval( source, frame ) |
|||
-- Evaluate expression |
|||
local function faculty( analyze ) |
|||
-- Test for boolean interpretation |
|||
-- Precondition: |
-- Precondition: |
||
-- |
-- analyze -- string or boolean or nil |
||
-- |
-- Postcondition: |
||
-- returns boolean |
|||
return frame:callParserFunction( "#expr", source ) |
|||
local s = type( analyze ) |
|||
end -- eval() |
|||
local r |
|||
if s == "string" then |
|||
r = mw.text.trim( analyze ) |
|||
if r == "" or r == "0" or r == "-" then |
|||
r = false |
|||
elseif r == "1" then |
|||
r = true |
|||
else |
|||
r = r:lower() |
|||
if r == "y" or |
|||
r == "yes" or |
|||
r == "true" or |
|||
r == "on" then |
|||
r = true |
|||
elseif r == "n" or |
|||
r == "no" or |
|||
r == "false" or |
|||
r == "off" then |
|||
r = false |
|||
else |
|||
if r == "falsch" or r == "nein" then |
|||
r = false |
|||
-- error( "faculty@Expr", 0 ) |
|||
else |
|||
r = true |
|||
end |
|||
end |
|||
end |
|||
elseif s == "boolean" then |
|||
r = analyze |
|||
elseif s == "nil" then |
|||
r = false |
|||
else |
|||
r = true |
|||
end |
|||
return r |
|||
end -- faculty() |
|||
local function expr( source |
local function expr( source, show ) |
||
-- Safe evaluation of presumable expression |
-- Safe evaluation of presumable expression |
||
-- Precondition: |
-- Precondition: |
||
-- source -- string |
-- source -- string, mathematical expression |
||
-- |
-- show -- string, details about source |
||
-- show -- string; details about source |
|||
-- Postcondition: |
-- Postcondition: |
||
-- throws error, if expression failed |
-- throws error, if expression failed |
||
Zeile 70: | Zeile 158: | ||
-- Uses: |
-- Uses: |
||
-- factory() |
-- factory() |
||
local lucky, r = pcall( |
local lucky, r = pcall( mw.ext.ParserFunctions.expr, source ) |
||
local n = tonumber( r, 10 ) |
local n = tonumber( r, 10 ) |
||
if not lucky |
if not ( lucky and n ) then |
||
r = r .. " " .. factory( "ErrorExpr" ) |
r = r .. " " .. factory( "ErrorExpr" ) |
||
.. " ''" .. show .. "'' (" .. source .. ")" |
.. " ''" .. show .. "'' (" .. source .. ")" |
||
Zeile 83: | Zeile 171: | ||
local function base62( value ) |
|||
local function ellipse( a, epsilon ) |
|||
-- Convert number from and to base62 encoding |
|||
-- Circumference of an ellipse. Approximation by Ramanujan's formula. |
|||
-- Precondition: |
|||
-- |
-- Returns the approximation and a locical value (true, if the data is well) |
||
epsilon = tonumber(epsilon) or false; |
|||
-- number: to base62 |
|||
a = tonumber(a) or false; |
|||
-- string: base62 to number |
|||
if not epsilon then return 0, false; end |
|||
-- Lua limitation at 10^53; larger numbers are less precise |
|||
if not a then return 0, false; end |
|||
-- Postcondition: |
|||
if epsilon < 0 or epsilon > 1 then return 0, false; end |
|||
a = math.abs(a); |
|||
local state = type( value ) |
|||
local b = a * math.sqrt (1 - epsilon * epsilon); |
|||
if state == "number" then |
|||
local lambda = (a - b) / (a + b); |
|||
local circumference = math.pi * (a + b) * (1 + (3 * lambda * lambda)/(10 + math.sqrt (4 - 3 * lambda * lambda))); |
|||
if k == value and value > 0 then |
|||
if circumference then |
|||
local m |
|||
return circumference, true; |
|||
else |
|||
return 0, false; |
|||
end |
|||
end |
|||
local function logicaland( args ) |
|||
local r = "" |
|||
for k, v in pairs( args ) do |
|||
if mw.text.trim( v ) == "" then |
|||
r = "" |
r = "" |
||
break -- for k, v |
|||
else |
|||
r = "1" |
|||
if m >= 36 then |
|||
m = m + 61 |
|||
elseif m >= 11 then |
|||
m = m + 55 |
|||
else |
|||
m = m + 48 |
|||
end |
|||
r = string.char( m ) .. r |
|||
end |
|||
elseif value == 0 then |
|||
r = "0" |
|||
end |
end |
||
end -- for k, v |
|||
elseif state == "string" then |
|||
if value:match( "^%w+$" ) then |
|||
local n = #value |
|||
local k = 1 |
|||
local c |
|||
r = 0 |
|||
for i = n, 1, -1 do |
|||
c = value:byte( i, i ) |
|||
if c >= 48 and c <= 57 then |
|||
c = c - 48 |
|||
elseif c >= 65 and c <= 90 then |
|||
c = c - 55 |
|||
elseif c >= 97 and c <= 122 then |
|||
c = c - 61 |
|||
else -- How comes? |
|||
r = nil |
|||
break -- for i |
|||
end |
|||
r = r + c * k |
|||
k = k * 62 |
|||
end -- for i |
|||
end |
|||
end |
|||
return r |
return r |
||
end |
|||
end -- base62() |
|||
function logicaland(args) |
|||
local r = true; |
|||
local function logicalor( args ) |
|||
local r = "" |
|||
for k, v in pairs( args ) do |
|||
if faculty( v ) then |
|||
s = mw.text.trim(v) |
|||
r = "1" |
|||
break -- for k, v |
|||
r = r and b |
|||
end |
|||
end -- for k, v |
|||
return r |
return r |
||
end |
end |
||
function logicalor(args) |
|||
local r = false; |
|||
local k, v, s |
|||
local b |
|||
for k, v in pairs(args) do |
|||
s = mw.ustring.lower(mw.text.trim(v) or ''); |
|||
if s == '' then |
|||
b = false; |
|||
elseif s=='0' then |
|||
b = false; |
|||
elseif s=='false' then |
|||
b = false; |
|||
elseif s=='falsch' then |
|||
b = false; |
|||
elseif s=='nein' then |
|||
b = false; |
|||
else |
|||
b = true; |
|||
end |
|||
if b then |
|||
r = true; |
|||
end |
|||
end |
|||
return r |
|||
end |
|||
local function minmax( params, frame, low, lazy ) |
|||
local function minmax( params, low, lazy ) |
|||
-- Find extremum of unnamed params values |
-- Find extremum of unnamed params values |
||
-- Precondition: |
-- Precondition: |
||
-- params -- table |
-- params -- table, like args |
||
-- .minus |
-- .minus |
||
-- .zeroBlank |
-- .zeroBlank |
||
-- |
-- low -- true: minimum, false: maximum |
||
-- |
-- lazy -- true: try numeric result, false: return string |
||
-- lazy -- true: try numeric result; false: return string |
|||
-- Postcondition: |
-- Postcondition: |
||
-- throws error, if expression failed |
-- throws error, if expression failed |
||
Zeile 193: | Zeile 236: | ||
-- false if no data provided |
-- false if no data provided |
||
-- Uses: |
-- Uses: |
||
-- > Expr.signMinus |
|||
-- expr() |
-- expr() |
||
local k, v, n, scope |
|||
local light = ( params.minus ~= "-" ) |
local light = ( params.minus ~= "-" ) |
||
local luxury = ( params.minus and light ) |
local luxury = ( params.minus and light ) |
||
local c = mw.ustring.char( 8722 ) -- minus |
|||
local scan = "^%s*%-?[0-9]*%.?[0-9]*%s*$" |
|||
local r = false |
local r = false |
||
local n, scope |
|||
for k, v in pairs( params ) do |
for k, v in pairs( params ) do |
||
if type( k ) == "number" then |
if type( k ) == "number" then |
||
scope |
scope = type( v ) |
||
if scope == "string" then |
if scope == "string" then |
||
v = mw.text.trim( v ) |
|||
if v == "" then |
|||
n = false |
n = false |
||
else |
else |
||
if mw.ustring. |
if mw.ustring.sub( v, 1, 1 ) == Expr.signMinus then |
||
luxury = light |
luxury = light |
||
v = mw.ustring. |
v = "-" .. mw.ustring.sub( v, 2 ) |
||
end |
end |
||
n = Expr.figure( v, ".", true ) |
|||
if not n then |
|||
if low then |
if low then |
||
scope = "min()#" |
scope = "min()#" |
||
Zeile 218: | Zeile 262: | ||
end |
end |
||
scope = scope .. tostring( k ) |
scope = scope .. tostring( k ) |
||
expr( v, scope ) |
|||
end |
end |
||
n = tonumber( v ) |
|||
end |
end |
||
elseif scope == "number" then |
elseif scope == "number" then |
||
Zeile 246: | Zeile 289: | ||
if r then |
if r then |
||
if luxury and r < 0 then |
if luxury and r < 0 then |
||
r = |
r = Expr.signMinus .. tostring( -1 * r ) |
||
elseif not lazy then |
elseif not lazy then |
||
if r == 0 then |
if r == 0 then |
||
Zeile 261: | Zeile 304: | ||
return r |
return r |
||
end -- minmax() |
end -- minmax() |
||
Expr.average = function ( array, ask ) |
|||
-- Calculate average |
|||
-- Precondition: |
|||
-- array -- sequence table, with strings and/or numbers |
|||
-- ask -- string or not, with figure format |
|||
-- Postcondition: |
|||
-- returns number, at least 0 |
|||
local r, n = Expr.sum( array, ask ) |
|||
if n > 1 then |
|||
r = r / n |
|||
end |
|||
return r |
|||
end -- Expr.average() |
|||
Expr.base62 = function ( adjust ) |
|||
-- Convert number from and to base62 encoding |
|||
-- Precondition: |
|||
-- adjust -- number or ASCII string to be converted |
|||
-- number: to base62 |
|||
-- string: base62 to number |
|||
-- Lua limitation at 10^53; larger numbers are less precise |
|||
-- Postcondition: |
|||
-- returns string, or number, or false |
|||
local r = false |
|||
local state = type( adjust ) |
|||
if state == "number" then |
|||
local k = math.floor( adjust ) |
|||
if k == adjust and adjust > 0 then |
|||
local m |
|||
r = "" |
|||
while k > 0 do |
|||
m = k % 62 |
|||
k = ( k - m ) / 62 |
|||
if m >= 36 then |
|||
m = m + 61 |
|||
elseif m >= 11 then |
|||
m = m + 55 |
|||
else |
|||
m = m + 48 |
|||
end |
|||
r = string.char( m ) .. r |
|||
end |
|||
elseif adjust == 0 then |
|||
r = "0" |
|||
end |
|||
elseif state == "string" then |
|||
if adjust:match( "^%w+$" ) then |
|||
local n = #adjust |
|||
local k = 1 |
|||
local c |
|||
r = 0 |
|||
for i = n, 1, -1 do |
|||
c = adjust:byte( i, i ) |
|||
if c >= 48 and c <= 57 then |
|||
c = c - 48 |
|||
elseif c >= 65 and c <= 90 then |
|||
c = c - 55 |
|||
elseif c >= 97 and c <= 122 then |
|||
c = c - 61 |
|||
else -- How comes? |
|||
r = nil |
|||
break -- for i |
|||
end |
|||
r = r + c * k |
|||
k = k * 62 |
|||
end -- for i |
|||
end |
|||
end |
|||
return r |
|||
end -- Expr.base62() |
|||
Expr.crossTotal = function ( amount ) |
|||
-- Calculate sum of digits in integer number |
|||
-- Precondition: |
|||
-- amount -- string or number, with integer |
|||
-- Postcondition: |
|||
-- returns number, at least 0 |
|||
local r = 0 |
|||
local s = Expr.figure( amount ) |
|||
if s then |
|||
if s < 0 then |
|||
s = -1 * s |
|||
end |
|||
s = tostring( math.floor( s ) ) |
|||
if s:match( "^%d+$" ) then |
|||
for i = 1, #s do |
|||
r = r + tonumber( s:sub( i, i ) ) |
|||
end -- for i |
|||
end |
|||
end |
|||
return r |
|||
end -- Expr.crossTotal() |
|||
Expr.decimal2minsec = function ( amount, align, ask, allow, frame ) |
|||
-- Format coordinate value in degree, minutes, seconds |
|||
-- Precondition: |
|||
-- amount -- string or number, with decimal coordinate |
|||
-- align -- string, number, nil, with number of decimal digits |
|||
-- ask -- string or not, with figure format |
|||
-- allow -- true, if unformatted result |
|||
-- frame -- object, if available |
|||
-- Postcondition: |
|||
-- returns mw.html -- with formatted data, or |
|||
-- string -- with "0" if any problem |
|||
-- Uses: |
|||
-- > Expr.signMinus |
|||
local r = Expr.figure( amount, ask ) |
|||
if r then |
|||
local d = tonumber( align ) |
|||
local e = mw.html.create( "span" ) |
|||
local kd, km, low, sd |
|||
if r < 0 then |
|||
low = true |
|||
r = -1 * r |
|||
end |
|||
kd = math.floor( r ) |
|||
r = ( r - kd ) * 60 |
|||
if kd > 360 then |
|||
kd = kd - math.floor( kd / 360 ) * 360 |
|||
end |
|||
sd = tostring( kd ) |
|||
if low then |
|||
sd = Expr.signMinus .. sd |
|||
end |
|||
km = math.floor( r ) |
|||
r = ( r - km ) * 60 |
|||
if d and d >= 1 and d < 10 then |
|||
local n = math.floor( r ) |
|||
if r == n then |
|||
r = tostring( n ) |
|||
else |
|||
local s = string.format( "%%.%df", math.floor( d ) ) |
|||
r = tonumber( string.format( s, r ) ) |
|||
if allow then |
|||
r = tostring( r ) |
|||
else |
|||
if not Expr.frame then |
|||
Expr.frame = frame or mw.getCurrentFrame() |
|||
end |
|||
r = Expr.frame:callParserFunction( "formatnum", r ) |
|||
end |
|||
end |
|||
else |
|||
r = tostring( math.floor( r + 0.5 ) ) |
|||
end |
|||
if not Expr.degminsec then |
|||
Expr.degminsec = string.format( "%%s%s %%d%s %%s%s", |
|||
mw.ustring.char( 0xB0 ), |
|||
mw.ustring.char( 0x2032 ), |
|||
mw.ustring.char( 0x2033 ) ) |
|||
end |
|||
r = string.format( Expr.degminsec, sd, km, r ) |
|||
e:css( "white-space", "nowrap" ) |
|||
:addClass( "coordinate-deg-min-sec" ) |
|||
e:wikitext( r ) |
|||
r = e |
|||
else |
|||
r = "0" |
|||
end |
|||
return r |
|||
end -- Expr.decimal2minsec() |
|||
Expr.figure = function ( amount, ask, advance, area ) |
|||
-- Convert number from various formats |
|||
-- Precondition: |
|||
-- amount -- string (or number), with number |
|||
-- ask -- string, with permitted formatting, defaults to "." |
|||
-- advance -- true, if expressions permitted |
|||
-- area -- string, or not, with permitted set |
|||
-- Postcondition: |
|||
-- returns number, or false |
|||
-- Uses: |
|||
-- > Expr.signMinus |
|||
-- > Expr.breakFigures |
|||
-- 2022-08-08 |
|||
local seek = type( amount ) |
|||
local r |
|||
if seek == "string" then |
|||
local scan = mw.text.trim( amount ) |
|||
seek = ask or "." |
|||
if scan == "" then |
|||
seek = false |
|||
elseif advance and |
|||
not tonumber( scan ) and |
|||
scan:find( "[+%-*/)]", 2 ) then |
|||
local lucky |
|||
lucky, r = pcall( mw.ext.ParserFunctions.expr, scan ) |
|||
if lucky then |
|||
seek = false |
|||
r = tonumber( r ) |
|||
else |
|||
r = false |
|||
end |
|||
end |
|||
if type( seek ) == "string" then |
|||
if scan:find( "[Ee]" ) then |
|||
scan = scan:match( "^[+%-]?([%.%d]+)[Ee][+%-]?%d+$" ) |
|||
if scan and |
|||
( scan:match( "^%.%d+$" ) or |
|||
scan:match( "^%d+%.?%d*$" ) ) then |
|||
r = tonumber( amount ) |
|||
end |
|||
else |
|||
local low, split |
|||
seek = mw.text.trim( seek ) |
|||
if seek == "" then |
|||
seek = "." |
|||
end |
|||
split = seek:sub( -1 ) |
|||
seek = seek:sub( 1, -2 ) |
|||
if seek:sub( 1, 1 ) == "-" then |
|||
seek = seek:sub( 2 ) |
|||
if mw.ustring.sub( scan, 1, 1 ) |
|||
== Expr.signMinus then |
|||
low = true |
|||
scan = mw.ustring.sub( scan, 2 ) |
|||
end |
|||
end |
|||
if not low then |
|||
if scan:sub( 1, 1 ) == "-" then |
|||
low = true |
|||
scan = scan:sub( 2 ) |
|||
elseif scan:sub( 1, 1 ) == "+" then |
|||
scan = scan:sub( 2 ) |
|||
end |
|||
end |
|||
if ( split == "." or split == "," ) and |
|||
not seek:find( split, 1, true ) then |
|||
local i = scan:find( split, 1, true ) |
|||
if i then |
|||
split = scan:sub( i + 1 ) |
|||
if split == "" then |
|||
split = false |
|||
end |
|||
if i > 1 then |
|||
r = scan:sub( 1, i - 1 ) |
|||
elseif split then |
|||
r = "" |
|||
else |
|||
r = false |
|||
end |
|||
else |
|||
split = false |
|||
r = scan |
|||
end |
|||
if r then |
|||
seek = Expr.breakFigures[ seek ] |
|||
if seek then |
|||
local f = function ( a ) |
|||
local rf = a |
|||
if rf:find( "&.+;" ) then |
|||
rf = mw.text.decode( rf, |
|||
true ) |
|||
end |
|||
rf = mw.ustring.gsub( rf, |
|||
seek, |
|||
"%1%2" ) |
|||
return rf |
|||
end |
|||
seek = "(%d)" .. seek .. "(%d)" |
|||
if r ~= "" then |
|||
r = f( r ) |
|||
end |
|||
if split then |
|||
split = f( split ) |
|||
end |
|||
end |
|||
if split and |
|||
not split:match( "^%d+$" ) then |
|||
r = false |
|||
end |
|||
if r and |
|||
not r:match( "^%d+$" ) then |
|||
r = false |
|||
end |
|||
if r and split then |
|||
r = string.format( "%s.%s", r, split ) |
|||
end |
|||
end |
|||
end |
|||
if r then |
|||
r = tonumber( r ) |
|||
if low then |
|||
r = -1 * r |
|||
end |
|||
end |
|||
end |
|||
end |
|||
elseif seek == "number" then |
|||
r = amount |
|||
end |
|||
if r and type( area ) == "string" then |
|||
local set = mw.text.trim( area ) |
|||
if set == "" then |
|||
elseif set == "N" or set == "Z+" then |
|||
if r < 0 or |
|||
r ~= math.floor( r ) then |
|||
r = false |
|||
end |
|||
elseif set == "Z" then |
|||
if r ~= math.floor( r ) then |
|||
r = false |
|||
end |
|||
elseif set == "Z-" then |
|||
if r > 0 or |
|||
r ~= math.floor( r ) then |
|||
r = false |
|||
end |
|||
elseif set == "R+" then |
|||
if r < 0 then |
|||
r = false |
|||
end |
|||
elseif set == "R-" then |
|||
if r > 0 then |
|||
r = false |
|||
end |
|||
end |
|||
end |
|||
return r or false |
|||
end -- Expr.figure() |
|||
Expr.minsec2decimal = function ( aDeg, aMin, aSec, alter, ask ) |
|||
-- Convert coordinate value from degree, minutes, seconds, letter |
|||
-- Precondition: |
|||
-- aDeg -- string or number, with degree |
|||
-- aMin -- string or number, with minutes |
|||
-- aSec -- string or number, with seconds |
|||
-- alter -- string or boolean, true|S|W, negative sign |
|||
-- ask -- string, with permitted formatting, defaults to "." |
|||
local r = Expr.figure( aDeg, ask ) |
|||
if r then |
|||
local qm = Expr.figure( aMin, ask ) |
|||
local qt = Expr.figure( aSec, ask ) |
|||
local m = 360 |
|||
local less |
|||
if qm then |
|||
r = r + qm * 0.0166666666666667 |
|||
if qt then |
|||
r = r + qt * 0.0002777777777777778 |
|||
end |
|||
elseif qt then |
|||
r = false |
|||
end |
|||
if alter then |
|||
local s = type( alter ) |
|||
if s == "string" then |
|||
s = mw.text.trim( alter ):upper() |
|||
if s == "S" or s == "W" then |
|||
less = true |
|||
end |
|||
if s == "N" or s == "S" then |
|||
m = 180 |
|||
end |
|||
elseif s == "boolean" then |
|||
less = alter |
|||
end |
|||
end |
|||
if r then |
|||
if r < 0 then |
|||
r = -1 * r |
|||
less = true |
|||
end |
|||
if r > 0 then |
|||
r = r - math.floor( r / m ) * m |
|||
end |
|||
if less then |
|||
r = -1 * r |
|||
end |
|||
end |
|||
end |
|||
return r or 0 |
|||
end -- Expr.minsec2decimal() |
|||
Expr.modulo = function ( amount, adjust, ask ) |
|||
-- Retrieve modulo remainder |
|||
-- Precondition: |
|||
-- amount -- string or number, with total amount (dividend) |
|||
-- adjust -- string or number, with modulo divisor, non-zero |
|||
-- ask -- string or not, with figure format |
|||
-- Postcondition: |
|||
-- returns number -- with modulo remainder |
|||
-- 0 -- if numbers are not available |
|||
local qt = Expr.figure( amount, ask ) |
|||
local qm = Expr.figure( adjust, ask ) |
|||
local r |
|||
if qt and qm and qm ~= 0 then |
|||
r = qt - math.floor( qt / qm ) * qm |
|||
else |
|||
r = 0 |
|||
end |
|||
return r |
|||
end -- Expr.modulo() |
|||
Expr.percent = function ( amount, all, align, after, ask, allow, frame ) |
|||
-- Retrieve percentage |
|||
-- Precondition: |
|||
-- amount -- string or number, with partial value |
|||
-- all -- string or number, with base value (100%) |
|||
-- align -- string, number, nil, with number of decimal digits |
|||
-- after -- true, if trailing zeroes shall be kept |
|||
-- ask -- string or not, with figure format |
|||
-- allow -- true, if unformatted result |
|||
-- frame -- object, if available |
|||
-- Postcondition: |
|||
-- returns string -- with formatted percentage, terminated by % |
|||
-- 0 -- if numbers are not available |
|||
local qp = Expr.figure( amount, ask ) |
|||
local qb = Expr.figure( all, ask ) |
|||
local r |
|||
if qp and qb and qb ~= 0 then |
|||
local d = tonumber( align ) |
|||
r = qp * 100 / qb |
|||
if d and d >= 1 and d < 10 then |
|||
local s = string.format( "%%.%df", math.floor( d ) ) |
|||
s = string.format( s, r ) |
|||
if after then |
|||
r = s |
|||
else |
|||
local n = math.floor( r ) |
|||
if tonumber( s ) == n then |
|||
r = tostring( n ) |
|||
else |
|||
r = s:gsub( "^(-?%d+%.%d*[1-9])0+$", "%1" ) |
|||
end |
|||
end |
|||
else |
|||
r = tostring( math.floor( r + 0.5 ) ) |
|||
end |
|||
if not allow then |
|||
if not Expr.frame then |
|||
Expr.frame = frame or mw.getCurrentFrame() |
|||
end |
|||
r = Expr.frame:callParserFunction( "formatnum", r ) |
|||
end |
|||
r = r .. " %" |
|||
else |
|||
r = 0 |
|||
end |
|||
return r |
|||
end -- Expr.percent() |
|||
Expr.sum = function ( array, ask ) |
|||
-- Calculate sum |
|||
-- Precondition: |
|||
-- array -- sequence table, with strings and/or numbers |
|||
-- ask -- string or not, with figure format |
|||
-- Postcondition: |
|||
-- returns -- 1, number, with sum, at least 0 |
|||
-- -- 2, number, of summands, at least 0 |
|||
local r1 = 0 |
|||
local r2 = 0 |
|||
if type( array ) == "table" then |
|||
for k, v in pairs( array ) do |
|||
v = Expr.figure( v, ask, true ) |
|||
if v then |
|||
r1 = r1 + v |
|||
r2 = r2 + 1 |
|||
end |
|||
end -- for k, v |
|||
end |
|||
return r1, r2 |
|||
end -- Expr.sum() |
|||
Failsafe.failsafe = function ( atleast ) |
|||
-- Retrieve versioning and check for compliance |
|||
-- Precondition: |
|||
-- atleast -- string, with required version |
|||
-- or wikidata|item|~|@ or false |
|||
-- Postcondition: |
|||
-- returns string -- with queried version/item, also if problem |
|||
-- false -- if appropriate |
|||
-- 2020-08-17 |
|||
local since = atleast |
|||
local last = ( since == "~" ) |
|||
local linked = ( since == "@" ) |
|||
local link = ( since == "item" ) |
|||
local r |
|||
if last or link or linked or since == "wikidata" then |
|||
local item = Failsafe.item |
|||
since = false |
|||
if type( item ) == "number" and item > 0 then |
|||
local suited = string.format( "Q%d", item ) |
|||
if link then |
|||
r = suited |
|||
else |
|||
local entity = mw.wikibase.getEntity( suited ) |
|||
if type( entity ) == "table" then |
|||
local seek = Failsafe.serialProperty or "P348" |
|||
local vsn = entity:formatPropertyValues( seek ) |
|||
if type( vsn ) == "table" and |
|||
type( vsn.value ) == "string" and |
|||
vsn.value ~= "" then |
|||
if last and vsn.value == Failsafe.serial then |
|||
r = false |
|||
elseif linked then |
|||
if mw.title.getCurrentTitle().prefixedText |
|||
== mw.wikibase.getSitelink( suited ) then |
|||
r = false |
|||
else |
|||
r = suited |
|||
end |
|||
else |
|||
r = vsn.value |
|||
end |
|||
end |
|||
end |
|||
end |
|||
end |
|||
end |
|||
if type( r ) == "nil" then |
|||
if not since or since <= Failsafe.serial then |
|||
r = Failsafe.serial |
|||
else |
|||
r = false |
|||
end |
|||
end |
|||
return r |
|||
end -- Failsafe.failsafe() |
|||
Zeile 266: | Zeile 848: | ||
-- Export |
-- Export |
||
local p = {} |
local p = {} |
||
function p.average( frame ) |
|||
local d = { } |
|||
for k, v in pairs( frame.args ) do |
|||
k = tostring( k ) |
|||
if k:match( "^%d+$" ) then |
|||
table.insert( d, v ) |
|||
end |
|||
end -- for k, v |
|||
return Expr.average( d, frame.args.parse ) |
|||
end |
|||
function p.base62( frame ) |
function p.base62( frame ) |
||
Zeile 281: | Zeile 874: | ||
s2 = false |
s2 = false |
||
end |
end |
||
r = base62( s ) |
r = Expr.base62( s ) |
||
if r and not s2 then |
if r and not s2 then |
||
r = |
r = string.format( "%17d", r ) |
||
end |
end |
||
end |
end |
||
return r or "" |
return r or "" |
||
end |
end |
||
function p.crossTotal( frame ) |
|||
return Expr.crossTotal( frame.args[ 1 ] ) |
|||
end |
|||
function p.decimal2minsec( frame ) |
|||
return tostring( Expr.decimal2minsec( frame.args[ 1 ], |
|||
frame.args[ 2 ], |
|||
frame.args.parse, |
|||
faculty( frame.args.low ), |
|||
frame ) ) |
|||
end |
|||
p.figure = function ( frame ) |
|||
local r = Expr.figure( frame.args[ 1 ], |
|||
frame.args.parse, |
|||
faculty( frame.args.expr ), |
|||
frame.args.set ) |
|||
if r then |
|||
r = tostring( r ) |
|||
else |
|||
r = "" |
|||
end |
|||
return r |
|||
end -- p.figure |
|||
function p.max( frame ) |
function p.max( frame ) |
||
local lucky, r = pcall( minmax, frame.args |
local lucky, r = pcall( minmax, frame.args, false, false ) |
||
return r or "" |
return r or "" |
||
end |
end |
||
function p.min( frame ) |
function p.min( frame ) |
||
local lucky, r = pcall( minmax, frame.args |
local lucky, r = pcall( minmax, frame.args, true, false ) |
||
return r or "" |
return r or "" |
||
end |
end |
||
function p. |
function p.minsec2decimal( frame ) |
||
return |
return Expr.minsec2decimal( frame.args[ 1 ], |
||
frame.args[ 2 ], |
|||
frame.args[ 3 ], |
|||
frame.args[ 4 ], |
|||
frame.args.parse ) |
|||
end |
end |
||
function p. |
function p.modulo( frame ) |
||
return |
return Expr.modulo( frame.args[ 1 ], |
||
frame.args[ 2 ], |
|||
frame.args[ 3 ] ) |
|||
end |
end |
||
function p. |
function p.percent( frame ) |
||
local base = frame.args[ 2 ] |
|||
local pars |
|||
return logicaland(fr.args) |
|||
if base then |
|||
pars = frame.args |
|||
else |
|||
pars = frame:getParent().args |
|||
base = pars[ 2 ] |
|||
end |
|||
return Expr.percent( pars[ 1 ], |
|||
base, |
|||
pars[ 3 ], |
|||
faculty( pars[ 4 ] ), |
|||
pars.parse, |
|||
faculty( pars.low ), |
|||
frame ) |
|||
end |
end |
||
function p. |
function p.Ramanujan( frame ) |
||
local semiaxis = frame.args[1] or 0; |
|||
local eps = frame.args[2] or 0; |
|||
return logicalor(fr.args) |
|||
local value, isOk = ellipse( semiaxis,eps ); |
|||
if isOk then |
|||
return tostring(value); |
|||
else |
|||
return -1 -- An error code |
|||
end |
|||
end |
end |
||
function p. |
function p.random( frame ) |
||
local |
local n = Expr.figure( frame.args[ 1 ] ) |
||
if |
if n and n >= 2 then |
||
n = math.floor( n ) |
|||
else |
|||
local low = ( f == "min" ) |
|||
n = 100 |
|||
lucky, r = pcall( minmax, a, frame, low, true ) |
|||
elseif f == "base62" then |
|||
r = base62( a ) |
|||
end |
end |
||
math.randomseed( math.floor( 100000 * os.clock() ) ) |
|||
return math.random( 0, n - 1 ) |
|||
end |
|||
function p.sum( frame ) |
|||
local d = { } |
|||
local r, n |
|||
for k, v in pairs( frame.args ) do |
|||
k = tostring( k ) |
|||
if k:match( "^%d+$" ) then |
|||
table.insert( d, v ) |
|||
end |
|||
end -- for k, v |
|||
r, n = Expr.sum( d, frame.args.parse ) |
|||
return r |
return r |
||
end |
end |
||
function p.TemplateAverage( frame ) |
|||
return p.average( frame:getParent() ) |
|||
end |
|||
function p.TemplateBooland( frame ) |
|||
return logicaland( frame:getParent().args ) |
|||
end |
|||
function p.TemplateBoolor( frame ) |
|||
return logicalor( frame:getParent().args ) |
|||
end |
|||
function p.TemplateMax( frame ) |
|||
return p.max( frame:getParent() ) |
|||
end |
|||
function p.TemplateMin( frame ) |
|||
return p.min( frame:getParent() ) |
|||
end |
|||
function p.TemplateSum( frame ) |
|||
return p.sum( frame:getParent() ) |
|||
end |
|||
p.failsafe = function ( frame ) |
|||
-- Versioning interface |
|||
local s = type( frame ) |
|||
local since |
|||
if s == "table" then |
|||
since = frame.args[ 1 ] |
|||
elseif s == "string" then |
|||
since = frame |
|||
end |
|||
if since then |
|||
since = mw.text.trim( since ) |
|||
if since == "" then |
|||
since = false |
|||
end |
|||
end |
|||
return Failsafe.failsafe( since ) or "" |
|||
end -- p.failsafe |
|||
p.Expr = function () |
|||
return Expr |
|||
end -- p.Expr() |
|||
setmetatable( p, { __call = function ( func, ... ) |
|||
setmetatable( p, nil ); |
|||
return Failsafe; |
|||
end } ); |
|||
return p |
return p |
Aktuelle Version vom 21. September 2024, 02:22 Uhr
Vorlagenprogrammierung | Diskussionen | Lua | Test | Unterseiten | |||
Modul | Deutsch | English
|
Modul: | Dokumentation |
Diese Seite enthält Code in der Programmiersprache Lua. Einbindungszahl Cirrus
Dies ist die (produktive) Mutterversion eines global benutzten Lua-Moduls.
Wenn die serial-Information nicht übereinstimmt, müsste eine Kopie hiervon in das lokale Wiki geschrieben werden.
Wenn die serial-Information nicht übereinstimmt, müsste eine Kopie hiervon in das lokale Wiki geschrieben werden.
Versionsbezeichnung auf WikiData:
2022-09-12
local Expr = { suite = "Expr",
serial = "2022-09-12",
item = 54991461 }
--[==[
Expr
* average
* base62
* crossTotal
* decimal2minsec
* figure
* max
* min
* minsec2decimal
* modulo
* percent
* Ramanujan
* random
* sum
* TemplateAverage
* TemplateBooland
* TemplateBoolor
* TemplateMax
* TemplateMin
* TemplateSum
]==]
local Failsafe = Expr
Expr.messagePrefix = "lua-module-Expr-"
Expr.l10nDef = {}
Expr.l10nDef[ "en" ] = {
ErrorExpr = "Error in mathematical expression, function#parameter"
}
Expr.l10nDef[ "de" ] = {
ErrorExpr = "Fehler in mathematischem Ausdruck, Funktion#Parameter"
}
Expr.breakFigures = { [","] = ",",
["."] = "%.",
["'"] = "'",
["',"] = "[',]",
["'."] = "['%.]",
["U+20"] = " ",
["U+A0"] = mw.ustring.char( 0xA0 ),
["U+202F"] = mw.ustring.char( 0x202F ),
["%s"] = mw.ustring.char( 91, 0x20,
0xA0,
0x2009,
0x202F, 93 ),
[".%s"] = mw.ustring.char( 91, 0x20,
0x2E,
0xA0,
0x2009,
0x202F, 93 ),
["'%s"] = mw.ustring.char( 91, 0x20,
0x27,
0xA0,
0x2009,
0x202F, 93 ),
["'.%s"] = mw.ustring.char( 91, 0x20,
0x27,
0x2E,
0xA0,
0x2009,
0x202F, 93 )
}
Expr.signMinus = mw.ustring.char( 0x2212 )
local function factory( say )
-- Retrieve localized message string in content language
-- Precondition:
-- say -- string, message ID
-- Postcondition:
-- Return some message string
-- Uses:
-- > Expr.messagePrefix
-- > Expr.l10nDef
-- mw.language.getContentLanguage()
-- mw.message.new()
local slang = mw.language.getContentLanguage():getCode()
local msg = mw.message.new( Expr.messagePrefix .. say )
local r = false
if msg:isBlank() then
local def = Expr.l10nDef[ slang ]
if not def then
def = Expr.l10nDef[ "en" ]
end
r = def[ say ]
else
msg:inLanguage( slang )
r = msg:plain()
end
if not r then
r = "(((" .. say .. ")))"
end
return r
end -- factory()
local function faculty( analyze )
-- Test for boolean interpretation
-- Precondition:
-- analyze -- string or boolean or nil
-- Postcondition:
-- returns boolean
local s = type( analyze )
local r
if s == "string" then
r = mw.text.trim( analyze )
if r == "" or r == "0" or r == "-" then
r = false
elseif r == "1" then
r = true
else
r = r:lower()
if r == "y" or
r == "yes" or
r == "true" or
r == "on" then
r = true
elseif r == "n" or
r == "no" or
r == "false" or
r == "off" then
r = false
else
if r == "falsch" or r == "nein" then
r = false
-- error( "faculty@Expr", 0 )
else
r = true
end
end
end
elseif s == "boolean" then
r = analyze
elseif s == "nil" then
r = false
else
r = true
end
return r
end -- faculty()
local function expr( source, show )
-- Safe evaluation of presumable expression
-- Precondition:
-- source -- string, mathematical expression
-- show -- string, details about source
-- Postcondition:
-- throws error, if expression failed
-- returns number with resulting figure
-- Uses:
-- factory()
local lucky, r = pcall( mw.ext.ParserFunctions.expr, source )
local n = tonumber( r, 10 )
if not ( lucky and n ) then
r = r .. " " .. factory( "ErrorExpr" )
.. " ''" .. show .. "'' (" .. source .. ")"
error( r, 0 )
else
r = n
end
return r
end -- expr()
local function ellipse( a, epsilon )
-- Circumference of an ellipse. Approximation by Ramanujan's formula.
-- Returns the approximation and a locical value (true, if the data is well)
epsilon = tonumber(epsilon) or false;
a = tonumber(a) or false;
if not epsilon then return 0, false; end
if not a then return 0, false; end
if epsilon < 0 or epsilon > 1 then return 0, false; end
a = math.abs(a);
local b = a * math.sqrt (1 - epsilon * epsilon);
local lambda = (a - b) / (a + b);
local circumference = math.pi * (a + b) * (1 + (3 * lambda * lambda)/(10 + math.sqrt (4 - 3 * lambda * lambda)));
if circumference then
return circumference, true;
else
return 0, false;
end
end
local function logicaland( args )
local r = ""
for k, v in pairs( args ) do
if mw.text.trim( v ) == "" then
r = ""
break -- for k, v
else
r = "1"
end
end -- for k, v
return r
end
local function logicalor( args )
local r = ""
for k, v in pairs( args ) do
if faculty( v ) then
r = "1"
break -- for k, v
end
end -- for k, v
return r
end
local function minmax( params, low, lazy )
-- Find extremum of unnamed params values
-- Precondition:
-- params -- table, like args
-- .minus
-- .zeroBlank
-- low -- true: minimum, false: maximum
-- lazy -- true: try numeric result, false: return string
-- Postcondition:
-- throws error, if expression failed
-- returns number, or
-- string if formatting required, or
-- false if no data provided
-- Uses:
-- > Expr.signMinus
-- expr()
local light = ( params.minus ~= "-" )
local luxury = ( params.minus and light )
local r = false
local n, scope
for k, v in pairs( params ) do
if type( k ) == "number" then
scope = type( v )
if scope == "string" then
v = mw.text.trim( v )
if v == "" then
n = false
else
if mw.ustring.sub( v, 1, 1 ) == Expr.signMinus then
luxury = light
v = "-" .. mw.ustring.sub( v, 2 )
end
n = Expr.figure( v, ".", true )
if not n then
if low then
scope = "min()#"
else
scope = "max()#"
end
scope = scope .. tostring( k )
expr( v, scope )
end
end
elseif scope == "number" then
n = v
else
n = false
end
if n then
if r then
if low then
if n < r then
r = n
end
else
if n > r then
r = n
end
end
else
r = n
end
end
end
end -- for k, v
if r then
if luxury and r < 0 then
r = Expr.signMinus .. tostring( -1 * r )
elseif not lazy then
if r == 0 then
if params.zeroBlank then
r = ""
else
r = "0"
end
else
r = tostring( r )
end
end
end
return r
end -- minmax()
Expr.average = function ( array, ask )
-- Calculate average
-- Precondition:
-- array -- sequence table, with strings and/or numbers
-- ask -- string or not, with figure format
-- Postcondition:
-- returns number, at least 0
local r, n = Expr.sum( array, ask )
if n > 1 then
r = r / n
end
return r
end -- Expr.average()
Expr.base62 = function ( adjust )
-- Convert number from and to base62 encoding
-- Precondition:
-- adjust -- number or ASCII string to be converted
-- number: to base62
-- string: base62 to number
-- Lua limitation at 10^53; larger numbers are less precise
-- Postcondition:
-- returns string, or number, or false
local r = false
local state = type( adjust )
if state == "number" then
local k = math.floor( adjust )
if k == adjust and adjust > 0 then
local m
r = ""
while k > 0 do
m = k % 62
k = ( k - m ) / 62
if m >= 36 then
m = m + 61
elseif m >= 11 then
m = m + 55
else
m = m + 48
end
r = string.char( m ) .. r
end
elseif adjust == 0 then
r = "0"
end
elseif state == "string" then
if adjust:match( "^%w+$" ) then
local n = #adjust
local k = 1
local c
r = 0
for i = n, 1, -1 do
c = adjust:byte( i, i )
if c >= 48 and c <= 57 then
c = c - 48
elseif c >= 65 and c <= 90 then
c = c - 55
elseif c >= 97 and c <= 122 then
c = c - 61
else -- How comes?
r = nil
break -- for i
end
r = r + c * k
k = k * 62
end -- for i
end
end
return r
end -- Expr.base62()
Expr.crossTotal = function ( amount )
-- Calculate sum of digits in integer number
-- Precondition:
-- amount -- string or number, with integer
-- Postcondition:
-- returns number, at least 0
local r = 0
local s = Expr.figure( amount )
if s then
if s < 0 then
s = -1 * s
end
s = tostring( math.floor( s ) )
if s:match( "^%d+$" ) then
for i = 1, #s do
r = r + tonumber( s:sub( i, i ) )
end -- for i
end
end
return r
end -- Expr.crossTotal()
Expr.decimal2minsec = function ( amount, align, ask, allow, frame )
-- Format coordinate value in degree, minutes, seconds
-- Precondition:
-- amount -- string or number, with decimal coordinate
-- align -- string, number, nil, with number of decimal digits
-- ask -- string or not, with figure format
-- allow -- true, if unformatted result
-- frame -- object, if available
-- Postcondition:
-- returns mw.html -- with formatted data, or
-- string -- with "0" if any problem
-- Uses:
-- > Expr.signMinus
local r = Expr.figure( amount, ask )
if r then
local d = tonumber( align )
local e = mw.html.create( "span" )
local kd, km, low, sd
if r < 0 then
low = true
r = -1 * r
end
kd = math.floor( r )
r = ( r - kd ) * 60
if kd > 360 then
kd = kd - math.floor( kd / 360 ) * 360
end
sd = tostring( kd )
if low then
sd = Expr.signMinus .. sd
end
km = math.floor( r )
r = ( r - km ) * 60
if d and d >= 1 and d < 10 then
local n = math.floor( r )
if r == n then
r = tostring( n )
else
local s = string.format( "%%.%df", math.floor( d ) )
r = tonumber( string.format( s, r ) )
if allow then
r = tostring( r )
else
if not Expr.frame then
Expr.frame = frame or mw.getCurrentFrame()
end
r = Expr.frame:callParserFunction( "formatnum", r )
end
end
else
r = tostring( math.floor( r + 0.5 ) )
end
if not Expr.degminsec then
Expr.degminsec = string.format( "%%s%s %%d%s %%s%s",
mw.ustring.char( 0xB0 ),
mw.ustring.char( 0x2032 ),
mw.ustring.char( 0x2033 ) )
end
r = string.format( Expr.degminsec, sd, km, r )
e:css( "white-space", "nowrap" )
:addClass( "coordinate-deg-min-sec" )
e:wikitext( r )
r = e
else
r = "0"
end
return r
end -- Expr.decimal2minsec()
Expr.figure = function ( amount, ask, advance, area )
-- Convert number from various formats
-- Precondition:
-- amount -- string (or number), with number
-- ask -- string, with permitted formatting, defaults to "."
-- advance -- true, if expressions permitted
-- area -- string, or not, with permitted set
-- Postcondition:
-- returns number, or false
-- Uses:
-- > Expr.signMinus
-- > Expr.breakFigures
-- 2022-08-08
local seek = type( amount )
local r
if seek == "string" then
local scan = mw.text.trim( amount )
seek = ask or "."
if scan == "" then
seek = false
elseif advance and
not tonumber( scan ) and
scan:find( "[+%-*/)]", 2 ) then
local lucky
lucky, r = pcall( mw.ext.ParserFunctions.expr, scan )
if lucky then
seek = false
r = tonumber( r )
else
r = false
end
end
if type( seek ) == "string" then
if scan:find( "[Ee]" ) then
scan = scan:match( "^[+%-]?([%.%d]+)[Ee][+%-]?%d+$" )
if scan and
( scan:match( "^%.%d+$" ) or
scan:match( "^%d+%.?%d*$" ) ) then
r = tonumber( amount )
end
else
local low, split
seek = mw.text.trim( seek )
if seek == "" then
seek = "."
end
split = seek:sub( -1 )
seek = seek:sub( 1, -2 )
if seek:sub( 1, 1 ) == "-" then
seek = seek:sub( 2 )
if mw.ustring.sub( scan, 1, 1 )
== Expr.signMinus then
low = true
scan = mw.ustring.sub( scan, 2 )
end
end
if not low then
if scan:sub( 1, 1 ) == "-" then
low = true
scan = scan:sub( 2 )
elseif scan:sub( 1, 1 ) == "+" then
scan = scan:sub( 2 )
end
end
if ( split == "." or split == "," ) and
not seek:find( split, 1, true ) then
local i = scan:find( split, 1, true )
if i then
split = scan:sub( i + 1 )
if split == "" then
split = false
end
if i > 1 then
r = scan:sub( 1, i - 1 )
elseif split then
r = ""
else
r = false
end
else
split = false
r = scan
end
if r then
seek = Expr.breakFigures[ seek ]
if seek then
local f = function ( a )
local rf = a
if rf:find( "&.+;" ) then
rf = mw.text.decode( rf,
true )
end
rf = mw.ustring.gsub( rf,
seek,
"%1%2" )
return rf
end
seek = "(%d)" .. seek .. "(%d)"
if r ~= "" then
r = f( r )
end
if split then
split = f( split )
end
end
if split and
not split:match( "^%d+$" ) then
r = false
end
if r and
not r:match( "^%d+$" ) then
r = false
end
if r and split then
r = string.format( "%s.%s", r, split )
end
end
end
if r then
r = tonumber( r )
if low then
r = -1 * r
end
end
end
end
elseif seek == "number" then
r = amount
end
if r and type( area ) == "string" then
local set = mw.text.trim( area )
if set == "" then
elseif set == "N" or set == "Z+" then
if r < 0 or
r ~= math.floor( r ) then
r = false
end
elseif set == "Z" then
if r ~= math.floor( r ) then
r = false
end
elseif set == "Z-" then
if r > 0 or
r ~= math.floor( r ) then
r = false
end
elseif set == "R+" then
if r < 0 then
r = false
end
elseif set == "R-" then
if r > 0 then
r = false
end
end
end
return r or false
end -- Expr.figure()
Expr.minsec2decimal = function ( aDeg, aMin, aSec, alter, ask )
-- Convert coordinate value from degree, minutes, seconds, letter
-- Precondition:
-- aDeg -- string or number, with degree
-- aMin -- string or number, with minutes
-- aSec -- string or number, with seconds
-- alter -- string or boolean, true|S|W, negative sign
-- ask -- string, with permitted formatting, defaults to "."
local r = Expr.figure( aDeg, ask )
if r then
local qm = Expr.figure( aMin, ask )
local qt = Expr.figure( aSec, ask )
local m = 360
local less
if qm then
r = r + qm * 0.0166666666666667
if qt then
r = r + qt * 0.0002777777777777778
end
elseif qt then
r = false
end
if alter then
local s = type( alter )
if s == "string" then
s = mw.text.trim( alter ):upper()
if s == "S" or s == "W" then
less = true
end
if s == "N" or s == "S" then
m = 180
end
elseif s == "boolean" then
less = alter
end
end
if r then
if r < 0 then
r = -1 * r
less = true
end
if r > 0 then
r = r - math.floor( r / m ) * m
end
if less then
r = -1 * r
end
end
end
return r or 0
end -- Expr.minsec2decimal()
Expr.modulo = function ( amount, adjust, ask )
-- Retrieve modulo remainder
-- Precondition:
-- amount -- string or number, with total amount (dividend)
-- adjust -- string or number, with modulo divisor, non-zero
-- ask -- string or not, with figure format
-- Postcondition:
-- returns number -- with modulo remainder
-- 0 -- if numbers are not available
local qt = Expr.figure( amount, ask )
local qm = Expr.figure( adjust, ask )
local r
if qt and qm and qm ~= 0 then
r = qt - math.floor( qt / qm ) * qm
else
r = 0
end
return r
end -- Expr.modulo()
Expr.percent = function ( amount, all, align, after, ask, allow, frame )
-- Retrieve percentage
-- Precondition:
-- amount -- string or number, with partial value
-- all -- string or number, with base value (100%)
-- align -- string, number, nil, with number of decimal digits
-- after -- true, if trailing zeroes shall be kept
-- ask -- string or not, with figure format
-- allow -- true, if unformatted result
-- frame -- object, if available
-- Postcondition:
-- returns string -- with formatted percentage, terminated by %
-- 0 -- if numbers are not available
local qp = Expr.figure( amount, ask )
local qb = Expr.figure( all, ask )
local r
if qp and qb and qb ~= 0 then
local d = tonumber( align )
r = qp * 100 / qb
if d and d >= 1 and d < 10 then
local s = string.format( "%%.%df", math.floor( d ) )
s = string.format( s, r )
if after then
r = s
else
local n = math.floor( r )
if tonumber( s ) == n then
r = tostring( n )
else
r = s:gsub( "^(-?%d+%.%d*[1-9])0+$", "%1" )
end
end
else
r = tostring( math.floor( r + 0.5 ) )
end
if not allow then
if not Expr.frame then
Expr.frame = frame or mw.getCurrentFrame()
end
r = Expr.frame:callParserFunction( "formatnum", r )
end
r = r .. " %"
else
r = 0
end
return r
end -- Expr.percent()
Expr.sum = function ( array, ask )
-- Calculate sum
-- Precondition:
-- array -- sequence table, with strings and/or numbers
-- ask -- string or not, with figure format
-- Postcondition:
-- returns -- 1, number, with sum, at least 0
-- -- 2, number, of summands, at least 0
local r1 = 0
local r2 = 0
if type( array ) == "table" then
for k, v in pairs( array ) do
v = Expr.figure( v, ask, true )
if v then
r1 = r1 + v
r2 = r2 + 1
end
end -- for k, v
end
return r1, r2
end -- Expr.sum()
Failsafe.failsafe = function ( atleast )
-- Retrieve versioning and check for compliance
-- Precondition:
-- atleast -- string, with required version
-- or wikidata|item|~|@ or false
-- Postcondition:
-- returns string -- with queried version/item, also if problem
-- false -- if appropriate
-- 2020-08-17
local since = atleast
local last = ( since == "~" )
local linked = ( since == "@" )
local link = ( since == "item" )
local r
if last or link or linked or since == "wikidata" then
local item = Failsafe.item
since = false
if type( item ) == "number" and item > 0 then
local suited = string.format( "Q%d", item )
if link then
r = suited
else
local entity = mw.wikibase.getEntity( suited )
if type( entity ) == "table" then
local seek = Failsafe.serialProperty or "P348"
local vsn = entity:formatPropertyValues( seek )
if type( vsn ) == "table" and
type( vsn.value ) == "string" and
vsn.value ~= "" then
if last and vsn.value == Failsafe.serial then
r = false
elseif linked then
if mw.title.getCurrentTitle().prefixedText
== mw.wikibase.getSitelink( suited ) then
r = false
else
r = suited
end
else
r = vsn.value
end
end
end
end
end
end
if type( r ) == "nil" then
if not since or since <= Failsafe.serial then
r = Failsafe.serial
else
r = false
end
end
return r
end -- Failsafe.failsafe()
-- Export
local p = {}
function p.average( frame )
local d = { }
for k, v in pairs( frame.args ) do
k = tostring( k )
if k:match( "^%d+$" ) then
table.insert( d, v )
end
end -- for k, v
return Expr.average( d, frame.args.parse )
end
function p.base62( frame )
local r
local s = frame.args[ 1 ]
if s then
local s2 = frame.args[ 2 ]
if s2 then
s2 = mw.text.trim( s2 )
end
if s2 == "D2B" then
s = tonumber( s )
else
s = mw.text.trim( s )
s2 = false
end
r = Expr.base62( s )
if r and not s2 then
r = string.format( "%17d", r )
end
end
return r or ""
end
function p.crossTotal( frame )
return Expr.crossTotal( frame.args[ 1 ] )
end
function p.decimal2minsec( frame )
return tostring( Expr.decimal2minsec( frame.args[ 1 ],
frame.args[ 2 ],
frame.args.parse,
faculty( frame.args.low ),
frame ) )
end
p.figure = function ( frame )
local r = Expr.figure( frame.args[ 1 ],
frame.args.parse,
faculty( frame.args.expr ),
frame.args.set )
if r then
r = tostring( r )
else
r = ""
end
return r
end -- p.figure
function p.max( frame )
local lucky, r = pcall( minmax, frame.args, false, false )
return r or ""
end
function p.min( frame )
local lucky, r = pcall( minmax, frame.args, true, false )
return r or ""
end
function p.minsec2decimal( frame )
return Expr.minsec2decimal( frame.args[ 1 ],
frame.args[ 2 ],
frame.args[ 3 ],
frame.args[ 4 ],
frame.args.parse )
end
function p.modulo( frame )
return Expr.modulo( frame.args[ 1 ],
frame.args[ 2 ],
frame.args[ 3 ] )
end
function p.percent( frame )
local base = frame.args[ 2 ]
local pars
if base then
pars = frame.args
else
pars = frame:getParent().args
base = pars[ 2 ]
end
return Expr.percent( pars[ 1 ],
base,
pars[ 3 ],
faculty( pars[ 4 ] ),
pars.parse,
faculty( pars.low ),
frame )
end
function p.Ramanujan( frame )
local semiaxis = frame.args[1] or 0;
local eps = frame.args[2] or 0;
local value, isOk = ellipse( semiaxis,eps );
if isOk then
return tostring(value);
else
return -1 -- An error code
end
end
function p.random( frame )
local n = Expr.figure( frame.args[ 1 ] )
if n and n >= 2 then
n = math.floor( n )
else
n = 100
end
math.randomseed( math.floor( 100000 * os.clock() ) )
return math.random( 0, n - 1 )
end
function p.sum( frame )
local d = { }
local r, n
for k, v in pairs( frame.args ) do
k = tostring( k )
if k:match( "^%d+$" ) then
table.insert( d, v )
end
end -- for k, v
r, n = Expr.sum( d, frame.args.parse )
return r
end
function p.TemplateAverage( frame )
return p.average( frame:getParent() )
end
function p.TemplateBooland( frame )
return logicaland( frame:getParent().args )
end
function p.TemplateBoolor( frame )
return logicalor( frame:getParent().args )
end
function p.TemplateMax( frame )
return p.max( frame:getParent() )
end
function p.TemplateMin( frame )
return p.min( frame:getParent() )
end
function p.TemplateSum( frame )
return p.sum( frame:getParent() )
end
p.failsafe = function ( frame )
-- Versioning interface
local s = type( frame )
local since
if s == "table" then
since = frame.args[ 1 ]
elseif s == "string" then
since = frame
end
if since then
since = mw.text.trim( since )
if since == "" then
since = false
end
end
return Failsafe.failsafe( since ) or ""
end -- p.failsafe
p.Expr = function ()
return Expr
end -- p.Expr()
setmetatable( p, { __call = function ( func, ... )
setmetatable( p, nil );
return Failsafe;
end } );
return p