Modulo:kat

El Vikivortaro
 MODULO
Memtesto disponeblas sur la dokumentaĵa subpaĝo.
Ĉi tiu modulo estas multfoje bindita.
Se vi konas la eblajn sekvojn, tiam vi povas zorgeme ekredakti.
Se vi ne kuraĝas redakti tiam vi povas proponi la deziratan ŝanĝon en la diskutejo.



--[===[

MODULE "KAT"

"eo.wiktionary.org/wiki/Modulo:kat" <!--2023-Nov-18-->

Purpose: automatically generated content for categories

Utilo: auxtogenerita enhavo por kategorioj

Manfaat: isi dibangkitkan secara otomatis untuk kategori

Syfte: automatiskt genererat innehaall foer kategorier

Used by templates / Uzata far sxablonoj:
- only "kat" (not to be called from any other place)

Required submodules / Bezonataj submoduloj / Submodul yang diperlukan:
- "loaddata-tbllingvoj" T78 T80 in turn requiring template "tbllingvoj" (EO)
- "loaddata-tblbahasa" T78 T80 in turn requiring template "tblbahasa" (ID)

Required images:
- "File:Vista-folder open.png"
- "File:Hidden icon.png"
- "File:User-invisible.svg"

Required templates:
- YES, see above

This module can accept parameters whether sent to itself (own frame) or
to the caller (caller's frame). If there is a parameter "caller=true"
on the own frame then that own frame is discarded in favor of the
caller's one.

Incoming: * no ordinary parameters
          * 3 hidden named and optional parameters
            * "pagenameoverridetestonly=" can directly cause #E01
            * "nsnumberoverridetestonly=" can indirectly cause #E08
            * "detrc=" no error possible

Returned: * one string with visible part (HTML box) and category part,
            or red error and tracking category part

All relevant information is peeked from the pagename and thus
ordinary parameters are not used.

Unfortunately wikis define some dialects and scripts that we have to
support natively here ie circumventing central language and dialect tables:
* en-GB, en-CA (but NOT "en-US")
* de-AT
* zh-Hans, zh-Hant

To be called from category namespace only.

So far 6 types of pagename patterns after "Kategorio:" are recognized:
* G "El la anglosaksa" "El Sanskrito" has opposite cat
* G "Laux stilo (indonezia)"
* H "Tradukoj (indonezia)" needs __HIDDENCAT__ except "Tradukoj (Esperanto)"
* U "Vikivortaristo mi"
* U "Vikivortaristo mi-D"
* G "Vortaro (indonezia)"

Types of categories by content are:
* G generic
* H hidden
* U user

Tracking categories:
* #E06 "Kategori:Auxtokato kun nesubtenata pagxonomo"
* #E07 "Kategori:Auxtokato kun nekonata lingvonomo"
* #E05 #E08 "Kategori:Erara uzo de sxablono" &
            "Kategori:Erara uzo de sxablono (kat)"

]===]

local exporttable = {}

------------------------------------------------------------------------

---- CONSTANTS [O] ----

------------------------------------------------------------------------

  -- uncommentable strings (core site-related features)

  local constrpriv   = "eo"                          -- EO (privileged site language)
  -- local constrpriv   = "id"                          -- ID (privileged site language)
  local constringvoj = "Modulo:loaddata-tbllingvoj"  -- EO
  -- local constringvoj = "Modul:loaddata-tblbahasa"    -- ID
  local constrapxq   = "Aldono"             -- EO
  -- local constrapxq   = "Lampiran"           -- ID
  local constrkatq   = "Kategorio"          -- EO
  -- local constrkatq   = "Kategori"           -- ID

  -- surrogate transcoding table (only needed for EO)

  local contabtransluteo = {}
  contabtransluteo[ 67] = 0xC488 -- CX
  contabtransluteo[ 99] = 0xC489 -- cx
  contabtransluteo[ 71] = 0xC49C -- GX
  contabtransluteo[103] = 0xC49D -- gx
  contabtransluteo[ 74] = 0xC4B4 -- JX
  contabtransluteo[106] = 0xC4B5 -- jx
  contabtransluteo[ 83] = 0xC59C -- SX
  contabtransluteo[115] = 0xC59D -- sx
  contabtransluteo[ 85] = 0xC5AC -- UX breve
  contabtransluteo[117] = 0xC5AD -- ux breve

  -- strings (error circumfixes)

  local constrelabg = '<span class="error"><b>'  -- lagom whining begin
  local constrelaen = '</b></span>'              -- lagom whining end
  local constrlaxhu = '&nbsp;&#42;&#42;&nbsp;'   -- lagom -> huge circumfix " ** "

  -- uncommentable string (caller name for error messages)

  local constrkoll = 'sxablono "kat"'  -- EO augmented name of the caller (semi-hardcoded, we do NOT peek it) !!!FIXME!!!
  -- local constrkoll = 'templat "kat"'   -- ID augmented name of the caller (semi-hardcoded, we do NOT peek it)

  -- uncommentable table (error messages)

  -- #E02...#E99, note that #E00 and #E01 are NOT supposed to be included here

  local contaberaroj = {}
  contaberaroj[5] = 'Parametroj estas maldezirindaj por \\@, legu gxian dokumentajxon'   -- EO #E05 change to unknown param
  -- contaberaroj[5] = 'Parameter tidak diterima oleh \\@, bacalah dokumentasinya'          -- ID #E05 !!!FIXME!!!
  contaberaroj[6] = 'Erara pagxonomo (neniu el 6) por \\@'                               -- EO #E06
  -- contaberaroj[6] = 'Nama halaman salah (tidak satu dari 6) untuk \\@'                   -- ID #E06
  contaberaroj[7] = 'Nekonata lingvonomo por \\@'                                        -- EO #E07
  -- contaberaroj[7] = 'Nama bahasa yang tidak dikenal untuk \\@'                           -- ID #E07
  contaberaroj[8] = 'Erara nomspaco (estu 14, "Kategorio:") por \\@'                     -- EO #E08
  -- contaberaroj[8] = 'Ruang nama salah (sebaiknya 14, "Kategori:") untuk \\@'             -- ID #E08

  -- uncommentable table (boasting)

  local contabboasting = {}

  contabboasting['g0'] = 'En aux sub cxi tiu'
  contabboasting['g1'] = 'kategorio trovigxas'
  contabboasting['g2'] = 'lingvokodo'

  contabboasting['elel0'] = 'kapvortoj derivitaj el'
  contabboasting['elel1'] = 'La kontrauxa kategorio estas'

  contabboasting['stil0'] = 'subkategorioj por kapvortoj en la lingvo'
  contabboasting['stil1'] = 'laux stilo AKK lingvoregistro'

  contabboasting['trad'] = 'kapvortoj kun tradukoj al la lingvo'

  contabboasting[30] = 'vortaroj (aliaj ol tuta vikivortaro mem), kiuj specifas por la lingvo'
  contabboasting[31] = 'La esperantan vikivortaron vi jam uzas'  -- if code is "eo"
  contabboasting[32] = 'La'                                      -- if it exists
  contabboasting[33] = 'Bedauxrinde ne disponeblas'              -- if it lacks
  contabboasting[34] = 'vikivortaro'                    -- in both cases
  contabboasting[35] = 'provu'                          -- if it lacks
  contabboasting[36] = 'kaj ecx la kodo estas okupita'  -- if it lacks and ... (for example "als", has "??" in /c6/)

  contabboasting['isnl'] = 'subkategorioj por uzantoj kiuj indikis sian nivelon en'

  contabboasting['islv0'] = 'uzantoj kiuj indikis sian nivelon en'
  contabboasting['islv1'] = 'je'

  -- main list with pagename patterns --

  -- * [1] parsing method
  --       * 3 -> sepbracket
  --       * 4 "el"-type with "la" trouble
  --       * 5 isto with level
  --       * 6 isto without level
  -- * [2] pattern for parsing method (outer string for 3 | unused for 4)
  -- * [3] reserved
  -- * [4] (char) type by conent G H U
  -- * [5] (boo) categorize in lang (for example in "Kategori:Sveda" or in "Kategori:Greka antikva") with no hint
  -- * [6] (char) type of hint in extra cat
  --   * ""  empty string to deactive this cat
  --   * "N" lang name lowercase -- "Ido -> ido"
  --   * "K" lang code
  --   * "L" level -- "0" "1" "2" "3" "4" "D" -> "d"
  -- * [7] name of extra cat (by lang or by level or by something else)
  --       (empty if none, but [6] is the control, also empty if cat
  --       used but name not needed)

  local contabtypesofcats = {}
  contabtypesofcats['elel'] = {4, '',           '', 'G', true,  'N', 'Laux fonta lingvo'}           -- "El la anglosaksa"
  contabtypesofcats['stil'] = {3, 'Laux stilo', '', 'G', true,  'N', 'Laux stilo kaj lingvo'}       -- "Laux stilo (indonezia)"
  contabtypesofcats['trad'] = {3, 'Tradukoj',   '', 'H', true,  'N', 'Laux traduko'}                -- "Tradukoj (indonezia)"
  contabtypesofcats['isnl'] = {6, '',           '', 'U', false, 'K', 'Vikivortaristo laux lingvo'}  -- "Vikivortaristo mi"
  contabtypesofcats['islv'] = {5, '',           '', 'U', false, 'L', ''}                            -- "Vikivortaristo mi-D"
  contabtypesofcats['varo'] = {3, 'Vortaro',    '', 'G', true,  'N', 'Vortaro laux lingvo'}         -- "Vortaro (indonezia)"

  -- tracking cat:s --

  local contabtracking = {}
  contabtracking[0] = 'Auxtokato kun nesubtenata pagxonomo' -- #E06
  contabtracking[1] = 'Auxtokato kun nekonata lingvonomo'   -- #E07
  contabtracking[2] = 'Erara uzo de sxablono'               -- #E05 #E08
  contabtracking[3] = 'Erara uzo de sxablono (kat)'         -- #E05 #E08

  -- icons and descriptions for types of category by content

  local contabicon = {}
  contabicon['G'] = 'Vista-folder open.png'  -- generic
  contabicon['H'] = 'Hidden icon.png'        -- hidden
  contabicon['U'] = 'User-invisible.svg'     -- user     alternative image "Nuvola apps personal unisex.svg"

  local contabdesc = {}
  contabdesc['G'] = ''
  contabdesc['H'] = 'kasxita'
  contabdesc['U'] = 'uzanta'

  -- table (HTML)

  local contabhtml = {}
  contabhtml[0] = '<table style="border:1px solid #A0A0A0; background:#F0F0F0; width:95%; margin:0.6em auto 0.6em auto; padding:0.6em; text-align:justify;">'
  contabhtml[3] = '<tr><td style="width:30px;">[[File:'
  contabhtml[4] = '|50px]]</td><td style="padding-left:1em;">'
  contabhtml[5] = '</td></tr></table>'

------------------------------------------------------------------------

---- SPECIAL STUFF OUTSIDE MAIN [B] ----

------------------------------------------------------------------------

---- SPECIAL VAR:S ----

local qldingvoj = {}     -- type "table" and nested
local qboodetrc = true   -- from "detrc=true" but default is "true" !!!
local qstrtrace = '<br>' -- for main & sub:s, debug report request by "detrc="
local qbooguard = false  -- only for the guard test, pass to other var ASAP

---- GUARD AGAINST INTERNAL ERROR AND IMPORT ONE VIA LOADDATA ----

qbooguard = (type(constrpriv)~='string') or (type(constringvoj)~='string') or (type(constrapxq)~='string') or (type(constrkatq)~='string') or (type(constrkoll)~='string')
if (not qbooguard) then
  qldingvoj = mw.loadData(constringvoj) -- can crash here
  qbooguard = (type(qldingvoj)~='table') -- seems to be always false
end--if

------------------------------------------------------------------------

---- DEBUG FUNCTIONS [D] ----

------------------------------------------------------------------------

-- Local function LFTRACEMSG

-- Enhance upvalue "qstrtrace" with fixed text.

-- for variables the other sub "lfdshowvar" is preferable but in exceptional
-- cases it can be justified to send text with values of variables to this sub

-- no size limit

-- upvalue "qstrtrace" must NOT be type "nil" on entry (is inited to "<br>")

-- uses upvalue "qboodetrc"

local function lftracemsg (strshortline)
  if (qboodetrc and (type(strshortline)=='string')) then
    qstrtrace = qstrtrace .. strshortline .. '.<br>' -- dot added !!!
  end--if
end--function lftracemsg

------------------------------------------------------------------------

-- Local function LFDIMPORTREPORT

-- Enhance upvalue "qstrtrace" with imported report.

-- use this one to import detrc text from submodule

-- upvalue "qstrtrace" must NOT be type "nil" on entry (is inited to "<br>")

-- uses upvalue "qboodetrc"

local function lfdimportreport (strshortlineorbigtext)
  local strseparator = ''
  if (qboodetrc and (type(strshortlineorbigtext)=='string')) then
    strseparator = '<br>&#35;######################&#35;<br>'
    qstrtrace = qstrtrace .. strseparator .. strshortlineorbigtext .. strseparator .. '<br>'
  end--if
end--function lfdimportreport

------------------------------------------------------------------------

-- Local function LFDMINISANI

-- Input  : * strdangerous -- must be type "string", empty legal
--          * numlimitdivthree

-- Output : * strsanitized -- can happen to be quasi-empty with <<"">>

-- To be called from "lfdshowvcore" <- "lfdshowvar" only.

-- * we absolutely must disallow: cross "#" 35 | apo "'" 39 |
--   star "*" 42 | dash 45 | colon 58 | "<" 60 | ">" 62 | "[" 91 | "]" 93
-- * spaces are showed as "{32}" if repetitive or at begin or at end

local function lfdminisani (strdangerous, numlimitdivthree)

  local strsanitized = '"' -- begin quot
  local num38len = 0
  local num38index = 1 -- ONE-based
  local num38signo = 0
  local num38prev = 0
  local boohtmlenc = false
  local boovisienc = false

  num38len = string.len (strdangerous)
  while true do
    boohtmlenc = false -- % reset on
    boovisienc = false -- % every iteration
    if (num38index>num38len) then -- ONE-based
      break -- done string char after char
    end--if
    num38signo = string.byte (strdangerous,num38index,num38index)
    if ((num38signo<43) or (num38signo==45) or (num38signo==58) or (num38signo==60) or (num38signo==62) or (num38signo==91) or (num38signo==93) or (num38signo>122)) then
      boohtmlenc = true
    end--if
    if ((num38signo<32) or (num38signo>126)) then
      boovisienc = true -- overrides "boohtmlenc"
    end--if
    if ((num38signo==32) and ((num38prev==32) or (num38index==1) or (num38index==num38len))) then
      boovisienc = true -- overrides "boohtmlenc"
    end--if
    if (boovisienc) then
      strsanitized = strsanitized .. '{' .. tostring (num38signo) .. '}'
    else
      if (boohtmlenc) then
        strsanitized = strsanitized .. '&#' .. tostring (num38signo) .. ';'
      else
        strsanitized = strsanitized .. string.char (num38signo)
      end--if
    end--if
    if ((num38len>(numlimitdivthree*3)) and (num38index==numlimitdivthree)) then
      num38index = num38len - numlimitdivthree -- jump forwards
      strsanitized = strsanitized .. '" ... "'
    else
      num38index = num38index + 1 -- ONE-based
    end--if
    num38prev = num38signo
  end--while
  strsanitized = strsanitized .. '"' -- don't forget final quot

  return strsanitized

end--function lfdminisani

------------------------------------------------------------------------

-- Local function LFDSHOWVCORE

-- Prebrew report about content of a variable including optional full
-- listing of a table with numerical and string indexes.                        !!!FIXME!!!

-- Input  : * vardubious -- content (any type including "nil")
--          * strname -- name of the variable (string)
--          * vardescri -- optional, defa empty
--          * vartablim -- optional, defa 0, max 20

-- Depends on functions :
-- [D] lfdminisani

local function lfdshowvcore (vardubious, strname, vardescri, vartablim)

  local taballkeystring = {}
  local strtype = ''
  local strreport = ''
  local numindax = 0
  local numlencx = 0
  local numkeynumber = 0
  local numkeystring = 0
  local numkeycetera = 0
  local numkey77min = 999999
  local numkey77max = -999999

  if (type(vartablim)~='number') then
    vartablim = 0 -- deactivate listing of a table
  end--if
  if (type(vardescri)~='string') then
    vardescri = '' -- omit comment
  else
    vardescri = ' (' .. vardescri .. ')' -- augment only if used !!!
  end--if
  if (type(strname)~='string') then -- this must be below "type(vardescri)"
    strname = '??' -- bite the bullet
  else
    strname = '"' .. strname .. '"' .. vardescri -- combo
  end--if

  strtype = type(vardubious)

  if (strtype=='table') then

    for k,v in pairs(vardubious) do
      if (type(k)=='number') then
        numkey77min = math.min (numkey77min,k)
        numkey77max = math.max (numkey77max,k)
        numkeynumber = numkeynumber + 1
      else
        if (type(k)=='string') then
          taballkeystring [numkeystring] = k
          numkeystring = numkeystring + 1
        else
          numkeycetera = numkeycetera + 1
        end--if
      end--if
    end--for

    strreport = 'Table ' .. strname
    if ((numkeynumber==0) and (numkeystring==0) and (numkeycetera==0)) then
      strreport = strreport .. ' is empty'
    else
      strreport = strreport .. ' contains '
      if (numkeynumber==0) then
        strreport = strreport .. 'NO numeric keys'
      end--if
      if (numkeynumber==1) then
        strreport = strreport .. 'a single numeric key equal ' .. tostring (numkey77min)
      end--if
      if (numkeynumber>=2) then
        strreport = strreport .. tostring (numkeynumber) .. ' numeric keys ranging from ' .. tostring (numkey77min) .. ' to ' .. tostring (numkey77max)
      end--if
      strreport = strreport .. ' and ' .. tostring (numkeystring) .. ' string keys and ' .. tostring (numkeycetera) .. ' other keys'
    end--if

    if ((numkeynumber~=0) and (vartablim~=0)) then -- !!!FIXME!!!
      strreport = strreport .. ' ### content num keys :'
      numindax = numkey77min
        while true do
          if ((numindax>vartablim) or (numindax>numkey77max)) then
            break -- done table
          end--if
          strreport = strreport .. ' ' .. tostring(numindax) .. ' -> ' .. lfdminisani(tostring(vardubious[numindax]),30)
          numindax = numindax + 1
        end--while
    end--if

    if ((numkeystring~=0) and (vartablim~=0)) then -- !!!FIXME!!!
      strreport = strreport .. ' ### content string keys :'
    end--if

  else

      strreport = 'Variable ' .. strname .. ' has type "' .. strtype .. '"'
      if (strtype=='string') then
        numlencx = string.len (vardubious)
        strreport = strreport .. ' and length ' .. tostring (numlencx)
        if (numlencx~=0) then
          strreport = strreport .. ' and content ' .. lfdminisani (vardubious,30)
        end--if
      else
        if (strtype~='nil') then
          strreport = strreport .. ' and content "' .. tostring (vardubious) .. '"'
        end--if
      end--if (strtype=='string') else

  end--if (strtype=='table') else

  return strreport

end--function lfdshowvcore

------------------------------------------------------------------------

-- Local function LFDSHOWVAR

-- Enhance upvalue "qstrtrace" with report about content of a
-- variable including optional full listing of a table with numerical
-- and string indexes.                                                          !!!FIXME!!!

-- Depends on functions :
-- [D] lfdminisani lfdshowvcore

-- upvalue "qstrtrace" must NOT be type "nil" on entry (is inited to "<br>")

-- uses upvalue "qboodetrc"

local function lfdshowvar (varduubious, strnaame, vardeskkri, vartabljjm)

  if (qboodetrc) then
    qstrtrace = qstrtrace .. lfdshowvcore (varduubious, strnaame, vardeskkri, vartabljjm) .. '.<br>' -- dot added !!!
  end--if

end--function lfdshowvar

------------------------------------------------------------------------

---- MATH FUNCTIONS [E] ----

------------------------------------------------------------------------

local function mathisintrange (numinpuut, numzjmin, numzjmax)
  local numclean = 0
  local booisclean = false
  numclean = math.floor (numinpuut) -- no transcendental
  numclean = math.max (numclean,numzjmin) -- not below minimum
  numclean = math.min (numclean,numzjmax) -- no trillions
  booisclean = (numclean==numinpuut)
  return booisclean
end--function mathisintrange

local function mathdiv (xdividens, xdivisero)
  local resultdiv = 0 -- DIV operator lacks in LUA :-(
  resultdiv = math.floor (xdividens / xdivisero)
  return resultdiv
end--function mathdiv

local function mathmod (xdividendo, xdivisoro)
  local resultmod = 0 -- MOD operator is "%" and bitwise AND operator lack too
  resultmod = xdividendo % xdivisoro
  return resultmod
end--function mathmod

------------------------------------------------------------------------

-- Local function MATHBITWRIT

-- Write bit selected by ZERO-based index assigning it to "1" or "0".

-- Depends on functions :
-- [E] mathdiv mathmod

local function mathbitwrit (numinkoming, numbityndex, boowrite)
  local numpatched = 0
  local numcountup = 0
  local numweight = 1 -- single bit value 1 -> 2 -> 4 -> 8 ...
  local boosinglebit = false
  while true do
    if ((numinkoming==0) and (numcountup>numbityndex)) then
      break -- we have run out of bits on BOTH possible sources
    end--if
    if (numcountup==numbityndex) then
      boosinglebit = boowrite -- overwrite bit
    else
      boosinglebit = (mathmod(numinkoming,2)==1) -- pick bit
    end--if
    numinkoming = mathdiv(numinkoming,2) -- shift right
    if (boosinglebit) then
      numpatched = numpatched + numweight -- add one bit rtl only if true
    end--if
    numcountup = numcountup + 1 -- count up here until we run out of bits
    numweight = numweight * 2
  end--while
  return numpatched
end--function mathbitwrit

------------------------------------------------------------------------

---- NUMBER CONVERSION FUNCTIONS [N] ----

------------------------------------------------------------------------

-- Local function LFNUMTO2DIGIT

-- Convert integer 0...99 to decimal ASCII string always 2 digits "00"..."99".

-- Depends on functions :
-- [E] mathisintrange mathdiv mathmod

local function lfnumto2digit (numzerotoninetynine)
  local strtwodig = '??' -- always 2 digits
  if (mathisintrange(numzerotoninetynine,0,99)) then
    strtwodig = tostring(mathdiv(numzerotoninetynine,10)) .. tostring(mathmod(numzerotoninetynine,10))
  end--if
  return strtwodig
end--function lfnumto2digit

------------------------------------------------------------------------

---- LOW LEVEL STRING FUNCTIONS [G] ----

------------------------------------------------------------------------

-- test whether char is an ASCII digit "0"..."9", return boolean

local function lfgtestnum (numkaad)
  local boodigit = false
  boodigit = ((numkaad>=48) and (numkaad<=57))
  return boodigit
end--function lfgtestnum

------------------------------------------------------------------------

-- test whether char is an ASCII uppercase letter, return boolean

local function lfgtestuc (numkode)
  local booupperc = false
  booupperc = ((numkode>=65) and (numkode<=90))
  return booupperc
end--function lfgtestuc

------------------------------------------------------------------------

-- test whether char is an ASCII lowercase letter, return boolean

local function lfgtestlc (numcode)
  local boolowerc = false
  boolowerc = ((numcode>=97) and (numcode<=122))
  return boolowerc
end--function lfgtestlc

------------------------------------------------------------------------

---- UTF8 FUNCTIONS [U] ----

------------------------------------------------------------------------

-- Local function LFULNUTF8CHAR

-- Evaluate length of a single UTF8 char in octet:s.

-- Input  : * numbgoctet  -- beginning octet of a UTF8 char

-- Output : * numlen1234x -- unit octet, number 1...4, or ZERO if invalid

-- Does NOT thoroughly check the validity, looks at ONE octet only.

local function lfulnutf8char (numbgoctet)
  local numlen1234x = 0
    if (numbgoctet<128) then
      numlen1234x = 1 -- $00...$7F -- ANSI/ASCII
    end--if
    if ((numbgoctet>=194) and (numbgoctet<=223)) then
      numlen1234x = 2 -- $C2 to $DF
    end--if
    if ((numbgoctet>=224) and (numbgoctet<=239)) then
      numlen1234x = 3 -- $E0 to $EF
    end--if
    if ((numbgoctet>=240) and (numbgoctet<=244)) then
      numlen1234x = 4 -- $F0 to $F4
    end--if
  return numlen1234x
end--function lfulnutf8char

------------------------------------------------------------------------

-- Local function LFCASEREST

-- Adjust case of a single letter (restricted), only ASCII
-- plus a very limited set of 2-octet UTF8 letters. (this is REST)

-- Input  : * strucinrsut : single unicode letter (1 or 2 octet:s)
--          * booupcas    : for desired uppercase "true" and for
--                          lowercase "false"
--          * numselset   : 0 ASCII -- 2 eo -- 5 sv (value 255 NOT here)

-- Output : * strucinrsut (same var) : special value "ZZ" on failure

-- Depends on functions : (this is REST)
-- [U] lfulnutf8char
-- [G] lfgtestuc lfgtestlc
-- [E] mathdiv mathmod mathbitwrit

-- Unknown non-ASCII input strictly returns "ZZ"

-- Defined sets:
-- 2: 2 x 6 uppercase and lowercase -eo- (CX GX HX JX SX UX cx gx hx jx sx ux)
--    upper CX $0108 GX $011C HX $0124 JX $0134 SX $015C UX $016C lower +1
-- 5: 2 x 4 uppercase and lowercase -sv- (AA AE OE EE aa ae oe ee)
--    upper AE $00C4 AA $00C5 EE $00C9 OE $00D6 lower +$20

local function lfcaserest (strucinrsut, booupcas, numselset)

  local numlaengdn = 0 -- length from "string.len"
  local numchaerr = 0 -- UINT8 beginning char
  local numchaess = 0 -- UINT8 later char (BIG ENDIAN, lower value here up)
  local numchareel = 0 -- UINT8 code relative to beginning of block $00...$FF
  local numdeta = 0 -- SINT8 +32 or -32 or +1 or -1 or ZERO
  local numtheemp = 0

  local boowantlowr = false
  local booiisuppr = false
  local booiislowr = false
  local boovalid = false -- preASSume guilt

  booupcas = not (not booupcas)
  boowantlowr = (not booupcas)

  while true do -- upper fake loop (this is REST)

    numlaengdn = string.len (strucinrsut)
    if ((numlaengdn<1) or (numlaengdn>2)) then -- only 1 or 2 accepted
      break -- bad string length
    end--if
    numchaerr = string.byte (strucinrsut,1,1)
    if ((lfulnutf8char(numchaerr))~=numlaengdn) then
      break -- mismatch with length
    end--if

    if (numlaengdn==1) then
      booiisuppr = lfgtestuc(numchaerr)
      booiislowr = lfgtestlc(numchaerr)
      if (booiisuppr and boowantlowr) then
        numdeta = 32 -- ASCII UPPER->lower
      end--if
      if (booiislowr and booupcas) then
        numdeta = -32 -- ASCII lower->UPPER
      end--if
      boovalid = true
      break -- success with ASCII, almost done
    end--if

    numchaess = string.byte (strucinrsut,2,2) -- only $80 to $BF
    numchareel = (mathmod(numchaerr,4)*64) + (numchaess-128) -- 4 times 64

    if ((numselset==2) and ((numchaerr==196) or (numchaerr==197))) then -- eo
      numtheemp = mathbitwrit (numchareel,0,false) -- bad way to do AND $FE
      if ((numtheemp==8) or (numtheemp==28) or (numtheemp==36) or (numtheemp==52) or (numtheemp==92) or (numtheemp==108)) then
        booiisuppr = (numtheemp==numchareel) -- UC below and even
        booiislowr = not booiisuppr
        if (booiisuppr and boowantlowr) then
          numdeta = 1 -- UPPER->lower
        end--if
        if (booiislowr and booupcas) then
          numdeta = -1 -- lower->UPPER
        end--if
        boovalid = true
        break -- success with -eo-, almost done
      end--if
    end--if ((numselset==2) and ...

    if ((numselset==5) and (numchaerr==195)) then -- sv
      numtheemp = mathbitwrit (numchareel,5,false) -- bad way to do AND $DF
      if ((numtheemp==196) or (numtheemp==197) or (numtheemp==201) or (numtheemp==214)) then
        booiisuppr = (numtheemp==numchareel) -- UC below and bit is ZERO
        booiislowr = not booiisuppr
        if (booiisuppr and boowantlowr) then
          numdeta = 32 -- UPPER->lower
        end--if
        if (booiislowr and booupcas) then
          numdeta = -32 -- lower->UPPER
        end--if
        boovalid = true
        break -- success with -sv-, almost done
      end--if
    end--if ((numselset==5) and ...

    break -- finally to join mark -- unknown non-ASCII char is a fact :-(
  end--while -- upper fake loop -- join mark (this is REST)

  while true do -- lower fake loop (this is REST)

    if (not boovalid) then
      strucinrsut = "ZZ" -- unknown non-ASCII char
      break -- helvete
    end--if

    if (numdeta==0) then
      break -- nothing to do
    end--if

    if (numlaengdn==1) then
      strucinrsut = string.char (numchaerr + numdeta) -- no risk of carry here
      break -- done
    end--if

    strucinrsut = string.char (numchaerr) .. string.char (numchaess + numdeta)

    break -- finally to join mark
  end--while -- lower fake loop -- join mark (this is REST)

  return strucinrsut -- same var for input and output

end--function lfcaserest

------------------------------------------------------------------------

-- Local function LFUCASESTR

-- Adjust case of beginning letter or of all letters in a word or group
-- of words to upper or lower, with limited and adjustable unicode support.

-- Input  : * strenigo  : word or group of words (may be empty)
--          * boouprcas : "true" for uppercase and "false" for lowercase
--          * boodooall : "true" to adjust all letters, "false" only beginning
--          * numslsaet : 0 ASCII -- 2 eo -- 5 sv -- NOPE 255 generic

-- Depends on functions : (restricted LFCASEREST)
-- [U] lfulnutf8char lfcaserest
-- [G] lfgtestuc lfgtestlc
-- [E] mathdiv mathmod mathbitwrit

local function lfucasestr (strenigo, boouprcas, boodooall, numslsaet)

  local numlein = 0
  local numposi = 1 -- octet position ONE-based
  local numcut = 0 -- length of an UTF8 char
  local bootryadj = false -- try to adjust single char
  local strte7mp = ''
  local strelygo = ''

  boouprcas = not (not boouprcas)
  boodooall = not (not boodooall)
  numlein = string.len (strenigo)

  while true do
    if (numposi>numlein) then
      break -- done
    end--if
    bootryadj = (boodooall or (numposi==1))
    numcut = lfulnutf8char(string.byte(strenigo,numposi,numposi))
    if ((numcut==0) or ((numposi+numcut-1)>numlein)) then
      numcut = 1 -- skip ie copy one faulty octet
      bootryadj = false
    end--if
    strte7mp = string.sub (strenigo,numposi,(numposi+numcut-1)) -- 1...4 oct
    if (bootryadj) then
      -- if (numslsaet==255) then
        -- strte7mp = lfcasegene(strte7mp,boouprcas) -- (generous LFCASEGENE)
      -- else
        strte7mp = lfcaserest(strte7mp,boouprcas,numslsaet) -- (restricted LFCASEREST)
      -- end--if
    end--if
    strelygo = strelygo .. strte7mp -- this can be slow
    numposi = numposi + numcut -- done 1...4 octet:s
  end--while

  return strelygo

end--function lfucasestr

------------------------------------------------------------------------

---- HIGH LEVEL STRING FUNCTIONS [I] ----

------------------------------------------------------------------------

-- Local function LFIKATALDIGU

-- Brew cat insertion (no extra colon ":") or link to   !!!FIXME!!! DEPRECATED
-- appendix from 3 elements.

local function lfikataldigu (strprefixx, strkataldnomo, strhintvisi)
  local strrbkma = ''
  if (type(strhintvisi)=='string') then
    strrbkma = '[[' .. strprefixx .. ':' .. strkataldnomo .. '|' .. strhintvisi .. ']]'
  else
    strrbkma = '[[' .. strprefixx .. ':' .. strkataldnomo .. ']]'
  end--if
  return strrbkma
end--function lfikataldigu

------------------------------------------------------------------------

-- Local function LFIKATPALDIGU

-- Brew cat insertion (no extra colon ":") or link to cat (with extra
-- colon ":") or link to page (appendix, other ns, even ns ZERO) from
-- 3 elements with optimization.

-- Input  : * strprephyx -- ns prefix without colon, empty or
--                          non-string if not desired ie ns ZERO
--          * strkataldnomo
--          * strhintvisi -- right part, empty or non-string if not desired
--          * numkattxtrakol -- ZERO for non-cat, ONE for cat insertion
--                              (needed for optimization), TWO for extra
--                              colon ie "colon rule" link to cat

local function lfikatpaldigu (strprephyx, strkataldnomo, strhintvisi, numkattxtrakol)
  local strtigatipwiki = ''
  if (type(strprephyx)~="string") then -- optional
    strprephyx = ''
  end--if
  if (type(strhintvisi)~="string") then -- optional
    strhintvisi = ''
  end--if
  if ((numkattxtrakol==1) and (strhintvisi==strkataldnomo)) then
    strhintvisi = '' -- optimize: default is without ns prefix for cat:s
  end--if
  if (strprephyx~='') then
    strkataldnomo = strprephyx .. ':' .. strkataldnomo -- now prefix plus name
  end--if
  if ((numkattxtrakol~=1) and (strhintvisi==strkataldnomo)) then
    strhintvisi = '' -- optimize: default is with ns prefix if such is present
  end--if
  if (numkattxtrakol==2) then
    strkataldnomo = ':' .. strkataldnomo -- ":Category"... apply "colon rule"
  end--if
  if (strhintvisi=='') then
    strtigatipwiki = '[[' .. strkataldnomo .. ']]'
  else
    strtigatipwiki = '[[' .. strkataldnomo .. '|' .. strhintvisi .. ']]'
  end--if
  return strtigatipwiki
end--function lfikatpaldigu

------------------------------------------------------------------------

-- Local function LFIBREWCATHINT

-- Brew sorting hint/key for wiki category by lowering all letters and
-- removing junk chars other than letters and numbers. All ASCII and
-- selected non-ASCII letters do count as such. Particularly ban spaces
-- and dashes. Controllably UTF8-aware.

-- Input  : * strhinthink
--          * numsignaro -- 0 ASCII -- 2 eo -- 5 sv -- NOPE 255 generic
--          * bookeepuk -- keep unknown non-ASCII (by default dropped)

-- Output : * strhasiil -- risk of empty

-- Depends on functions : (restricted LFCASEREST)
-- [U] lfulnutf8char lfcaserest
-- [G] lfgtestnum lfgtestuc lfgtestlc
-- [E] mathdiv mathmod mathbitwrit

-- Simplified strategy:
-- * numbers unchanged
-- * ASCII lowercase unchanged
-- * ASCII uppercase lowered
-- * non-ASCII sent to "lfcase" with attempt to lower, if unknown then
--   by default dropped, alternatively unchanged
-- * everything else dropped
-- * broken stream aborts and gives empty result

local function lfibrewcathint (strhinthink, numsignaro, bookeepuk)

  local strhasiil = ''
  local stronechhar = ''
  local stronechaar = ''
  local numstrleon = 0
  local numeindx = 1 -- ONE-based
  local numczaar = 0
  local numczanx = 0 -- pre-picked next char
  local numettfyra = 0

  numstrleon = string.len (strhinthink)

  while true do -- outer genuine loop over source string

    if (numeindx>numstrleon) then
      break -- empty input is useless but cannot cause major harm
    end--if

    numczaar = string.byte (strhinthink,numeindx,numeindx)
    numeindx = numeindx + 1 -- do INC here
    numczanx = 0
    if (numeindx<=numstrleon) then -- pick but do NOT INC
      numczanx = string.byte (strhinthink,numeindx,numeindx)
    end--if

    while true do -- inner fake loop
      numettfyra = lfulnutf8char (numczaar) -- 1...4 or ZERO on error
      if (numettfyra==1) then
        if (lfgtestnum(numczaar) or lfgtestlc(numczaar)) then
          stronechaar = string.char (numczaar)
          break -- numbers and ASCII lowercase pass unchanged
        end--if
        if (lfgtestuc(numczaar)) then
          stronechaar = string.char (numczaar+32) -- lower ASCII letter
          break -- lower it
        end--if
      end--if
      if (numettfyra==2) then
        stronechaar = string.char(numczaar,numczanx)
        -- if (numsignaro==255) then
          -- stronechhar = lfcasegene (stronechaar,false)
        -- else
          stronechhar = lfcaserest (stronechaar,false,numsignaro)
        -- end--if
        if (stronechhar=='ZZ') then -- unknown non-ASCII
          if (not bookeepuk) then
            stronechaar = '' -- discard it, else let it pass
          end--if
        else
          stronechaar = stronechhar -- converted
        end--if
        break -- done here
      end--if
      stronechaar = '' -- anything else -> discard it
      break -- finally to join mark
    end--while -- inner fake loop -- join mark

    if (numettfyra==0) then
      strhasiil = ''
      break -- broken stream -> bugger all -- exit outer loop
    end--if

    strhasiil = strhasiil .. stronechaar -- ASCII char or UTF8 char or ""
    numeindx = numeindx + numettfyra - 1 -- do ADD here

  end--while -- outer genuine loop over source string

  return strhasiil

end--function lfibrewcathint

------------------------------------------------------------------------

-- Local function LFISEPBRACKET

-- Separate bracketed part of a string and return the inner and outer
-- part, the outer one with the brackets. There must be exactly ONE "("
-- and exactly ONE ")" in correct order.

-- Input  : * strsep33br
--          * numxmin33len -- minimal length of inner part, must be >= 1

-- Output : * boosaxes, strinner, strouter

-- Note that for length of hit ZERO ie "()" we would have "begg" + 1 = "endd"
-- and for length of hit ONE ie "(x)" we have "begg" + 2 = "endd".

-- Example: "crap (NO)" -> len = 9
--           123456789
-- "begg" = 6 and "endd" = 9
-- Expected result: "NO" and "crap ()"

-- Example: "(XX) YES" -> len = 8
--           12345678
-- "begg" = 1 and "endd" = 4
-- Expected result: "XX" and "() YES"

local function lfisepbracket (strsep33br, numxmin33len)

  local strinner = ''
  local strouter = ''
  local num33idx = 1 -- ONE-based
  local numdlong = 0
  local num33wesel = 0
  local numbegg = 0 -- ONE-based, ZERO invalid
  local numendd = 0 -- ONE-based, ZERO invalid
  local boosaxes = false -- preASSume guilt

  numdlong = string.len (strsep33br)
  while true do
    if (num33idx>numdlong) then
      break -- ONE-based -- if both "numbegg" "numendd" non-ZERO then maybe
    end--if
    num33wesel = string.byte(strsep33br,num33idx,num33idx)
    if (num33wesel==40) then -- "("
      if (numbegg==0) then
        numbegg = num33idx -- pos of "("
      else
        numbegg = 0
        break -- damn: more than 1 "(" present
      end--if
    end--if
    if (num33wesel==41) then -- ")"
      if ((numendd==0) and (numbegg~=0) and ((numbegg+numxmin33len)<num33idx)) then
        numendd = num33idx -- pos of ")"
      else
        numendd = 0
        break -- damn: more than 1 ")" present or ")" precedes "("
      end--if
    end--if
    num33idx = num33idx + 1
  end--while

  if ((numbegg~=0) and (numendd~=0)) then
    boosaxes = true
    strouter = string.sub(strsep33br,1,numbegg) .. string.sub(strsep33br,numendd,-1)
    strinner = string.sub(strsep33br,(numbegg+1),(numendd-1))
  end--if

  return boosaxes, strinner, strouter

end--function lfisepbracket

------------------------------------------------------------------------

-- Local function LFIFILLNAME

-- Replace placeholder "\@" "\\@" by augmented name of the caller.

-- To be called ONLY from "lfhfillsurrstrtab".

-- The name of the caller is submitted to us as a parameter thus we
-- do NOT access any constants and do NOT have to peek it either.

local function lfifillname (strmessage, strcaller)

  local strhasill = ''
  local numstrloen = 0
  local numindfx = 1 -- ONE-based
  local numcjar = 0
  local numcjnext = 0

  numstrloen = string.len (strmessage)

  while true do
    if (numindfx>numstrloen) then
      break -- empty input is useless but cannot cause major harm
    end--if
    numcjar = string.byte (strmessage,numindfx,numindfx)
    numindfx = numindfx + 1
    numcjnext = 0 -- preASSume no char
    if (numindfx<=numstrloen) then
      numcjnext = string.byte (strmessage,numindfx,numindfx)
    end--if
    if ((numcjar==92) and (numcjnext==64)) then
      strhasill = strhasill .. strcaller -- invalid input is caller's risk
      numindfx = numindfx + 1 -- skip 2 octet:s of the placeholder
    else
      strhasill = strhasill .. string.char (numcjar)
    end--if
  end--while

  return strhasill

end--function lfifillname

------------------------------------------------------------------------

-- Local function LFIKODEOSG

-- Transcode eo X-surrogates to cxapeloj in a single string (eo only).

-- Input  : * streosurr -- ANSI string (empty is useless but cannot
--                                      cause major harm)

-- Output : * strutf8eo -- UTF8 string

-- Depends on functions :
-- [E] mathdiv mathmod

-- Depends on constants :
-- * table "contabtransluteo" inherently holy

-- To be called ONLY from "lfhfillsurrstrtab".

-- * the "x" in a surr pair is case insensitive,
--   for example both "kacxo" and "kacXo" give same result
-- * avoid "\", thus for example "ka\cxo" would get converted but the "\" kept
-- * double "x" (both case insensitive) prevents conversion and becomes
--   reduced to single "x", for example "kacxxo" becomes "kacxo"

local function lfikodeosg (streosurr)

  local vareopeek = 0
  local strutf8eo = ''
  local numeoinplen = 0
  local numinpinx = 0 -- ZERO-based source index
  local numknar0k = 0 -- current char
  local numknaf1x = 0 -- next char (ZERO is NOT valid)
  local numknaf2x = 0 -- post next char (ZERO is NOT valid)
  local boonext1x = false
  local boonext2x = false
  local boosudahdone = false

  numeoinplen = string.len(streosurr)

  while true do

    if (numinpinx>=numeoinplen) then
      break
    end--if

    numknar0k = string.byte(streosurr,(numinpinx+1),(numinpinx+1))
    numknaf1x = 0 -- preASSume no char
    numknaf2x = 0 -- preASSume no char
    if ((numinpinx+1)<numeoinplen) then
      numknaf1x = string.byte(streosurr,(numinpinx+2),(numinpinx+2))
    end--if
    if ((numinpinx+2)<numeoinplen) then
      numknaf2x = string.byte(streosurr,(numinpinx+3),(numinpinx+3))
    end--if

    boonext1x = ((numknaf1x==88) or (numknaf1x==120)) -- case insensitive
    boonext2x = ((numknaf2x==88) or (numknaf2x==120)) -- case insensitive
    boosudahdone = false
    if (boonext1x and boonext2x) then -- got "xx"
      strutf8eo = strutf8eo .. string.char(numknar0k,numknaf1x) -- keep one "x" only
      numinpinx = numinpinx + 3 -- eaten 3 written 2
      boosudahdone = true
    end--if
    if (boonext1x and (not boonext2x)) then -- got yes-"x" and no-"x"
      vareopeek = contabtransluteo[numknar0k] -- UINT16 or type "nil"
      if (type(vareopeek)=='number') then
        strutf8eo = strutf8eo .. string.char(mathdiv(vareopeek,256),mathmod(vareopeek,256)) -- add UTF8 char
        numinpinx = numinpinx + 2 -- eaten 2 written 2
        boosudahdone = true
      end--if
    end--if
    if (not boosudahdone) then
      strutf8eo = strutf8eo .. string.char(numknar0k) -- copy char
      numinpinx = numinpinx + 1 -- eaten 1 written 1
    end--if

  end--while

  return strutf8eo

end--function lfikodeosg

------------------------------------------------------------------------

-- Local function LFIVARILINGVONOMO

-- Input  : * strlang29name : such as "Ido" or "dana" or "samea suda"
--          * numtip29mon   : 0 native -- 1 la (AJ SB) -- 2 aj -- 3 sf (root)
--                            4 av (AV real or surrogate) -- 5 pl (AJ and PL)
--          * boomaju

-- Depends on functions :
-- [U] lfulnutf8char lfcaserest lfucasestr
-- [G] lfgtestuc lfgtestlc
-- [E] mathdiv mathmod mathbitwrit

-- incoming multiword SB such as "Malnova Volapuko" is bad, we
-- can't process such

-- note that for "numtip29mon" 1 ie "la" we will add the article to
-- native AJ only, never to native SB (but type 4 ie "av" can force AJ
-- and subsequently add "la")

-- note that we MUST NOT change the word class to AJ
-- for "numtip29mon" 4 ie "av" if multiword

-- for "numtip29mon" 5 ie "pl" we change the word class to AJ too

local function lfivarilingvonomo (strlang29name, numtip29mon, boomaju)

  local strpluralizator = ''
  local num29lon     = 0
  local num29plindex = 0
  local nummychar    = 0
  local nummyches    = 0
  local booisnoun    = false
  local boomulwords  = false
  local booaddprepen = false
  local booaddartla  = false

  if ((numtip29mon>=1) and (numtip29mon<=5)) then

    num29lon = string.len (strlang29name)
    nummychar = string.byte (strlang29name,num29lon,num29lon) -- last

    booisnoun = (nummychar==111) -- "o"
    boomulwords = (string.find (strlang29name, ' ', 1, true)~=nil) -- plain tx
    booaddprepen = (numtip29mon==4) and boomulwords -- surrogate AV with "en"
    booaddartla = (booisnoun==false) and ((numtip29mon==1) or booaddprepen)

    if ((numtip29mon>=2) and (booaddprepen==false)) then
      strlang29name = string.sub (strlang29name,1,-2) -- cut off last letter
      strlang29name = lfucasestr (strlang29name,false,false,2) -- lower it
    end--if
    if (booaddartla) then
      strlang29name = 'la ' .. strlang29name -- we will NOT pluralize this
    end--if
    if ((numtip29mon==2) or (numtip29mon==5)) then
      strlang29name = strlang29name .. "a" -- AJ (pluralize later if needed)
    end--if

    if (numtip29mon==4) then
      if (boomulwords) then
        strlang29name = "en " .. strlang29name -- surrog AV from PP "en" + ...
      else
        strlang29name = strlang29name .. "e" -- AV
      end--if
    end--if

    if (numtip29mon==5) then
      if (boomulwords) then
        strpluralizator = strlang29name
        strlang29name = '' -- we will rebuild it
        num29lon = string.len (strpluralizator)
        num29plindex = 0 -- ZERO-based
        while true do -- pluralize all words except the last one
          if (num29plindex==num29lon) then
            break
          end--if
          nummychar = string.byte (strpluralizator,(num29plindex+1),(num29plindex+1))
          num29plindex = num29plindex + 1 -- ZERO-based
          nummyches = 0
          if (num29plindex~=num29lon) then
            nummyches = string.byte (strpluralizator,(num29plindex+1),(num29plindex+1))
          end--if
          if ((nummychar==97) and (nummyches==32)) then
            strlang29name = strlang29name .. 'aj '
            num29plindex = num29plindex + 1 -- eaten 2 char:s, written 3
          else
            strlang29name = strlang29name .. string.char(nummychar) -- copy ch
          end--if
        end--while
      end--if (boomulwords) then
      strlang29name = strlang29name .. "j" -- pluralize last or only one AJ
    end--if (numtip29mon==5) then

  end--if ((numtip29mon>=1) and (numtip29mon<=5)) then

  if (boomaju) then
    strlang29name = lfucasestr (strlang29name,true,false,2) -- upper, one, eo
  end--if

  return strlang29name

end--function lfivarilingvonomo

------------------------------------------------------------------------

---- HIGH LEVEL FUNCTIONS [H] ----

------------------------------------------------------------------------

-- Local function LFHCONSTRUCTERAR

-- Input  : * numerar6code -- 1 ... 99 or 2 ... 99 invalid type ignored
--          * boopeek6it

-- Depends on functions :
-- [N] lfnumto2digit
-- [E] mathisintrange mathdiv mathmod

-- Depends on constants :
-- * maybe table contaberaroj TWO-based (holes permitted)

-- To be called ONLY from lfhbrewerror, lfhbrewerrsm,
-- lfhbrewerrsvr, lfhbrewerrinsi.

local function lfhconstructerar (numerar6code, boopeek6it)
  local vardes6krip = 0
  local numbottom6limit = 1
  local stryt6sux = '#E'
  if (type(numerar6code)~='number') then
    numerar6code = 0 -- invalid
  end--if
  if (boopeek6it) then
    numbottom6limit = 2 -- #E01 is a valid code for submodule only
  end--if
  if (mathisintrange(numerar6code,numbottom6limit,99)) then
    stryt6sux = stryt6sux .. lfnumto2digit(numerar6code)
    if (boopeek6it) then
      vardes6krip = contaberaroj[numerar6code] -- risk of type "nil"
      if (type(vardes6krip)=='string') then
        stryt6sux = stryt6sux .. ' ' .. vardes6krip
      else
        stryt6sux = stryt6sux .. ' ??' -- no text found
      end--if
    end--if (boopeek6it) then
  else
    stryt6sux = stryt6sux .. '??' -- no valid error code
  end--if
  return stryt6sux

end--function lfhconstructerar

------------------------------------------------------------------------

-- Local function LFHBREWERROR

-- Input  : * numerar7code -- 2 ... 99

-- Depends on functions :
-- [H] lfhconstructerar
-- [N] lfnumto2digit
-- [E] mathisintrange mathdiv mathmod

-- Depends on constants :
-- * 3 strings constrelabg constrelaen constrlaxhu
-- * table contaberaroj TWO-based (holes permitted)

-- #E02...#E99, note that #E00 and #E01 are NOT supposed to be included here.

local function lfhbrewerror (numerar7code)
  local stryt7sux = ''
  stryt7sux = constrlaxhu .. constrelabg .. lfhconstructerar (numerar7code,true) .. constrelaen .. constrlaxhu
  return stryt7sux
end--function lfhbrewerror
------------------------------------------------------------------------

-- Local function LFHFILLSURRSTRTAB

-- Process (fill in, transcode surr) either a single string, or all string
-- items in a table (even nested) using any type of keys/indexes (such as
-- a holy number sequence and non-numeric ones). Items with a non-string
-- value are kept as-is. For filling in own name, and converting eo and
-- NOPE sv surrogates (via 3 separate sub:s).

-- Input  : * varinkommen -- type "string" or "table"
--          * varfyllo -- string, or type "nil" if no filling-in desired
--          * strlingkod -- "eo" or NOPE "sv" to convert surrogates, anything
--                          else (preferably type "nil") to skip this

-- Depends on functions :
-- [I] lfifillname (only if filling-in desired)
-- [I] lfikodeosg (only if converting of eo X-surrogates desired)
-- NOPE [I] lfikodsvsg
-- [E] mathdiv mathmod (via "lfikodeosg" and NOPE "lfikodsvsg")

-- Depends on constants :
-- * table "contabtransluteo" inherently holy (via "lfikodeosg")
-- NOPE * table "contabtranslutsv"

local function lfhfillsurrstrtab (varinkommen, varfyllo, strlingkod)

  local varkey = 0 -- variable without type
  local varele = 0 -- variable without type
  local varutmatning = 0
  local boodone = false

  if (type(varinkommen)=='string') then
    if (type(varfyllo)=='string') then
      varinkommen = lfifillname (varinkommen,varfyllo) -- fill-in
    end--if
    if (strlingkod=='eo') then
      varinkommen = lfikodeosg (varinkommen) -- surr
    end--if
    -- if (strlingkod=='sv') then
      -- varinkommen = lfikodsvsg (varinkommen) -- surr
    -- end--if
    varutmatning = varinkommen -- copy, risk for no change
    boodone = true
  end--if

  if (type(varinkommen)=='table') then
    varutmatning = {} -- brew new table
    varkey = next (varinkommen) -- try to pick 0:th (in no order) key/index
    while true do
      if (varkey==nil) then
        break -- empty table or end reached
      end--if
      varele = varinkommen[varkey] -- pick element of unknown type
      if ((type(varele)=='string') or (type(varele)=='table')) then
        varele = lfhfillsurrstrtab (varele, varfyllo, strlingkod) -- RECURSION
      end--if
      varutmatning[varkey] = varele -- write at same place in dest table
      varkey = next (varinkommen, varkey) -- try to pick next key/index
    end--while
    boodone = true
  end--if

  if (not boodone) then
    varutmatning = varinkommen -- copy as-is whatever it is
  end--if

  return varutmatning

end--function lfhfillsurrstrtab

------------------------------------------------------------------------

---- VARIABLES [R] ----

------------------------------------------------------------------------

function exporttable.ek (arxframent)

  -- general unknown type

  local vartymp = 0      -- variable without type multipurpose

  -- special type "args" AKA "arx"

  local arxsomons = 0    -- metaized "args" from our own or caller's "frame"

  -- general tab

  local tab78ysubt = {}
  local tab80lefty = {}
  local tabonelang = {} -- subtable for one language

  -- general str

  local strtump     = '' -- temp

  local strpagenam  = '' -- "{{PAGENAME}}" o "pagenameoverridetestonly"
  local strruangna  = '' -- "{{NAMESPACENUMBER}}" o "nsnumberoverridetestonly"
  local strkodbah   = '' -- directly picked or translated from "strnambah"
  local strnambah   = '' -- language name (short)
  local strnambauc  = '' -- language name with uppercased begin letter
  local strnambalc  = '' -- language name with lowercased begin letter
  local strnambala  = '' -- language name (long) with "la " if needed
  local strnambaaj  = '' -- language name (force AJ)
  local strlauxcatip = '' -- N K L
  local strlauxcatnm = '' -- name of extra cat (exp or brewed from parent cat)
  local strcol6q     = '' -- subdomain, "-" same, "??" stolen
  local strcol7q     = '' -- status 2 digits
  local straliawk    = ''
  local strenkonduko = '' -- begin of text with type by content
  local straldonsh   = '' -- link to appendix page (short)
  local straldonlg   = '' -- link to appendix page (long)
  local striconbc    = '' -- picked based on "G" or "H" or "U"
  local strdescbc    = '' -- picked based on "G" or "H" or "U"
  local strtypeof    = '' -- type of cat

  local strvisgud   = '' -- visible output on success
  local strinvkat   = '' -- invisible category part on success
  local strviserr   = '' -- visible error message on error
  local strtrakat   = '' -- invisible tracking categories on success or error
  local strret      = '' -- final result string

  -- general num

  local numerr       = 0 -- 1 internal
  local num2statcode = 0
  local numlevel     = 0 -- ASCII thus 48...52 or 69 for "D"
  local numtamp      = 0

  -- general boo

  local boohidden     = false -- whether to hide the cat
  local boolangcat    = false
  local bootaroexists = false
  local boocodestolen = false
  local booteemp      = false

------------------------------------------------------------------------

---- MAIN [Z] ----

------------------------------------------------------------------------

  ---- GUARD AGAINST INTERNAL ERROR AGAIN ----

  -- later reporting of #E01 may NOT depend on uncommentable strings

  lftracemsg ('This is "kat", requested "detrc" report')

  if (qbooguard) then
    numerr = 1 -- #E01 internal
  end--if

  ---- PROCESS MESSAGES, FILL IN ALWAYS, SURR ONLY IF NEEDED ----

  -- placeholder "\@" "\\@" is replaced by augmented name of the caller
  -- from "constrkoll" in any case, for example 'sxablono "test"' or
  -- 'templat "test"'

  -- only for some languages the surr-transcoding is subsequently performed

  if (numerr==0) then
    contaberaroj = lfhfillsurrstrtab (contaberaroj, constrkoll, constrpriv)
    contabboasting = lfhfillsurrstrtab (contabboasting, nil, constrpriv) -- no filling
    contabtypesofcats = lfhfillsurrstrtab (contabtypesofcats, nil, constrpriv) -- nested, no filling
    contabtracking = lfhfillsurrstrtab (contabtracking, nil, constrpriv) -- no filling
    contabdesc = lfhfillsurrstrtab (contabdesc, nil, constrpriv) -- no filling
  end--if

  lfdshowvar (numerr,'numerr','messages processed')

  ---- PICK TWO SUBTABLES FROM ONE IMPORT ----

  -- on error we assign "numerr" (used far below) and "num2statcode"

  while true do -- fake loop

    if (numerr~=0) then
      break -- to join mark
    end--if

    num2statcode = qldingvoj[2]
    if (type(num2statcode)~='number') then -- important check
      numerr = 2 -- #E02 malica
      break -- to join mark
    end--if
    if (num2statcode~=0) then
      if (mathisintrange(num2statcode,2,79)) then
        numerr = 3 -- #E03 nombrigita
      else
        numerr = 2 -- #E02 malica
      end--if
      break -- to join mark
    end--if

    tab78ysubt = qldingvoj['T78']
    if (type(tab78ysubt)~='table') then -- important check
      numerr = 2 -- #E02 malica
      break -- to join mark
    end--if
    tab80lefty = qldingvoj['T80']
    if (type(tab80lefty)~='table') then -- important check
      numerr = 2 -- #E02 malica
      break -- to join mark
    end--if

    break -- finally to join mark
  end--while -- fake loop -- join mark

  lfdshowvar (numerr,'numerr','after attempt to seize tables "T78" "T80"')
  lfdshowvar (num2statcode,'num2statcode')

  ---- GET THE ARX (ONE OF TWO) ----

  -- must be seized independently on "numerr" even if we already suck

  -- give a f**k in possible params other than "caller=true"

  arxsomons = arxframent.args -- "args" from our own "frame"
  if (type(arxsomons)~='table') then
    arxsomons = {} -- guard against indexing error from our own
    numerr = 1 -- #E01 internal
  end--if
  if (arxsomons['caller']=='true') then
    arxsomons = arxframent:getParent().args -- "args" from caller's "frame"
  end--if
  if (type(arxsomons)~='table') then
    arxsomons = {} -- guard against indexing error again
    numerr = 1 -- #E01 internal
  end--if

  ---- PROCESS 3 HIDDEN NAMED PARAMS ----

  -- this may override "mw.title.getCurrentTitle().text" and
  -- stipulate content in "strpagenam", empty is NOT valid

  -- bad "pagenameoverridetestonly=" can give #E01
  -- no error is possible from other hidden parameters

  -- "detrc=" must be seized independently on "numerr"
  -- even if we already suck, but type "table" must be ensured above !!!

  strpagenam = ''
  if (numerr==0) then
    vartymp = arxsomons['pagenameoverridetestonly']
    if (type(vartymp)=='string') then
      numtamp = string.len(vartymp)
      if ((numtamp>=1) and (numtamp<=120)) then
        strpagenam = vartymp -- empty is not legal
      else
        numerr = 1 -- #E01 internal
      end--if
    end--if
  end--if

  strruangna = ''
  if (numerr==0) then
    vartymp = arxsomons['nsnumberoverridetestonly']
    if (type(vartymp)=='string') then
      numtamp = string.len(vartymp)
      if ((numtamp>=1) and (numtamp<=4)) then
        strruangna = vartymp -- empty is not legal but ignore crime
      end--if
    end--if
  end--if

  if (arxsomons['detrc']=='true') then
    lftracemsg ('Param "detrc=true" seized')
  else
    qboodetrc = false -- was preassigned to "true"
    qstrtrace = '' -- shut up now
  end--if

  lfdshowvar (numerr,'numerr','done with hidden params')

  ---- SEIZE THE PAGENAME FROM MW ----

  -- later reporting of #E01 may NOT depend on uncommentable strings

  -- must be 1...120 octet:s keep consistent with "pagenameoverridetestonly="

  if ((numerr==0) and (strpagenam=='')) then
    vartymp = mw.title.getCurrentTitle().text -- without namespace prefix
    if (type(vartymp)=='string') then -- this can leave bhd "strpagenam" empty
      numtamp = string.len(vartymp)
      if ((numtamp>=1) and (numtamp<=120)) then
        strpagenam = vartymp -- pagename here (empty is NOT legal)
      else
        numerr = 1 -- #E01 internal
      end--if
    end--if
  end--if

  if (numerr==0) then
    if (strpagenam=='') then
      numerr = 1 -- #E01 internal
    end--if
  end--if

  lfdshowvar (numerr,'numerr','after seizure of pagename')
  lfdshowvar (strpagenam,'strpagenam')

  ---- SEIZE THE NAMESPACE FROM MW AND CHECK IT ----

  if ((numerr==0) and (strruangna=='')) then
    vartymp = mw.title.getCurrentTitle().namespace -- this is a number
    if (type(vartymp)=='number') then -- this can leave bhnd "strruangna" empty
      if ((vartymp>=0) and (vartymp<=9999)) then
        strruangna = tostring(vartymp) -- empty is not legal but ignore crime
      end--if
    end--if
  end--if

  if ((numerr==0) and (strruangna~='14')) then
    numerr = 8 -- #E08 ns
  end--if

  lfdshowvar (numerr,'numerr','after seizure of namespace')
  lfdshowvar (strruangna,'strruangna')

  ---- DISSECT THE PAGENAME ----

  -- the pagename comes in "strpagenam"

  -- "contabtypesofcats" (string keys) has subtables (ONE-based keys)
  -- * [1] parsing method (3 -> sepbracket | 4,5,6)
  -- * [2] pattern for parsing method
  -- * [3] reserved
  -- * [4] (char) type by conent G H U
  -- * [5] (boo) categorize in lang
  -- * [6] (char) categorize lang N K L (empty string to deactive)
  -- * [7] name of extra cat

  -- here we do NOT validate the language name in any way beyond length >= 2

  -- things like "Tradukoj (**)" will pass here, will get caught below

  -- "Tradukoj (Esperanto)" will pass here and will pass the validation
  -- of language name too, this must be caught even later

  -- things like "El la **" will pass here, will get caught below

  -- things like "El la Ido" will pass here and will pass the validation
  -- of language name too, this must be caught even later

  -- output from here (risk for #E01 and #E06):
  -- * strnambah or strkodbah   (from pagename)
  -- * numlevel                 (from pagename)
  -- * strtypeof                (from table "key", string with 4 char:s)
  -- * boolangcat               (from [5])
  -- * boohidden                (indirectly from [4])
  -- * striconbc and strdescbc  (indirectly from [4])
  -- * strlauxcatip             (from [6] N K L can be empty if none)
  -- * strlauxcatnm             (from [7] can be empty if none)

  if (numerr==0) then

    strtypeof = ''

    do -- scope
      local strdebin = '' -- from "lfisepbracket" can be needed
      local strdebut = '' -- from "lfisepbracket" can be needed
      local strau2tr = '' -- pattern from [2]
      local strby4content = '' -- from [4] "G" or "H" or "U"
      local numpanalength = 0
      local numpars1met = 0
      local numverlastch = 0
      local numprelastch = 0
      local boodebrako = false
      local booparsdan = false

      numpanalength = string.len(strpagenam)
      numverlastch = string.byte(strpagenam,-1,-1)
      if (numpanalength>=2) then
        numprelastch = string.byte(strpagenam,-2,-2)
      end--if
      boodebrako, strdebin, strdebut = lfisepbracket (strpagenam, 2)

      for k7k,v7v in pairs(contabtypesofcats) do
        if ((type(k7k)~='string') or (type(v7v)~='table')) then
          lftracemsg ('Aborting "for in pairs" due to broken internal table (k,v)')
          numerr = 1 -- #E01 internal
          break
        end--if
        lfdshowvar (k7k,'k7k','iterating over possible patterns')
        booparsdan = false -- reset on every iter
        numpars1met = v7v[1]
        strau2tr = v7v[2] -- empty if not used
        strby4content = v7v[4] -- G H U
        if ((type(strau2tr)~='string') or (type(strby4content)~='string')) then
          lftracemsg ('Aborting "for in pairs" due to broken internal table (pos 2,4)')
          numerr = 1 -- #E01 internal
          break
        end--if
        boohidden = (strby4content=='H')
        striconbc = contabicon [strby4content]
        strdescbc = contabdesc [strby4content]
        if ((type(striconbc)~='string') or (type(strdescbc)~='string')) then
          lftracemsg ('Aborting "for in pairs" due to broken table "GHU-icon-desc"')
          numerr = 1 -- #E01 internal
          break
        end--if
        boolangcat = v7v[5]
        strlauxcatip = v7v[6] -- N K L empty if not desired
        strlauxcatnm = v7v[7] -- empty if not used or brewed from parent cat
        if (numpars1met==3) then -- based on lfisepbracket
          booparsdan = true
          lftracemsg ('Trying parsing method 3')
          if (boodebrako and ((strau2tr..' ()')==strdebut)) then
            strtypeof = k7k
            strnambah = strdebin
            lfdshowvar (strtypeof,'strtypeof','L&G we found h*m by method 3')
            break
          end--if
        end--if
        if (numpars1met==4) then -- "el"-type
          booparsdan = true
          lftracemsg ('Trying parsing method 4')
          if ((numpanalength>=8) and (string.sub(strpagenam,1,6)=='El la ')) then
            strtypeof = k7k
            strnambah = string.sub(strpagenam,7,-1)
            lfdshowvar (strtypeof,'strtypeof','L&G we found h*m by method 4 with "la"')
            break
          end--if
          if ((numpanalength>=5) and (string.sub(strpagenam,1,3)=='El ')) then
            strtypeof = k7k
            strnambah = string.sub(strpagenam,4,-1)
            lfdshowvar (strtypeof,'strtypeof','L&G we found h*m by method 4 without "la"')
            break
          end--if
        end--if
        if (numpars1met==5) then -- isto with level "Vikivortaristo mi-D"
          booparsdan = true
          lftracemsg ('Trying parsing method 5')
          if (((numpanalength==19) or (numpanalength==20)) and (string.sub(strpagenam,1,15)=='Vikivortaristo ') and (numprelastch==45)) then
            if (((numverlastch>=48) and (numverlastch<=52)) or (numverlastch==68)) then
              strtypeof = k7k
              if (numpanalength==19) then
                strkodbah = string.sub(strpagenam,16,17) -- 2-letter code
              else
                strkodbah = string.sub(strpagenam,16,18) -- 3-letter code
              end--if
              numlevel = numverlastch -- ASCII
              lfdshowvar (strtypeof,'strtypeof','L&G we found h*m by method 5')
              break
            end--if
          end--if
        end--if
        if (numpars1met==6) then -- isto without level
          booparsdan = true
          lftracemsg ('Trying parsing method 6')
          if (((numpanalength==17) or (numpanalength==18)) and (string.sub(strpagenam,1,15)=='Vikivortaristo ')) then
            strtypeof = k7k
            if (numpanalength==17) then
              strkodbah = string.sub(strpagenam,16,17) -- 2-letter code
            else
              strkodbah = string.sub(strpagenam,16,18) -- 3-letter code
            end--if
            lfdshowvar (strtypeof,'strtypeof','L&G we found h*m by method 6')
            break
          end--if
        end--if
        if (not booparsdan) then
          lftracemsg ('Aborting "for in pairs" due to unknown parsing method')
          numerr = 1 -- #E01 internal
          break
        end--if
      end--for

      if (strtypeof=='') then -- NOT found
        numerr = 6 -- #E06 no valid pattern
        lftracemsg ('Exited "for in pairs" with pattern NOT found')
      end--if

    end--do scope

  end--if

  lfdshowvar (numerr,'numerr','after dissection')
  lfdshowvar (boohidden,'boohidden')
  lfdshowvar (strkodbah,'strkodbah','maybe lang code is here')
  lfdshowvar (strnambah,'strnambah','maybe lang name is here')

  ---- REVERSE-QUERY NAME -> CODE IF NEEDED ----

  if ((numerr==0) and (strkodbah=='')) then
    strkodbah = tab80lefty[strnambah]
    if (type(strkodbah)~='string') then
      numerr = 7 -- #E07 valid pattern but invalid language
    end--if
  end--if

  lfdshowvar (numerr,'numerr','after reverse query via T80')
  lfdshowvar (strkodbah,'strkodbah','maybe picked here')

  ---- ALWAYS PREPARE FOR FORWARD QUERIES ----

  -- error when picking tabonelang should be impossible
  -- if strkodbah comes from tab80lefty

  if (numerr==0) then
    tabonelang = tab78ysubt[strkodbah] -- get subtable from T78
    if (type(tabonelang)~='table') then
      numerr = 7 -- #E07 valid pattern but invalid language
      tabonelang = {} -- guard against indexing error
    end--if
  end--if

  ---- PERFORM LAST STEP OF FORWARD QUERY IF NEEDED ----

  if ((numerr==0) and (strnambah=='')) then
    strnambah = tabonelang[0] -- /c0/
    if (type(strnambah)~='string') then
      numerr = 1 -- #E01 internal (should be impossible)
    end--if
  end--if

  lfdshowvar (numerr,'numerr','after forward query via "tabonelang"')
  lfdshowvar (strnambah,'strnambah','maybe picked here')

  ---- REASSIGN WORD CLASS AND LETTER CASE ----

  if (numerr==0) then
    strnambauc = lfucasestr (strnambah,true,false,2) -- uppercase
    strnambalc = lfucasestr (strnambah,false,false,2) -- lowecase for the hint
    strnambala = lfivarilingvonomo (strnambah,1,false) -- long with "la "
    strnambaaj = lfivarilingvonomo (strnambah,2,false) -- adjective yes we can
  end--if

  lfdshowvar (strnambauc,'strnambauc')
  lfdshowvar (strnambalc,'strnambalc')
  lfdshowvar (strnambala,'strnambala')
  lfdshowvar (strnambaaj,'strnambaaj')

  ---- CATCH SOME FAULTY PAGENAMES ----

  -- incoming "strtypeof" and "strpagenam"

  -- "Tradukoj (Esperanto)" is illegal

  -- things like "El la Ido" or "El hispana" are illegal

  if (numerr==0) then
    while true do -- fake loop
      if (strpagenam=='Tradukoj (Esperanto)') then
        lftracemsg ('rejecting "Tradukoj (Esperanto)"')
        numerr = 6 -- #E06 no valid pattern
        break
      end--if
      strtump = "El " .. strnambala
      if ((strtypeof=='elel') and (strpagenam~=strtump)) then
        lftracemsg ('rejecting malformed "el"-type due to "' .. strpagenam .. '" <> "'.. strtump .. '"')
        numerr = 6 -- #E06 no valid pattern
        break
      end--if
      break -- finally to join mark
    end--while -- fake loop -- join mark
  end--if

  ---- PICK COLUMNS 6 AND 7 AND BREW LINK IF NEEDED ----

  -- this gives "straliawk" raw link and "bootaroexists" and "boocodestolen"

  if ((numerr==0) and (strtypeof=='varo')) then

    strcol6q = tabonelang[6] -- subdomain ie non-standard code /c6/
    if ((type(strcol6q)~='string') or (strcol6q=='-')) then
      strcol6q = strkodbah -- then ASSume same
    end--if
    strcol7q = tabonelang[7] -- status 2 digits /c7/
    if (type(strcol7q)~='string') then
      strcol7q = '00' -- ASSume nope
    end--if
    boocodestolen = (strcol6q=='??')
    if (not boocodestolen) then
      bootaroexists = ((string.byte(strcol7q,2,2))==50) -- got a "2" there ??
      straliawk = 'http://' .. strcol6q .. '.wiktionary.org' -- even if inexis
    end--if

    lftracemsg ('2 queries via T78 done')
    lfdshowvar (straliawk,'straliawk')
    lfdshowvar (bootaroexists,'bootaroexists')
    lfdshowvar (boocodestolen,'boocodestolen')

  end--if ((numerr==0) and (strtypeof=='varo')) then

  ---- PREBREW INTRO AND LINK TO APPENDIX PAGE ----

  -- incoming "strdescbc" "strkodbah" "strnambah" "strnambauc" "strnambala"

  -- string "constrapxq" is appendix prefix and excludes the colon ":"

  -- result "strenkonduko" will look like (indeed space at end)
  -- "En aux sub cxi tiu kategorio trovigxas "
  -- or
  -- "En aux sub cxi tiu uzanta kategorio trovigxas "

  -- result "straldonsh" will look like (no space at end)
  -- "<b>[[Aldono:Dana|dana]]</b> (lingvokodo <code>-da-</code>)"

  -- result "straldonlg" will look like (no space at end)
  -- "<b>[[Aldono:Dana|la dana]]</b> (lingvokodo <code>-da-</code>)"

  if (numerr==0) then
    if (strdescbc~='') then
      strdescbc = strdescbc .. ' ' -- below insert extra word incl space after
    end--if
    strenkonduko = contabboasting['g0'] .. ' ' .. strdescbc .. contabboasting['g1'] .. ' '
    strtump = '</b> (' .. contabboasting['g2'] .. ' <code>-' .. strkodbah .. '-</code>)'
    straldonsh = '<b>' .. lfikatpaldigu(constrapxq,strnambauc,strnambah,0) .. strtump -- (short)
    straldonlg = '<b>' .. lfikatpaldigu(constrapxq,strnambauc,strnambala,0) .. strtump -- (long)
  end--if (numerr==0) then

  ---- PREBREW TEXT ----

  -- this is controlled by "strtypeof"
  -- incoming also "striconbc" "strdescbc"
  -- further "numlevel"

  -- this is the latest occasion where #E01 can arise

  if (numerr==0) then

    booteemp = false

    if (strtypeof=='elel') then -- "el"-type
      strtump = lfikatpaldigu(constrkatq,'Deveno ('..strnambah..')','',2) -- colon
      strboxtext = strenkonduko .. contabboasting['elel0'] .. ' ' .. straldonlg .. '. ' .. contabboasting['elel1'] .. ' ' .. strtump .. '.'
      booteemp = true
    end--if

  if (strtypeof=='stil') then -- stilo
    strboxtext = strenkonduko .. contabboasting['stil0'] .. ' ' .. straldonsh .. ' ' .. contabboasting['stil1'] .. '.'
    booteemp = true
  end--if

  if (strtypeof=='trad') then -- tradukoj
    strboxtext = strenkonduko .. contabboasting['trad'] .. ' ' .. straldonsh .. '.'
    booteemp = true
  end--if

  if (strtypeof=='varo') then -- vortaro
    strboxtext = strenkonduko .. contabboasting[30] .. ' ' .. straldonsh .. '.'
    if (strkodbah=='eo') then
      strtump = contabboasting[31] .. '.'
    else
      if (bootaroexists) then
        strtump = '[' .. straliawk .. ' ' .. contabboasting[32] .. ' ' .. strnambaaj .. ' ' .. contabboasting[34] .. ']'
      else
        strtump = contabboasting[33] .. ' ' .. strnambaaj .. ' ' .. contabboasting[34]
        if (boocodestolen) then
          strtump = strtump .. ', ' .. contabboasting[36] .. '.'
        else
          strtump = strtump .. '. ([' .. straliawk .. ' ' .. contabboasting[35] ..'])'
        end--if
      end--if (bootaroexists) else
    end--if
    strboxtext = strboxtext .. '<br><br><div style="text-align:center;">' .. strtump .. '</div>'
    booteemp = true
  end--if (strtypeof=='varo') then

  if (strtypeof=='isnl') then
    strboxtext = strenkonduko .. contabboasting['isnl'] .. ' ' .. straldonlg .. '.'
    booteemp = true
  end--if

  if (strtypeof=='islv') then
    strboxtext = strenkonduko .. contabboasting['islv0'] .. ' ' .. straldonlg .. ' ' .. contabboasting['islv1'] .. ' "' .. string.char(numlevel) .. '".'
    strlauxcatnm = string.sub(strpagenam,1,-3) -- cut off 2 letters
    booteemp = true -- "strlauxcatnm" is generated from parent cat
  end--if

    if (not booteemp) then
      numerr = 1 -- #E01 internal
      lfdshowvar (strtypeof,'strtypeof','inconsistent, failed to brew box text, #E01')
    end--if

  end--if (numerr==0) then

  ---- WHINE IF YOU MUST #E01 ----

  -- reporting of this error #E01 must NOT depend on
  -- uncommentable stuff such as "constrkoll" and "contaberaroj"

  -- do NOT use sub "lfhbrewerror", report our name (NOT of template), in EN

  if (numerr==1) then
    strtump = '#E01 Internal error in module "kat".'
    strviserr = constrlaxhu .. constrelabg .. strtump .. constrelaen .. constrlaxhu
  end--if

  ---- WHINE IF YOU MUST #E02...#E99 ----

  -- reporting of errors #E02...#E99 depends on uncommentable strings
  -- and name of the caller filled in from "constrkoll"

  if (numerr>1) then
    strviserr = lfhbrewerror(numerr)
  end--if

  ---- BREW THE VISIBLE BOX PART AROUND THE TEXT ----

  -- incoming "striconbc" and "strboxtext"

  if (numerr==0) then
    strvisgud = contabhtml[0] .. contabhtml[3] .. striconbc .. contabhtml[4] .. strboxtext .. contabhtml[5]
  end--if

  ---- BREW THE INVISIBLE CATEGORY PART ----

  -- incoming:
  -- * "boolangcat" and "strlauxcatip" and "strlauxcatnm"
  -- * "strnambah" and "strnambauc" and "strnambalc"
  -- * "numlevel"
  -- * "boohidden"

  -- string "constrkatq" is cat prefix and excludes the colon ":"

  -- brew "__HIDDENCAT__" if needed as well as up to 2 category insertions

  if (numerr==0) then
    do -- scope
      local strkeysorthint = ''
      local boobrewthatone = false
      if (boolangcat) then -- cat insertion no hint
        strinvkat = lfikatpaldigu(constrkatq,strnambauc,'',1)
      end--if
      if (strlauxcatip=='N') then -- lang name
       strkeysorthint = lfibrewcathint(strnambah,2,false) -- must clean up
       boobrewthatone = true
      end--if
      if (strlauxcatip=='K') then -- lang code
       strkeysorthint = strkodbah -- no cleanup needed
       boobrewthatone = true
      end--if
      if (strlauxcatip=='L') then -- level
       strkeysorthint = string.char(numlevel) -- no cleanup needed
       boobrewthatone = true
      end--if
      if (boobrewthatone) then -- cat insertion custom hint
        strinvkat = strinvkat .. lfikatpaldigu(constrkatq,strlauxcatnm,strkeysorthint,1)
      end--if
      if (boohidden) then
        strinvkat = '__HIDDENCAT__' .. strinvkat
      end--if
    end--do scope
  end--if (numerr==0) then

  ---- BREW THE TRACKING CATEGORY PART ----

  if (numerr==6) then
    strtrakat = lfikataldigu(constrkatq,contabtracking[0]) -- #E06
  end--if
  if (numerr==7) then
    strtrakat = lfikataldigu(constrkatq,contabtracking[1]) -- #E07
  end--if
  if ((numerr==5) or (numerr==8)) then
    strtrakat = lfikataldigu(constrkatq,contabtracking[2]) .. lfikataldigu(constrkatq,contabtracking[3]) -- #E05 #E08
  end--if

  ---- RETURN THE JUNK STRING ----

  -- on #E02 and higher we risk partial results in "strvisgud" and "strinvkat"

  lftracemsg ('Ready to return string glued together from 1 + 4 parts')
  lfdshowvar (strvisgud,'strvisgud')
  lfdshowvar (strinvkat,'strinvkat')
  lfdshowvar (strviserr,'strviserr')
  lfdshowvar (strtrakat,'strtrakat')

  if (numerr==0) then
    strret = strvisgud .. strinvkat
  else
    strret = strviserr .. strtrakat
  end--if
  if (qboodetrc) then -- "qstrtrace" declared separately outside main function
    strret = '<br>' .. qstrtrace .. '<br><br>' .. strret
  end--if
  return strret

end--function

  ---- RETURN THE JUNK LUA TABLE ----

return exporttable