gemeinsam neue Wege der Erkenntnis gehen
Eine freie Initiative von Menschen bei anthrowiki.at anthrowiki.at, anthro.world anthro.world, biodyn.wiki biodyn.wiki und steiner.wiki steiner.wiki
mit online Lesekreisen, Übungsgruppen, Vorträgen ...
Wie Sie die Entwicklung von AnthroWiki durch Ihre Spende unterstützen können, erfahren Sie hier.

Use Google Translate for a raw translation of our pages into more than 100 languages.
Please note that some mistranslations can occur due to machine translation.
Alle Banner auf einen Klick

Modul:Sort/cellNum

Aus AnthroWiki

Die Dokumentation für dieses Modul kann unter Modul:Sort/cellNum/Doku erstellt werden

local Sort = { suite   = "Sort",
               sub     = "cellNum",
               serial  = "2020-11-12",
               item    = 88370026,
               globals = { Cell      = 90144855,
                           FormatNum = 15709679 } }
--[=[
Sort/cellNum
         support table cells with numerical content and number formatting
]=]
local Failsafe  = Sort
local GlobalMod = Sort



Sort.digits  = { {    0x30 },
                 {  0x0660, "Arab" },
                 {  0x06F0, "Arab" },
                 {  0x07C0, "Nkoo" },
                 {  0x0966, "Deva" },
                 {  0x09E6, "Beng" },
                 {  0x0A66, "Guru" },
                 {  0x0AE6, "Gujr" },
                 {  0x0B66, "Orya" },
                 {  0x0BE6, "Taml" },
                 {  0x0C66, "Telu" },
                 {  0x0CE6, "Knda" },
                 {  0x0D66, "Mlym" },
                 {  0x0DE6, "Sinh" },
                 {  0x0E50, "Thai" },
                 {  0x0ED0, "Laoo" },
                 {  0x0F20, "Tibt" },
                 {  0x102E, "Copt" },
                 {  0x1040, "Mymr" },
                 {  0x1369, "Ethi" },
                 {  0x17E0, "Khmr" },
                 {  0x1810, "Mong" },
                 {  0x1946, "Limb" },
                 {  0x19D0, "Talu" },
                 {  0x1A80, "Lana" },
                 {  0x1A90, "Lana" },
                 {  0x1B50, "Bali" },
                 {  0x1BB0, "Sund" },
                 {  0x1C40, "Lepc" },
                 {  0x1C50, "Olck" },
                 {  0xA620, "Vaii" },
                 {  0xA8D0, "Saur" },
                 {  0xA900, "Kali" },
                 {  0xA9D0, "Java" },
               --{  0xA9F0, "" },        MYANMAR TAI LAING
                 {  0xAA50, "Cham" },
                 {  0xABF0, "Mtei" },
                 {  0xFF10 },         -- FULLWIDTH
                 { 0x1104A, "Osma" },
                 { 0x110D3, "Rohg" },
               --{ 0x110E6, "" },        RUMI
                 { 0x11106, "Brah" },
                 { 0x1110F, "Sora" },
                 { 0x11113, "Cakm" },
                 { 0x1111D, "Shrd" },
                 { 0x1112F, "Sind" },
                 { 0x11145, "Newa" },
                 { 0x1114D, "Tirh" },
                 { 0x11165, "Modi" },
                 { 0x1116C, "Takr" },
                 { 0x11173, "Ahom" },
                 { 0x1118E, "Wara" },
                 { 0x111C5, "Bhks" },
                 { 0x111D5, "Gonm" },
                 { 0x111DA, "Gong" },
                 { 0x116A6, "Mroo" },
                 { 0x116B5, "Hmng" },
                 { 0x116E8, "Medf" },
                 { 0x11E14, "Hmnp" },
                 { 0x11E2F, "Wcho" },
                 { 0x11E8C, "Mend" },
                 { 0x11E95, "Adlm" },
                 { 0x1D7F6 },        -- MATHEMATICAL MONOSPACE
                 { 0xE0030 }         -- Tags
               }
Sort.heading = { [0x2D]   = 45,   -- -
                 [0x2212] = 45,   -- -
                 [0x2B]   = 43,   -- +
                 [0xFF0B] = 43,   -- +
                 [0xFF0D] = 45    -- -
               }
Sort.mpz     = -0.5
Sort.prefix  = { [0x003C] = -5,
                 [0x003E] =  5,
                 [0x00B1] =  true,
                 [0x2248] =  false,
                 [0x2264] = -2,
                 [0x2265] =  2
               }
Sort.supreme = mw.ustring.char( 8734 )    -- infinit



local foreignModule = function ( access, advanced, append, alt, alert )
    -- Fetch global module
    -- Precondition:
    --     access    -- string, with name of base module
    --     advanced  -- true, for require(); else mw.loadData()
    --     append    -- string, with subpage part, if any; or false
    --     alt       -- number, of wikidata item of root; or false
    --     alert     -- true, for throwing error on data problem
    -- Postcondition:
    --     Returns whatever, probably table
    -- 2020-01-01
    local storage = access
    local finer   = function ()
                        if append then
                            storage = string.format( "%s/%s",
                                                     storage,
                                                     append )
                        end
                    end
    local fun, lucky, r, suited
    if advanced then
        fun = require
    else
        fun = mw.loadData
    end
    GlobalMod.globalModules = GlobalMod.globalModules or { }
    suited = GlobalMod.globalModules[ access ]
    if not suited then
        finer()
        lucky, r = pcall( fun,  "Module:" .. storage )
    end
    if not lucky then
        if not suited  and
           type( alt ) == "number"  and
           alt > 0 then
            suited = string.format( "Q%d", alt )
            suited = mw.wikibase.getSitelink( suited )
            GlobalMod.globalModules[ access ] = suited or true
        end
        if type( suited ) == "string" then
            storage = suited
            finer()
            lucky, r = pcall( fun, storage )
        end
        if not lucky and alert then
            error( "Missing or invalid page: " .. storage )
        end
    end
    return r
end -- foreignModule()



local fetch = function ( access, advanced, append )
    -- Fetch global library
    -- Precondition:
    --     access    -- string|false, with name of base module
    --     advanced  -- true, for require(); else mw.loadData()
    --     append    -- string, with subpage part, if any; or false
    local store, sub, suite
    if access then
        suite = access
        store = access
        sub   = append
    else
        suite = Sort.suite
        if append then
            sub   = append:lower()
            store = append
        else
            store = "Sorter"
        end
    end
    if type( Sort[ store ] ) == "nil" then
        local bib = foreignModule( suite,
                                   advanced,
                                   sub,
                                   Sort.globals[ store ],
                                   not access )
        if bib  and  type( bib[ suite ] ) == "function" then
            Sort[ store ] = bib[ suite ]()
        elseif advanced then
            error( tostring( bib ) )
        else
            Sort[ store ] = bib
        end
    end
    return Sort[ store ]
end -- fetch()



local factory = function ( ask )
    -- Ensure config data
    -- Precondition:
    --     assign    -- string|nil, particular query
    -- Postcondition:
    --     config data available
    --     Returns elements for "*", 10, 1000, fractpart
    local r
    if not ask then
        Sort.minus = Sort.minus  or  mw.ustring.char( 0x2212 )
    elseif ask == "sep" then
        if not Sort.sepDec then
            local seek = "%d(%p?)(%d?%d?456)(%p)7"
            local s
            Sort.contLang = Sort.contLang  or
                            mw.language.getContentLanguage()
            s             = Sort.contLang:formatNum( 123456.7 )
            Sort.sepGroup, s, Sort.sepDec = mw.ustring.match( s, seek )
            Sort.sepDec = Sort.sepDec or "."
            Sort.keyDec = mw.ustring.codepoint( Sort.sepDec, 1, 1 )
            if s then
                Sort.nGroup = #s
            end
            if Sort.sepDec == "." then
                Sort.seekDec = "%%."
            else
                Sort.seekDec = Sort.sepDec
            end
            if Sort.sepGroup  and  Sort.sepGroup ~= "" then
                if Sort.sepGroup == "." then
                    Sort.seekGroup = "%."
                else
                    Sort.seekGroup = Sort.sepGroup
                end
                Sort.separated = string.format( "%s%%d%%d%%d%s",
                                                Sort.seekGroup,
                                                Sort.seekGroup )
            end
        end
    elseif ask == "dec" then
        if not Sort.spanDec then
            if Sort.sepDec == "." then
                Sort.spanDec = "."
            else
                local e = mw.html.create( "span" )
                                 :addClass( "numericFormat-dec" )
                                 :node( mw.html.create( "span" )
                                               :wikitext( "." ) )
                Sort.spanDec = tostring( e )
            end
        end
    elseif ask == "minus" then
        if not Sort.spanMinus then
            local e = mw.html.create( "span" )
                             :addClass( "numericFormat-minus" )
                                 :node( mw.html.create( "span" )
                                               :wikitext( "-" ) )
            Sort.spanMinus = tostring( e )
        end
    elseif ask == 1000 then
        r = mw.html.create( "span" )
                   :addClass( "numericFormat-1000" )
    elseif ask == "fractpart" then
        r = mw.html.create( "span" )
                   :addClass( "numericFormat-fractpart" )
    elseif ask == "*" then
        r = mw.html.create( "span" )
                   :addClass( "numericFormat-multiply" )
    elseif ask == 10 then
        r = mw.html.create( "span" )
                   :addClass( "numericFormat-10" )
        --    ::before { content: "10"; }
    end
    return r
end -- factory()



local feeder = function ( access )
    -- Retrieve first TemplateStyles transclusion
    -- Precondition:
    --     access   -- string, TemplateStyles ID
    local ts = Sort[ access ]
    local r
    if ts  and  not  ts.loaded then
        local s = type( ts.origin )
        local src
        ts.loaded = true
        if s == "string" then
            src = ts.origin
        elseif s == "table" then
            src = ts.origin.prefixedText
        end
        if src then
            Sort.frame = Sort.frame or mw.getCurrentFrame()
            r = Sort.frame:extensionTag( "templatestyles",
                                         nil,
                                         { src = src } )
        end
    end
    return r or ""
end -- feeder()



local fine = function ( about )
    -- Equip number with styled special characters
    -- Precondition:
    --     about    -- table, parameters of base number
    --                 .show   -- string, digits
    --                 .low    -- boolean, heading minus
    --                 .sign   -- string, heading sign
    --                 .sub    -- string, decimal fraction
    --                 .long   -- boolean, grouping possible
    -- Postcondition:
    --     Returns updated entire presentation
    local r
    if about.long then
        factory( "sep" )
        local n = mw.ustring.len( about.show )
        local k = n - Sort.nGroup + 1
        local i, m, e, s
        r = mw.ustring.sub( about.show, k )
        n = k - Sort.nGroup
        for j = n,  -1,  - Sort.nGroup do
            if j > 0 then
                i = j
            else
                i = 1
            end
            m = j + Sort.nGroup - 1
            s = mw.ustring.sub( about.show, i, m )
            e = factory( 1000 ):wikitext( s )
            r = tostring( e ) .. r
        end --  for j
    end
    r = r or about.show or about.scream
    if about.low then
        factory( "minus" )
        r = Sort.spanMinus .. r
    elseif about.sign then
        r = about.sign .. r
    end
    if about.sub then
        local s = about.sub
        factory( "sep" )
        factory( "dec" )
        r = r .. Sort.spanDec
        if Sort.nGroup then
            local n = mw.ustring.len( s )
            if n  >  Sort.nGroup + 2 then
                local k = 0
                local m = n - Sort.nGroup
                local e, sg
                for j = 1, m, Sort.nGroup do
                    sg = mw.ustring.sub( about.sub,
                                         j,
                                         j + Sort.nGroup - 1 )
                    e  = factory( "fractpart" ):wikitext( sg )
                    r  = r .. tostring( e )
                    k  = k + Sort.nGroup
                end --  for j
                s = s:sub( k + 1,  n )
            end
        end
        r = r .. s
    end
    return feeder( "cssNum" ) .. r
end -- fine()



local fined = function ( all, assign )
    -- Append styled number
    -- Precondition:
    --     all       -- string, formatted entire presentation
    --     assign    -- table, exponent
    --                  .show   -- string, digits
    --                  .low    -- boolean, < 0
    -- Postcondition:
    --     Returns
    --         1. string, updated entire presentation
    --         2. string, styled number
    local r1 = all
    local r2 = assign.show
    if assign.low then
        if Sort.cssNum then
            factory( "minus" )
            r2 = Sort.spanMinus .. r2
            r1 = feeder( "cssNum" ) .. r1
        else
            factory()
            r2 = Sort.minus .. r2
        end
    end
    return r1, r2
end -- fined()



local finest = function ( args, ahead )
    -- Append styled decimal power
    -- Precondition:
    --     args     -- table, parameters
    --                 .exp    -- table, exponent
    --     ahead    -- string, formatted presentation
    -- Postcondition:
    --     Returns expanded presentation
    local r = string.format( "%s%s%s",
                             feeder( "cssNum" ),
                             feeder( "cssNumExp" ),
                             ahead )
    local em = factory( "*" )
    local ep = factory( 10 )
    local s
    r, s = fined( r, args.exp )
    em:wikitext( "e" )
    ep:node( mw.html.create( "sup" )
                    :wikitext( s ) )
    s = Sort.Cell.feature( args, "color" )  or  "#000000"
    em:css( "background-color", s )
    r = string.format( "%s%s%s",
                       r,
                       tostring( em ),
                       tostring( ep ) )
    return r
end -- finest()



local flat = function ( assign, after, adjust )
    -- Parse decimal number
    -- Precondition:
    --     assign    -- string, number to be parsed
    --     after     -- boolean, decimal separator expected
    --     adjust    -- number|boolean|nil, cheat sort figure
    -- Postcondition:
    --     Returns table, with analysis
    --             .long      -- >= 1000  etc.
    --             .low       -- < 0
    --             .sign      -- minus, plus
    --             .show      -- leading digits, scripting
    --             .sub       -- decimal fragment digits, scripting
    --             .sort      -- signed ASCII sort text
    --             .script    -- script code
    --             .scream    -- error text
    local r = { }
    local s = assign
    local k, init
    local face = function ()
                     local j
                     if init == 45 then
                         j = 8722
                     else
                         j = init
                     end
                     return mw.ustring.char( j )
                 end -- face()
    factory( "sep" )
    if s:find( "&", 1, true ) then
        s = mw.text.decode( s )
                   :gsub( "&thinsp;", mw.ustring.char( 0x2009 ) )
    end
    s = mw.ustring.gsub( s, "%s", "" )
    k = mw.ustring.codepoint( s, 1, 1 )
    if k then
        init = Sort.heading[ k ]
        if init then
            if mw.ustring.len( s ) > 1 then
                s = mw.ustring.sub( s, 2 )
                k = mw.ustring.codepoint( s, 1, 1 )
                r.low = ( init == 45 )
            else
                k = 0x30
                s = "0"
            end
        end
    else
        k = 0x30
        s = "0"
    end
    if after then
        local m = mw.ustring.find( s, Sort.sepGroup, 1, true )
        local dig, j0, j9, n
        if m  and
           Sort.separated  and
           mw.ustring.match( s, Sort.separated ) then
            s = mw.ustring.gsub( s, Sort.seekGroup, "" )
        end
        for i = 1, #Sort.digits do
            dig = Sort.digits[ i ]
            j0  = dig[ 1 ]
            j9  = j0 + 9
            if k >= j0  and  k <= j9 then
                r.script = dig[ 2 ]
                n        = mw.ustring.len( s )
                break    -- for i
            elseif k < j0 then
                break    -- for i
            end
        end -- for i
        if n then
            m = 0
            for i = 1, n do
                if k == j0 then
                    m = i
                    if i < n then
                        k = mw.ustring.codepoint( s,  i + 1,  i + 1 )
                    end
                else
                    break    -- for i
                end
            end -- for i
            if m > 0 then
                s = mw.ustring.sub( s,  m + 1 )
                n = n - m
            end
            m = 0
            for i = 1, n do
                if k == Sort.keyDec  or  k == 46 then
                    k = false
                    break    -- for i
                elseif k >= j0  and  k <= j9 then
                    r.sort = string.format( "%s%c",
                                            r.sort or "",
                                            k - j0 + 48 )
                    if i < n then
                        k = mw.ustring.codepoint( s,  i + 1,  i + 1 )
                    end
                    m = i
                end
            end -- for i
            if m > 0 then
                r.long = ( m > Sort.nGroup )
                r.sort = r.sort or "0"
                r.show = mw.ustring.sub( s, 1, m )
                if not k then
                    m = m + 1
                    s = mw.ustring.sub( s,  m + 1 )
                end
                n = n - m
            else
                r.show = mw.ustring.char( j0 )
                r.sort = "0"
                if not k then
                    s = mw.ustring.sub( s, 2 )
                    n = n - 1
                end
            end
            if r.low  and  r.sort ~= "0" then
                r.sort = "-" .. r.sort
            end
            if n > 0 then
                if k then
                    r.scream = s
                else
                    r.sort = r.sort .. Sort.sepDec
                    k      = mw.ustring.codepoint( s, 1, 1 )
                    for i = 1, n do
                        if k  and  k >= j0  and  k <= j9 then
                            r.sort = string.format( "%s%c",
                                                    r.sort,
                                                    k - j0 + 48 )
                            if i < n then
                                k = mw.ustring.codepoint( s,
                                                          i + 1,
                                                          i + 1 )
                            end
                        else
                            k = false
                        end
                    end -- for i
                    if k then
                        r.sub = s
                    else
                        r.scream = s
                        r.show   = false
                    end
                end
            end
            if adjust then
                if adjust == true then
                    -- plus/minus
                    r.sort = "0"
                elseif r.sort then
                    k = tonumber( r.sort )
                    if k then
                        local m = k * 0.0000000001
                        if m < 0 then
                             m = -m
                        end
                        k = k + m * adjust
                        r.sort = tostring( k )
                    end
                end
            end
        else
            r.scream = assign
        end
        if init  and  not r.scream then
            r.sign = face()
        end
    else
        k = tonumber( s )
        if k  and  k >= 0 then
            k = math.floor( k )
            r.show = tostring( k )
            r.sort = r.show
            if init then
                r.sign = face()
                if init == 45 then
                    r.sort = "-" .. r.sort
                end
            end
        else
            r.scream = assign
        end
    end
    return r
end -- flat()



local fore = function ( args )
    -- Create and merge sort attribute
    -- Precondition:
    --     args      -- table, parameters
    --                       .n      -- table, for base number
    --                                  .sort    -- string|nil
    --                       .exp    -- table|nil, for exponent
    --     amount    -- number, for base
    -- Postcondition:
    --     attributes extended
    local s = args.n.sort or "0"
    if args.exp  and  args.exp.sort then
        s = string.format( "%sE%s", s, args.exp.sort )
    end
    Sort.Cell.faced( args, s )
end -- fore()



local format = function ( args )
    -- Format visible number
    -- Precondition:
    --     args    -- table, parameters
    --                .pad       -- number|false, for padding
    --                .pre       -- string|false, for prefix
    --                .n         -- table, for base
    --                              .low       -- boolean, < 0
    --                              .sign      -- minus, plus
    --                              .show      -- leading digits
    --                              .sub       -- decimal fragment digits
    --                              .script    -- script code
    --                .suffix    -- string|false, extending base
    --                .exp       -- table|false, for exponent
    --                .post      -- string|false, for postfix
    --                .round     -- number|false, for rounding
    --                .cell      -- boolean, enfoce sort value
    -- Postcondition:
    --     Returns string
    local r = args.n.show or args.n.scream
    local e, move, s, shift
    if args.pad  and  args.pad < 0 then
        move = args.pad + mw.ustring.len( args.n.show )
        if args.n.sign then
            move = move + 1
        end
        if move < 0 then
            move = move + 1
        end
        if args.pre and move then
            move = move + mw.ustring.len( args.pre ) + 1
        end
    end
    if move then
        if move < 0 then
            if not Sort.shift then
                if Sort.Cell.following() then
                    Sort.shift = "left"
                else
                    Sort.shift = "right"
                end
                Sort.shift = "padding-" .. Sort.shift
            end
            Sort.Cell.feature( args,
                               Sort.shift,
                               string.format( "%.2fem",
                                              Sort.mpz * move ) )
        end
    elseif args.pad then
        move = args.pad
        if args.n.sub then
            move = move - mw.ustring.len( args.n.sub ) - 1
            if args.suffix then
                move = move - mw.ustring.len( args.suffix )
            end
        else
            move = move + 0.5
        end
        if args.post then
           move = move - mw.ustring.len( args.post ) - 1
        end
        if move > 0 then
            shift = string.rep( "0", move )
            e     = mw.html.create( "span" )
                           :css( "visibility", "hidden" )
                           :wikitext( shift )
            shift = tostring( e )
        end
    end
    if args.n.low or args.n.long or args.n.sub then
        r = fine( args.n )
    end
    if args.suffix then
        r = r .. args.suffix
    end
    if args.exp then
        if Sort.cssNumExp then
            r = finest( args, r )
        else
            Sort.stick = Sort.stick  or  mw.ustring.char( 0xB7 )
            r, s = fined( r, args.exp )
            e = mw.html.create( "sup" )
                       :wikitext( s )
            r = string.format( "%s%s10%s",
                               r,
                               Sort.stick,
                               tostring( e ) )
        end
    end
    if args.pre  or
       args.n.long  or
       args.n.sub  or
       args.exp  or
       args.post  or
       args.n.script then
        if args.pre then
            r =  string.format( "%s %s", args.pre, r )
        end
        if args.post then
            r =  string.format( "%s %s", r, args.post )
        end
        e = mw.html.create( "span" )
                   :css( "white-space", "nowrap" )
                   :wikitext( r )
        if ( args.pre or args.post )  and
           not Sort.Cell.following() then
            e:attr( "dir", "ltr" )
        end
        if args.n.script then
            e:attr( "lang", "und-" .. args.n.script )
        end
        r = tostring( e )
    end
    if shift then
        r =  r .. shift
    end
    if args.pad   or
       args.pre   or
       ( args.cell  and
         ( args.n.low   or
           args.n.sign  or
           args.n.sub   or
           args.n.long  or
           args.suffix  or
           args.exp     or
           args.n.script ) ) then
        fore( args )
    end
    return r
end -- format()



local front = function ( analyse )
    -- Interprete heading text in front of number
    -- Parameter:
    --     analyse    -- string, with probably prefixed number
    -- Postcondition:
    --     Returns
    --         1. string|nil  -- prefix
    --         2. string|nil  -- remaining text, hopefully number
    --         3. number|nil  -- weight, cheat sort figure
    local i, r1, r2, r3
    local s = analyse
    if s:find( "&", 1, true ) then
        s = mw.text.decode( s )
        if s:find( "&", 1, true ) then
            local html = { lt     = 0x003C,
                           gt     = 0x003E,
                           plusmn = 0x00B1,
                           asymp  = 0x2248,
                           le     = 0x2264,
                           ge     = 0x2265 }
            for k, v in pairs( html ) do
                s = s:gsub( string.format( "&%s;", k ),
                            mw.ustring.char( v ) )
            end -- for k, v
        end
    end
    i = mw.ustring.codepoint( s, 1, 1 )
    for k, v in pairs( Sort.prefix ) do
        if k == i then
            r1 = mw.ustring.char( k )
            r2 = mw.text.trim( mw.ustring.sub( s, 2 ) )
            r3 = v
            break    -- for k, v
        end
    end -- for k, v
    if not r1 then
        local Value = fetch( "Text", false, "value" )
        if Value  and  type( Value.prefix ) == "table" then
            for k, v in pairs( Value.prefix ) do
                i = mw.ustring.len( k )
                if k == mw.ustring.sub( s, 1, i ) then
                    r1 = k
                    r2 = mw.text.trim( mw.ustring.sub( s,  i + 1 ) )
                    r3 = v
                    break    -- for k, v
                end
            end -- for k, v
        end
    end
    return r1, r2, r3
end -- front()



local furnish = function ( args )
    -- Execute task
    -- Parameter:
    --     args    -- table, parameters
    -- Postcondition:
    --     Returns string, or expands .cell
    --     Throws error on failure
    local r
    fetch( false, true, "Cell" )
    if type( args ) == "table" then
        local present = Sort.Cell.first( args )
        local s = type( args.exp )
        if s == "string" then
           Sort.Cell.fair( args, "exp", present )
        elseif s == "number" then
            present.exp = args.exp
        end
        Sort.Cell.fair( args, "pre", present )
        s = type( args.n )
        if s == "string" then
            s = mw.text.trim( args.n )
            if s == Sort.supreme then
                present.infinit = 1
            elseif mw.ustring.len( s ) == 2  and
                   mw.ustring.codepoint( s, 2, 2 ) == 8734 then
                local m = mw.ustring.codepoint( s, 1, 1 )
                if m == 45  or  m == 8722 then
                    present.infinit = -1
                elseif m == 43 then
                    present.infinit = 1
                end
            end
            if not present.infinit then
                if not present.exp then
                    local i = s:find( "%d[.,]?[eE]%-?%d+$" )
                    if i then
                        local split = s:sub( i + 2 ):upper()
                        if split:sub( 1, 1 ) == "E" then
                            split = split:sub( 2 )
                        end
                        present.exp = split
                        s           = s:sub( 1, i )
                    end
                end
                present.n = flat( s, true )
                if present.n.scream  and  not present.pre then
                    local move
                    present.pre, s, move = front( s )
                    if present.pre then
                        present.n = flat( s, true, move )
                    end
                end
            end
        elseif s == "number" then
            local k = args.n
            present.n = { low  = ( k < 0 ),
                          sort = tostring( k ) }
            present.n.show = present.n.sort
            if present.n.low then
                present.n.show = present.n.show:sub( 2 )
            end
            k = present.n.show:find( ".", 1, true )
            if k then
                present.n.sub  = present.n.show:sub( k + 1 )
                k = k - 1
                present.n.show = present.n.show:sub( 1, k )
            else
                k = mw.ustring.len( present.n.show )
            end
            present.n.long = ( k > Sort.nGroup )
        end
        if present.n or present.infinit then
            if present.n then
                local lazy
                s = type( present.exp )
                if s == "string" then
                    present.exp = flat( present.exp )
                elseif s == "number" then
                    local k = present.exp
                    present.exp = { low = ( k < 0 ) }
                    if present.exp.low then
                        k = -1 * k
                    end
                    k = math.floor( k )
                    present.exp.show = tostring( k )
                    if k > 0 then
                        present.exp.sort = present.exp.show
                        if present.exp.low then
                            present.exp.sort = "-" .. present.exp.sort
                        end
                    end
                end
                s = type( args.lazy )
                if s == "string" then
                    lazy = ( args.lazy == "1" )
                elseif s == "" then
                    lazy = args.lazy
                end
                if lazy then
                    fore( present )
                else
                    Sort.Cell.fair( args, "suffix", present )
                    s = type( args.pad )
                    if s == "string" then
                        present.pad = tonumber( args.pad )
                    elseif s == "number" then
                        present.pad = args.pad
                    end
                    if present.pad  and  present.pad == 0 then
                        present.pad = false
                    end
                    Sort.Cell.fair( args, "post", present )
                    if not present.n.low then
                        s = type( args.plus )
                        if s == "string" then
                            if args.plus == "1"  or
                               args.plus == "+" then
                                present.n.sign = "+"
                            end
                        elseif s == "boolean" then
                            present.n.sign = "+"
                        end
                    end
                    if args.cssNum  and  not Sort.cssNum then
                        Sort.cssNum = { }
                        Sort.cssNum.origin = args.cssNum
                    end
                    if args.cssNumExp  and  not Sort.cssNumExp then
                        Sort.cssNumExp = { }
                        Sort.cssNumExp.origin = args.cssNumExp
                    end
                    r = format( present )
                end
            else
                present.n = { sort = "9999999" }
                if present.infinit < 0 then
                    present.n.sort = "-" .. present.n.sort
                end
                present.exp = { sort = "301" }
                fore( present )
            end
            r = Sort.Cell.finalize( present, r )
        else
            Sort.Cell.fault( "???", args )
        end
    else
        error( "'args' is not a table" )
    end
    return r
end -- furnish()



Sort.f = function ( args )
    -- Create table cell start
    -- Parameter:
    --     args    -- table, parameters
    --                .pad        -- number|string, for padding
    --                .pre        -- string, for prefix
    --                .plus       -- boolean|string, for plus sign
    --                .n          -- number|string, for base
    --                .suffix     -- string, extending base
    --                .exp        -- number|string, for exponent
    --                .post       -- string, for postfix
    --                .round      -- number|string, for rounding
    --                .lazy       -- string|boolean, mute
    --                .cell       -- string|boolean|table, enforce sort
    --                .rowspan    -- number|string, for cell attribute
    --                .colspan    -- number|string, for cell attribute
    --                .class      -- string, for cell attribute
    --                .style      -- string|table, for cell attribute
    --                .id         -- string, for cell attribute
    --                .cssNum     -- string|title, for templatestyles
    --                .cssNumExp  -- string|title, for templatestyles
    --                .cat        -- string|nil, for error category
    --                .frame      -- table, if present
    -- Postcondition:
    --     Returns string, or expands .cell, or nil
    local lucky, r = pcall( furnish, args )
    if not lucky then
        local e = mw.html.create( "span" )
                         :addClass( "error" )
                         :wikitext( "Module:Sort/cell * " .. r )
        if type( args.cell ) == "table"  and
           type( args.cell.wikitext ) == "function" then
            args.cell:node( e )
        else
            r = tostring( e )
        end
    end
    return r
end -- Sort.f()



Sort.furnish = function ()
    -- Retrieve list of project prefixes
    -- Postcondition:
    --     Returns  string  -- with wikitext list
    local r, Value
    for k, v in pairs( Sort.prefix ) do
        if r then
            r = r .. "\n"
        else
            r = ""
        end
        r = string.format( "%s* %s",  r,  mw.ustring.char( k ) )
    end -- for k, v
    Value = fetch( "Text", false, "value" )
    if Value  and  type( Value.prefix ) == "table" then
        for k, v in pairs( Value.prefix ) do
            r = string.format( "%s\n* %s",  r,  k )
        end -- for k, v
    end
    return r
end -- Sort.furnish()



Failsafe.failsafe = function ( atleast )
    -- Retrieve versioning and check for compliance
    -- Precondition:
    --     atleast  -- string, with required version or "wikidata" or "~"
    --                 or false
    -- Postcondition:
    --     Returns  string  -- with queried version, also if problem
    --              false   -- if appropriate
    -- 2019-10-15
    local last  = ( atleast == "~" )
    local since = atleast
    local r
    if last  or  since == "wikidata" then
        local item = Failsafe.item
        since = false
        if type( item ) == "number"  and  item > 0 then
            local entity = mw.wikibase.getEntity( string.format( "Q%d",
                                                                 item ) )
            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
                    else
                        r = vsn.value
                    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 = { }

p.f = function ( frame )
    -- Template call
    Sort.frame = frame
    return Sort.f( frame.args )  or  ""
end -- p.f

p.furnish = function ()
    return Sort.furnish()
end -- p.f

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.Sort = function ()
    -- Module interface
    return Sort
end

return p