Modulo:mgrup

El Vikivortaro
Salti al navigilo Salti al serĉilo





--[===[

MODULE "MGRUP" (ligiloj vortfarado vortgrupigado)

"eo.wiktionary.org/wiki/Modulo:mgrup" <!--2021-Mar-xx-->

Purpose: checks categories related to a word or root and brews
         either links or directly lists limited in size

Utilo: inspektas kategoriojn rilatajn al ...

Used by templates / Uzata far sxablonoj / Digunakan oleh templat:
- vfvg (EO)

Required submodules / Bezonataj submoduloj / Submodul yang diperlukan:
- none

Incoming: - one obligatory anonymous parameter
            - language code
          - 0...7 optional anonymous additional parameters
            - morpheme type optionally followed by dash "-" and special code,
              or colon ":" and explicit morpheme, optionally followed by
              percent "%" and script code
              multiple items must be in this order: C I M N P U W
              - special codes:
                - "1" drop last letter
                - "2" lowercase begin
                - "3" lowercase begin and drop last letter
                - "4" uppercase begin
                - "5" uppercase begin and drop last letter
              - explicit morpheme (1...40 octet:s)
              default is "M" and "W" (becomes internally M W Y)
          - 2 optional hidden named parameters
            - "pagenameoverridetestonly="
            - "detrc="

Returned: - one string that is inherently "block-type" and can span
            substantial (but protected from insane) height

Maximally 7 morphemes can be specified, but "W" counts double (can be both
group of words and sentence, both will be showed) and the limit is 9, thus
for example if 7 are given then maximally 2 of them may be "W".

Possible category syntaxes:
* a) M         "Vorto -id- enhavanta morfemon M (api)"        (api, perapian)
* b) M         "Vorto -en- enhavanta morfemon M (sun)"    (sun, Sun, sundown)
* c) C I N P U "Vorto -id- enhavanta morfemon N (endap)"        (pengendapan)
* d) N !!!     "Vorto -eo- enhavanta morfemon N (kapt)"        (kapti, kapto)
* e) W         "Vortgrupo -id- enhavanta (api)"     (kereta api, senjata api)
* f) W         "Vortgrupo -id- enhavanta (kereta api)"   (kereta api,
                                                          stasiun kereta api)
* g) Y         "Frazo -id- enhavanta vorton (api)"       (Ada asap, ada api.)
There is a problem with self-categorization and risk of showing a junk
box containing nothing but a self-link. It is the pagename that can cause
problems, not necessarily the root used in the category name (in case "d)"
they are different). We cannot drop the self-categorization since as
exemplified here they both are crucial and main pages of the category,
but we do not want a box with just one word identical to the pagename
(that is "kapti" or "kapto", not "kapt"). Nor can we drop the
self-categorization of standalone roots as the case "b)" shows.
In the 7 above cases following applies:
* a) M         -> self-categorization occurs
* b) M         -> self-categorization occurs
* c) C I N P U -> mutually exclusive groups, no self-categorization
* d) N !!!     -> self-categorization occurs
* e) W         -> mutually exclusive groups, no self-categorization
* f) W         -> self-categorization occurs
* g) Y         -> mutually exclusive groups, no self-categorization
The problem occurs in 4 of 7 cases namely "a)" "b)" "d)" "f)". The
suboptimal solution is to rise the minimum from 1 to 2 then. Make sure to
self-categorize the lemma (as main page), otherwise there is risk to miss the
box with the category and the link if there is only 1 member other than the
missing main one.

]===]

local grup = {}

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

---- CONSTANTS ----

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

  -- uncommentable EO vs ID constant strings (core site-related features)

  local constrpriv = "eo"                 -- EO (privileged site language)
  -- local constrpriv = "id"                 -- ID (privileged site language)
  local constrkatp = "Kategorio:"         -- EO
  -- local constrkatp = "Kategori:"          -- ID

  -- constant table (ban list)

  -- add only obviously invalid access codes (2-letter or 3-letter)

  -- length of the list is NOT stored anywhere, the processing stops
  -- when type "nil" is encountered

  -- "en.wiktionary.org/wiki/Wiktionary:Language_treatment" excluded languages
  -- "en.wikipedia.org/wiki/Spurious_languages"
  -- "iso639-3.sil.org/code/art" only valid in ISO 639-2
  -- "iso639-3.sil.org/code/zxx" "No linguistic content"

  local contabisbanned = {}
  contabisbanned = {'dc', 'll', 'art','deu','eng','epo','fra','lat','por','rus','spa','swe','tup','zxx'} -- 1...14

  -- constant table (surrogate transcoding table, only needed for EO)

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

  -- constant strings (error circumfixes)

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

  -- constant strings (HTML)

  -- note that '<categorytree>' generates "strip markers" only

  local constrdivxx = '<div style="width:92%; margin-top:0.15em; margin-bottom:0.15em; margin-left:4%; border:1px solid; background-color:#'
  local constrdivbg = 'E8E8FF; padding:0.15em; text-align:center;">'
  local constrdivbk = 'E8FFE8; padding:0.15em; text-align:center;">'
  local constrdiven = '</div>'
  local constrcatbk = '<categorytree mode="pages" hideroot="on" style="column-count:2; background-color:#E8FFE8; font-size:90%; text-align:center;">'
  local constrcaten = '</categorytree>'

  -- uncommentable EO vs ID strings

  local constrbuggerall = ':(neniuj)'     -- EO
  -- local constrbuggerall = ':(tidak ada)'  -- ID

  -- uncommentable EO vs ID string (caller name for error messages)

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

  -- uncommentable EO vs ID constant table (error messages)

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

  local contaberaroj = {}

  contaberaroj[2] = 'Erara uzo de \\@, legu gxian dokumentajxon'           -- EO #E02
  -- contaberaroj[2] = 'Penggunaan salah \\@, bacalah dokumentasinya'         -- ID #E02
  contaberaroj[4] = 'Evidente nevalida lingvokodo en \\@'                  -- EO #E04
  -- contaberaroj[4] = 'Kode bahasa jelas-jelas salah dalam \\@'              -- ID #E04
  contaberaroj[5] = 'Erara uzo de \\@ pro nedevigaj aldonaj parametroj'    -- EO #E05
  -- contaberaroj[5] = 'Penggunaan salah \\@ oleh parameter opsional tambah'  -- ID #E05

  -- constant table (3 integers for preliminary parameter check)

  local contabparam = {}
  contabparam[0] = 1   -- minimal number of anon parameters
  contabparam[1] = 8   -- maximal number of anon parameters (1 + 7)
  contabparam[2] = 160 -- maximal length of single para (min is hardcoded ONE)

  -- constants to control behaviour from source AKA semi-hardcoded parameters

  local conboomiddig = false -- controls lng code checking, assign to "true" to allow middle digit "s7a"

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

---- SPECIAL STUFF OUTSIDE MAIN FUNCTION ----

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

---- VAR:S ----

local qstrtrace = ''     -- 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 ----

if ((type(constrpriv)~="string") or (type(constrkatp)~="string") or (type(constrkoll)~="string")) then
  qbooguard = true
end--if

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

---- ORDINARY LOCAL DEBUG FUNCTIONS ----

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

-- Local function LFTRACEMSG

-- for variables the other sub "lfshowvar" is preferable but in exceptional
-- cases it can be justified to send text containing variables to this sub

-- enhances global "qstrtrace" (may NOT be type "nil")

local function lftracemsg (strbigcrap)
  qstrtrace = qstrtrace .. "<br>" .. strbigcrap .. '.'
end--function lftracemsg

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

-- Local function LFSHOWVAR

-- Show content of a variable or list content of a table with
-- integer indexes starting from ZERO.

-- "vardescri" (defa empty) and "vartablim" (defa 0) are optional

-- enhances global "qstrtrace" (may NOT be type "nil")

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

  local strtype = ''
  local strreport = ''
  local numindax = 0
  local numlencx = 0
  local numsigno = 0

  if (type(vardescri)~="string") then
    vardescri = ''
  else
    vardescri = ' (' .. vardescri .. ')'
  end--if
  if (type(vartablim)~="number") then
    vartablim = 0 -- completely deactivates listing of a table
  end--if
  strname = '"' .. strname .. '"' .. vardescri -- combo

  strtype = type(vardubious)
  if ((strtype=="table") and (vartablim~=0)) then
    strreport = 'Table ' .. strname .. ' :'
    numindax = 0
    while (true) do
      if (numindax>vartablim) then
        break -- done table
      end--if
      strreport = strreport .. ' ' .. tostring (numindax) .. ' -> "' .. tostring (vardubious[numindax]) .. '"'
      numindax = numindax + 1
    end--while
  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 "'
        numindax = 1
        while (true) do
          if (numindax>numlencx) then
            break -- done string char after char
          end--if
          numsigno = string.byte (vardubious,numindax,numindax)
          if ((numsigno<36) or (numsigno>122) or (numsigno==91) or (numsigno==93) or (numsigno==42) or (numsigno==58)) then
            strreport = strreport .. '{' .. tostring (numsigno) .. '}'
          else
            strreport = strreport .. string.char (numsigno)
          end--if
          if ((numlencx>50) and (numindax==20)) then
            numindax = numlencx - 20 -- jump
            strreport = strreport .. '" ... "'
          else
            numindax = numindax + 1
          end--if
        end--while
        strreport = strreport .. '"' -- don't forget final quot
      end--if (numlencx~=0) then
    end--if
    if ((strtype~="table") and (strtype~="string") and (strtype~="nil")) then
      strreport = strreport .. ' and content "' .. tostring (vardubious) .. '"'
    end--if
  end--if
  qstrtrace = qstrtrace .. "<br>" .. strreport .. '.'

end--function lfshowvar

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

---- ORDINARY LOCAL MATH FUNCTIONS ----

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

local function mathdiv (xdividend, xdivisor)
  local resultdiv = 0 -- DIV operator lacks in LUA :-(
  resultdiv = math.floor (xdividend / xdivisor)
  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

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

---- ORDINARY LOCAL STRING FUNCTIONS ----

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

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

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

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

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

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

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

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

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

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

---- ORDINARY LOCAL CONVERSION FUNCTIONS ----

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

-- This sub depends on "MATH FUNCTIONS"\"mathdiv"
-- and "MATH FUNCTIONS"\"mathmod".

local function lfnumto2digit (numzerotoninetynine)
  local strtwodig = ''
  strtwodig = mathdiv(numzerotoninetynine,10) .. mathmod(numzerotoninetynine,10)
  return strtwodig
end--function lfnumto2digit

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

---- ORDINARY LOCAL UTF8 FUNCTIONS ----

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

-- Local function LFUTF8LENGTH

-- Measure length of a single UTF8 char, return ZERO if invalid.

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

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

-- Output : * numlen1234x (1...4 or ZERO if invalid)

local function lfutf8length (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 lfutf8length

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

-- Local function LFCASEGENE

-- Adjust case of a single letter (generous), limited unicode support
-- with some common UTF8 ranges.

-- Input  : * strucinut : single unicode letter (1 or 2 octet:s)
--          * booucas   : for desired uppercase "true" and for
--                        lowercase "false"

-- Output : * strucinut : (same var, unchanged if input is
--                         empty or unknown or invalid)

-- * in ASCII lowercase is $20 above uppercase, b5 reveals
--   the case (1 is upper)
-- * the same is valid in $C3-block
-- * this is NOT valid in $C4-$C5-block, lowercase is usually 1 above
--   uppercase and nothing reveals the case reliably
-- * case delta can be 1 or $20 or $50 other
-- * lowercase is usually above uppercase but not always
-- * case pair distance can span $40-boundary or even $0100-boundary

-- $C2-block $0080 $C2,$80 ... $00BF $C2,$BF no letters (OTOH NBSP mm)

-- $C3-block $00C0 $C3,$80 ... $00FF $C3,$BF (SV mm) delta $20 UC-LC-UC-LC
-- upper $00C0 $C3,$80 ... $00DF $C3,$9F
-- lower $00E0 $C3,$A0 ... $00FF $C3,$BF
-- AA AE EE NN OE UE mm
-- $D7 $DF $F7 excluded (not letters)
-- $FF excluded (here LC, UC is $0178)

-- $C4-$C5-block $0100 $C4,$80 ... $017F $C5,$BF (EO mm)
-- delta 1 and UC even but messy with many exceptions
-- EO $0108 ... $016D case delta 1
-- for example SX upper $015C $C5,$9C - lower $015D $C5,$9D
-- $0138 $0149 $017F excluded (not letters)
-- $0178 excluded (here UC, LC is $FF)
-- $0100 ... $0137 UC even
-- $0139 ... $0148 reversed (UC odd) note that case delta is NOT reversed
-- $014A ... $0177 UC even again
-- $0179 ... $017E reversed (UC odd) note that case delta is NOT reversed

-- $CC-$CF-block $0300 $CC,$80 ... $03FF $CF,$BF (EL mm) delta $20
-- EL $0370 ... $03FF (officially)
-- strict EL base range $0391 ... $03C9 case delta $20
-- $0391 $CE,$91 ... $03AB $CE,$AB upper
-- $03B1 $CE,$B1 ... $03CB $CD,$8B lower
-- for example "omega" upper $03A9 $CE,$A9 - lower $03C9 $CF,$89

-- $D0-$D3-block $0400 $D0,$80 ... $04FF $D3,$BF (RU mm) delta $20 $50
-- strict RU base range $0410 ... $044F case delta $20 but 1 extra char !!!
-- $0410 $D0,$90 ... $042F $D0,$AF upper
-- $0430 $D0,$B0 ... $044F $D1,$8F lower
-- for example "CCCP-gamma" upper $0413 $D0,$93 - lower $0433 $D0,$B3
-- extra base char and exception is special "E" with horizontal doubledot
--       case delta $50 (upper $0401 $D0,$81 - lower $0451 $D1,$91)
-- same applies for ranges $0400 $D0,$80 ... $040F $D0,$8F upper
--      and $0450 $D1,$90 ... $045F $D1,$9F lower

-- This sub depends on "MATH FUNCTIONS"\"mathmod" and
-- "MATH FUNCTIONS"\"mathbittest" and "STRING FUNCTIONS"\"lftestuc" and
-- "STRING FUNCTIONS"\"lftestlc" and "UTF8 FUNCTIONS"\"lfutf8length".

local function lfcasegene (strucinut, booucas)

  local numlaengden = 0 -- length from "string.len"
  local numchaer = 0 -- UINT8 beginning char
  local numchaes = 0 -- UINT8 later char (BIG ENDIAN, lower value here)
  local numcharel = 0 -- UINT8 code relative to beginning of block $00...$FF
  local numdelabs = 0 -- UINT8 absolute positive delta
  local numdelta = 0 -- SINT16 signed, can be negative
  local numdelcarry = 0 -- SINT8 signed, can be negative

  local boowantlower = false
  local booisuppr = false
  local booislowr = false
  local boopending = false

  local booc3blok = false -- $C3 only $00C0...$00FF SV mm delta 32
  local booc4c5bl = false -- $C4 $C5  $0100...$017F EO mm delta 1
  local boocccfbl = false -- $CC $CF  $0300...$03FF EL mm delta 32
  local bood0d3bl = false -- $D0 $D3  $0400...$04FF RU mm delta 32 80

  while (true) do -- fake loop

    numlaengden = string.len (strucinut)
    if ((numlaengden==0) or (numlaengden>2)) then
      break -- to join mark
    end--if
    numchaer = string.byte (strucinut,1,1)
    if ((lfutf8length(numchaer))~=numlaengden) then
      break -- to join mark -- mismatch with length from sub "lfutf8length"
    end--if
    boowantlower = (not booucas)

    if (numlaengden==1) then
      booisuppr = lftestuc(numchaer)
      booislowr = lftestlc(numchaer)
      if (booisuppr and boowantlower) then
        numdelta = 32 -- ASCII UPPER->lower
      end--if
      if (booislowr and booucas) then
        numdelta = -32 -- ASCII lower->UPPER
      end--if
      break -- to join mark
    end--if

    numchaes = string.byte (strucinut,2,2)
    booc3blok = (numchaer==195) -- case delta is 32
    booc4c5bl = ((numchaer==196) or (numchaer==197)) -- case delta is 1
    boocccfbl = ((numchaer>=204) and (numchaer<=207)) -- case delta is 32
    bood0d3bl = ((numchaer>=208) and (numchaer<=211)) -- case delta is 32 80

    if (booc3blok) then
      boopending = true
      numcharel = numchaes + 64 -- simplified calculation here (begins at $C0)
      if ((numcharel==215) or (numcharel==223) or (numcharel==247)) then
        boopending = false -- not a letter, we are done
      end--if
      if (numcharel==255) then
        boopending = false -- special LC silly "Y" with horizontal doubledot
        if (booucas) then
          numdelta = 121 -- lower->UPPER (distant and reversed)
        end--if
      end--if
      if (boopending) then
        booislowr = (mathbittest(numcharel,5)) -- mostly regular block
        booisuppr = not booislowr
        if (booisuppr and boowantlower) then
          numdelta = 32 -- UPPER->lower
        end--if
        if (booislowr and booucas) then
          numdelta = -32 -- lower->UPPER
        end--if
      end--if (boopending) then
      break -- to join mark
    end--if

    if (booc4c5bl) then
      boopending = true
      numcharel = (numchaer-196)*64 + (numchaes-128) -- begins at $C4
      if ((numcharel==56) or (numcharel==73) or (numcharel==127)) then
        boopending = false -- not a letter, we are done
      end--if
      if (numcharel==120) then
        boopending = false -- special UC silly "Y" with horizontal doubledot
        if (boowantlower) then
          numdelta = -121 -- UPPER->lower (distant and reversed)
        end--if
      end--if
      if (boopending) then
        if (((numcharel>=57) and (numcharel<=73)) or (numcharel>=121)) then
          booislowr = ((mathmod(numcharel,2))==0) -- UC odd (reversed)
        else
          booislowr = ((mathmod(numcharel,2))==1) -- UC even (ordinary)
        end--if
        booisuppr = not booislowr
        if (booisuppr and boowantlower) then
          numdelta = 1 -- UPPER->lower
        end--if
        if (booislowr and booucas) then
          numdelta = -1 -- lower->UPPER
        end--if
      end--if (boopending) then
      break -- to join mark
    end--if

    if (boocccfbl) then
      numcharel = (numchaer-204)*64 + (numchaes-128) -- begins at $CC
      booisuppr = ((numcharel>=145) and (numcharel<=171))
      booislowr = ((numcharel>=177) and (numcharel<=203))
      if (booisuppr and boowantlower) then
        numdelta = 32 -- UPPER->lower
      end--if
      if (booislowr and booucas) then
        numdelta = -32 -- lower->UPPER
      end--if
      break -- to join mark
    end--if

    if (bood0d3bl) then
      numcharel = (numchaer-208)*64 + (numchaes-128) -- begins at $D0
      booisuppr = (numcharel<=47) -- delta $20 $50
      booislowr = ((numcharel>=48) and (numcharel<=95)) -- delta $20 $50
      if (booisuppr or booislowr) then
        numdelabs = 32
        if ((numcharel<=15) or (numcharel>=80)) then
          numdelabs = 80
        end--if
      end--if
      if (booisuppr and boowantlower) then
        numdelta = numdelabs -- UPPER->lower
      end--if
      if (booislowr and booucas) then
        numdelta = -numdelabs -- lower->UPPER
      end--if
      break -- to join mark
    end--if

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

  if ((numlaengden==1) and (numdelta~=0)) then
    strucinut = string.char (numchaer + numdelta) -- no risk of carry here
  end--if
  if ((numlaengden==2) and (numdelta~=0)) then
    numdelcarry = 0
    while ((numchaes+numdelta)>=192) do
       numdelta = numdelta - 64
       numdelcarry = numdelcarry + 1 -- add BIG ENDIAN 6 bits with carry
    end--while
    while ((numchaes+numdelta)<=127) do
       numdelta = numdelta + 64
       numdelcarry = numdelcarry - 1 -- negat add BIG ENDIAN 6 bits with carry
    end--while
    strucinut = string.char (numchaer + numdelcarry) .. string.char (numchaes + numdelta)
  end--if

  return strucinut -- same var for input and output !!!

end--function lfcasegene

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

-- Local function LFXCASEULT

-- Adjust letter case of beginning letter or all letters in a word or group of
-- words to upper or lower, limited unicode support (generous LFCASEGENE).

-- See LFFIXCASE for ASCII-only version.

-- Input  : * strenigo : word or group of words (may be empty)
--          * booupcas : "true" for uppercase and "false" for lowercase
--          * boodoall : "true" to adjust all letters, "false" only beginning

-- This sub depends on "MATH FUNCTIONS"\"mathmod" and
-- "MATH FUNCTIONS"\"mathbittest" and "STRING FUNCTIONS"\"lftestuc" and
-- "STRING FUNCTIONS"\"lftestlc" and "UTF8 FUNCTIONS"\"lfutf8length" and
-- "UTF8 FUNCTIONS"\"lfcasegene" (generous LFCASEGENE).

local function lfxcaseult (strenigo, booupcas, boodoall)

  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 = ""

  numlein = string.len (strenigo)
  while (true) do
    if (numposi>numlein) then
      break -- done
    end--if
    bootryadj = (boodoall or (numposi==1))
    numcut = lfutf8length(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
      strte7mp = lfcasegene(strte7mp,booupcas) -- (generous LFCASEGENE)
    end--if
    strelygo = strelygo .. strte7mp -- this can be slow
    numposi = numposi + numcut
  end--while
  return strelygo

end--function lfxcaseult

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

---- ORDINARY LOCAL HIGH LEVEL FUNCTIONS ----

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

-- Local function LFBREWERROR

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

-- We need 3 const strings "constrelabg", "constrelaen",
-- "constrlaxhu" and const table "contaberaroj".

-- This sub depends on "CONVERSION FUNCTIONS"\"lfnumto2digit".

local function lfbrewerror (numerrorcode)
  local vardeskrip = 0
  local strytsux = '#E'
  vardeskrip = contaberaroj[numerrorcode] -- risk of type "nil"
  if (type(vardeskrip)=="string") then
    strytsux = strytsux .. lfnumto2digit(numerrorcode) .. ' ' .. vardeskrip
  else
    strytsux = strytsux .. '??'
  end--if
  strytsux = constrlaxhu .. constrelabg .. strytsux .. constrelaen .. constrlaxhu
  return strytsux
end--function lfbrewerror

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

-- Local function LFCHKKODINV

-- Check whether a string (intended to be a language code) contains only 2
-- or 3 lowercase letters or maybe a digit in middle position or maybe
-- instead equals to "-" or "??" and maybe additionally is not
-- included on the ban list.

-- Input  : * strqooq -- string (empty is useless and returns
--                       "true" ie "bad" but can't cause any harm)
--          * numnokod -- "0" ... "3" how special codes "-" "??" should pass
--          * boodigit -- "true" to allow digit in middle position
--          * boonoban -- "true" to skip test against ban table

-- Output : * booisbadlk -- bool "true" if the string is evil

-- This sub depends on "STRING FUNCTIONS"\"lftestnum"
-- and "STRING FUNCTIONS"\"lftestlc".

-- We need const table "contabisbanned".

local function lfchkkodinv (strqooq, numnokod, boodigit, boonoban)
  local varomongkosong = "" -- for check against the ban list
  local booisbadlk = false -- pre-assume good
  local numchiiar = 0
  local numukurran = 0
  local numindeex = 0 -- ZERO-based
  while (true) do -- fake (outer) loop
    if ((strqooq=="-") and ((numnokod==1) or (numnokod==3))) then
      break -- to join mark -- good
    end--if
    if ((strqooq=="??") and ((numnokod==2) or (numnokod==3))) then
      break -- to join mark -- good
    end--if
    numukurran = string.len (strqooq)
    if ((numukurran<2) or (numukurran>3)) then
      booisbadlk = true
      break -- to join mark -- evil
    end--if
    numchiiar = string.byte (strqooq,1,1)
    if (lftestlc(numchiiar)==false) then
      booisbadlk = true
      break -- to join mark -- evil
    end--if
    numchiiar = string.byte (strqooq,numukurran,numukurran)
    if (lftestlc(numchiiar)==false) then
      booisbadlk = true
      break -- to join mark -- evil
    end--if
    if (numukurran==3) then
      numchiiar = string.byte (strqooq,2,2)
      if ((boodigit==false) or (lftestnum(numchiiar)==false)) then
        if (lftestlc(numchiiar)==false) then
          booisbadlk = true
          break -- to join mark -- evil
        end--if
      end--if ((boodigit==false) or (lftestnum(numchiiar)==false))
    end--if
    if (boonoban==false) then
      while (true) do -- ordinary inner loop
        varomongkosong = contabisbanned[numindeex+1] -- number of elem unknown
        if (type(varomongkosong)~="string") then
          break -- abort inner loop (then fake outer loop) due to end of table
        end--if
        numukurran = string.len (varomongkosong)
        if ((numukurran<2) or (numukurran>3)) then
          break -- abort inner loop (then fake outer loop) due to faulty table
        end--if
        if (strqooq==varomongkosong) then
          booisbadlk = true
          break -- abort inner loop (then fake outer loop) due to violation
        end--if
        numindeex = numindeex + 1 -- ZERO-based
      end--while -- ordinary inner loop
    end--if (boonoban==false) then
    break -- finally to join mark
  end--while -- fake loop -- join mark
  return booisbadlk
end--function lfchkkodinv

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

-- Local function LFFILLNAME

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

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

local function lffillname (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
    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 lffillname

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

-- Local function LFKODEOSG

-- Transcode X-surrogates (without "\", thus for example "kacxo",
-- NOT "ka\cxo") to UTF-8 cxapeloj in a string (EO only).

-- Input  : * strsurr -- string (empty is useless but can't cause major harm)

-- Output : * strcxapeloj

-- We need const table "contabtransiltable".

-- This sub depends on "MATH FUNCTIONS"\"mathdiv"
-- and "MATH FUNCTIONS"\"mathmod".

local function lfkodeosg (strsurr)
  local varpeek = 0
  local strcxapeloj = ''
  local numinputl = 0
  local numininx = 0 -- ZERO-based source index
  local numknark = 0 -- current char (ZERO is NOT valid)
  local numknarp = 0 -- previous char (ZERO is NOT valid)
  local numlow = 0
  local numhaj = 0
  numinputl = string.len(strsurr)
  while (true) do
    if (numininx==numinputl) then
      break
    end--if
    numknark = string.byte(strsurr,(numininx+1),(numininx+1))
    numininx = numininx + 1
    numhaj = 0 -- pre-assume no translation
    if ((numknarp~=0) and ((numknark==88) or (numknark==120))) then -- got "x"
      varpeek = contabtransiltable[numknarp] -- UINT16 or nil
      if (varpeek~=nil) then
        numlow = mathmod (varpeek,256)
        numhaj = mathdiv (varpeek,256)
      end--if
    end--if
    if (numhaj~=0) then
      strcxapeloj = strcxapeloj .. string.char(numhaj,numlow)
      numknark = 0 -- invalidade current char
    else
      if (numknarp~=0) then -- add previous char only if valid
        strcxapeloj = strcxapeloj .. string.char(numknarp) -- add it
      end--if
    end--if
    numknarp = numknark -- copy to previous even if invalid
  end--while
  if (numknarp~=0) then -- add previous and last char only if valid
    strcxapeloj = strcxapeloj .. string.char(numknarp) -- add it
  end--if
  return strcxapeloj
end--function lfkodeosg

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

---- MAIN EXPORTED FUNCTION ----

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

function grup.ek (arxframent)

  -- general unknown type

  local vartmp = 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 tabtriple   = {} -- 0...8 and 10...18 and 20...28

  -- general str

  local strpagenam  = '' -- "{{PAGENAME}}" o "pagenameoverridetestonly"
  local strkodbah   = '' -- obligatory param

  local strmurf     = ''
  local strkato     = ''
  local strtmp      = '' -- temp

  local strviserr   = '' -- visible error
  local strvisgud   = '' -- visible good output
  local strret      = '' -- final result string

  -- general num

  local numerr    = 0 -- 1 inter 2 param 4 evidente 5 additional params
  local numpindex = 0 -- number of anon params

  local numtamp   = 0
  local numtump   = 0
  local numoct    = 0
  local numodt    = 0

  local numlong   = 0
  local numprev   = 0
  local nummrtyp  = 0 -- mortyp
  local numskrip  = 0 -- script code
  local numspesl  = 0 -- special code ZERO or ONE or 49...53
  local numpaink  = 0 -- number from "mw.site.stats.pagesInCategory"
  local numonetwo = 0 -- minimal number for showing the box at all

  -- general boo

  local bootrace  = false  -- from "detrc=true"
  local boofake   = false  -- if no optional params then we must fake them
  local boospaceflight = false -- space in the pagename

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

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

  qstrtrace = '<br>This is "mgrup", requested "detrc" report.'

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

  ---- FILL IN ERROR MESSAGES AND TRANSCODE EO 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 EO the X-substitution is subsequently performed

  if (numerr==0) then

    numtamp = 2 -- start with #E02
    numtump = 0 -- tolerance for holes
    while (true) do
      vartmp = contaberaroj[numtamp] -- risk of type "nil"
      if (type(vartmp)=="string") then -- number of messages is NOT hardcoded
        numtump = 0
        strtmp = lffillname (vartmp,constrkoll)
        if (constrpriv=="eo") then
          strtmp = lfkodeosg (strtmp)
        end--if
        contaberaroj[numtamp] = strtmp
      else
        numtump = numtump + 1
      end--if
      if (numtump==4) then -- max 3 consecutive holes
        break
      end--if
      numtamp = numtamp + 1 -- TWO-based
    end--while

  end--if

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

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

  arxsomons = arxframent.args -- "args" from our own "frame"
  if (type(arxsomons)~="table") then
    arxsomons = {} -- guard against indexing error
    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 2 HIDDEN NAMED PARAMS INTO 1 STRING AND 1 BOOLEAN ----

  -- 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=" and "nocat=" must be seized independently on "numerr"
  -- even if we already suck, but type "table" must be ensured !!!

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

  if (arxsomons['detrc']=='true') then
    bootrace = true
    lfshowvar (numerr,'numerr','done with hidden parameters') -- "qstrtrace"
  end--if

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

  -- 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
    vartmp = mw.title.getCurrentTitle().text -- without namespace prefix
    if (type(vartmp)=="string") then
      numtamp = string.len(vartmp)
      if ((numtamp>=1) and (numtamp<=120)) then
        strpagenam = vartmp -- 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

  lfshowvar (strpagenam,'strpagenam','pagename finally seized') -- "qstrtrace"
  lfshowvar (numerr,'numerr') -- "qstrtrace"

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

  -- reporting of this error #E01 may NOT depend on
  -- uncommentable strings as "constrkoll" and "contaberaroj"

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

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

  ---- PRELIMINARILY ANALYZE ANONYMOUS PARAMETERS ----

  -- this will catch holes, empty parameters, too long parameters,
  -- and wrong number of parameters

  -- below on exit var "numpindex" will contain number of
  -- prevalidated anonymous params

  -- this depends on 3 constants:
  -- * contabparam[0] minimal number
  -- * contabparam[1] maximal number
  -- * contabparam[2] maximal length (default 160)

  if (numerr==0) then
    numpindex = 0 -- ZERO-based
    numtamp = contabparam[1] -- maximal number of params
    while (true) do
      vartmp = arxsomons [numpindex+1] -- can be "nil"
      if ((type(vartmp)~="string") or (numpindex>numtamp)) then
        break -- good or bad
      end--if
      numlong = string.len (vartmp)
      if ((numlong==0) or (numlong>contabparam[2])) then
        numerr = 2 -- #E02 param/RTFD
        break -- only bad here
      end--if
      numpindex = numpindex + 1 -- on exit has number of valid parameters
    end--while
    if ((numpindex<contabparam[0]) or (numpindex>numtamp)) then
      numerr = 2 -- #E02 param/RTFD
    end--if
  end--if

  ---- PROCESS 1 ANONYMOUS PARAM INTO 1 STRING ----

  -- now var "numpindex" sudah contains number of prevalidated params
  -- and is 1 ... 8

  -- note that "lfchkkodinv" returns "true" on failure

  -- this depends directly on const boolean "conboomiddig"

  -- this depends indirectly on const table "contabisbanned" via "lfchkkodinv"

  if (numerr==0) then
    strkodbah = arxsomons[1] -- language code (obligatory)
    if (lfchkkodinv(strkodbah,0,conboomiddig,false)) then
      numerr = 4 -- #E04
    end--if
  end--if

  if (bootrace) then
    lftracemsg ('Seized 1 anon param lng code') -- "qstrtrace"
    lfshowvar (numpindex,'numpindex') -- "qstrtrace"
    lfshowvar (strkodbah,'strkodbah') -- "qstrtrace"
    lfshowvar (numerr,'numerr') -- "qstrtrace"
  end--if

  ---- PROCESS 0...7 ANONYMOUS PARAMS INTO TABLE ----

  -- now var "numpindex" sudah contains number of prevalidated params
  -- and is 1 ... 8

  -- mortyp (1)
  -- mortyp -- script code (3)
  -- mortyp -- dash "-" -- number 1...5 (3)
  -- mortyp -- dash "-" -- number 1...5 -- percent "%" -- script code (5)
  -- mortyp -- colon ":" -- explicit (3...42)
  -- mortyp -- colon ":" -- explicit -- percent "%" -- script code (5...44)

  -- percent "%" 37 -- dash "-" 45 -- colon ":" 58

  -- explicit length 1...40 octet:s

  -- mortyp goes into "nummrtyp" (7 possible num values, must be valid)
  -- special code goes into "numspesl" (ZERO (as-is) or ONE (expl) or 49...53)
  -- script goes into "numskrip" (all uppercase possible 65...90, or ZERO none)

  -- "tabtriple":
  --  0... 8 (num) mortyp:s (7 possible num values, must be valid, "nil" EOF)
  -- 10...18 (str) translated (no limit) or explicit (1...40) morphemes
  -- 20...28 (num) script codes (ZERO if none specified)
  -- cannot be empty after while-loop due to faking

  numtamp = 2 -- TWO-based index in ARX 2...8
  numtump = 0 -- ZERO-based index in "tabtriple" step 1 or 2 !!!
  numprev = 0 -- previous mortyp
  boofake = (numpindex==1)
  if (boofake) then
    numpindex = 3 -- part of faking
  end--if

  while (true) do
    if (numerr~=0) then
      break -- jaevlar
    end--if
    if (numtump>=9) then
      numerr = 5 -- #E05 too many (0...8, index 9 is NOT valid anymore)
      break -- done
    end--if
    if (numtamp>numpindex) then -- ZERO iterations NOT possible
      break -- done
    end--if
    if (boofake) then
      if (numtamp==2) then
        strtmp = "M" -- faked index 2
      else
        strtmp = "W" -- faked index 3
      end--if
    else
      strtmp = arxsomons[numtamp] -- guaranteed to be nonempty string
    end--if
    numtamp = numtamp + 1
    numlong = string.len (strtmp)
    if ((numlong==2) or (numlong>44)) then
      numerr = 5 -- #E05 length 2 is blatantly invalid (1 and 3 are valid)
      if (bootrace) then
        lftracemsg ('Invalid length 2 or >44') -- "qstrtrace"
      end--if
      break
    end--if
    nummrtyp = string.byte (strtmp,1,1)
    if (nummrtyp<numprev) then
      numerr = 5 -- #E05 not ascending (equality is legal)
      if (bootrace) then
        lftracemsg ('Not ascending: ' .. tostring(nummrtyp) .. '<' .. tostring(numprev)) -- "qstrtrace"
      end--if
      break
    end--if
    numprev = nummrtyp
    if ((nummrtyp~=67) and (nummrtyp~=73) and (nummrtyp~=77) and (nummrtyp~=78) and (nummrtyp~=80) and (nummrtyp~=85) and (nummrtyp~=87)) then
      numerr = 5 -- #E05 illegal type C I M N P U W
      break
    end--if
    numspesl = 0 -- pre-ass'ume take pagename as-is
    numskrip = 0 -- pre-ass'ume none
    if (numlong>=3) then
      numoct = string.byte (strtmp,(numlong-1),(numlong-1)) -- maybe "%"
      numodt = string.byte (strtmp,(numlong  ),(numlong  )) -- maybe uc
      if ((numoct==37) and lftestuc(numodt)) then
        numskrip = numodt -- otherwise ZERO
        strtmp = string.sub (strtmp,1,(numlong-2)) -- saw off 2 char:s
        numlong = numlong - 2 -- at least ONE left
      end--if
    end--if
    if (numlong>=3) then -- BEWARE we could have cut above, new eval needed
      numoct = string.byte (strtmp,2,2) -- legal is "-" 45 or ":" 58 only
      if ((numoct~=45) and (numoct~=58)) then
        numerr = 5 -- #E05 invalid separator
        if (bootrace) then
          lftracemsg ('Invalid separator after mortyp char') -- "qstrtrace"
        end--if
        break
      end--if
      if (numoct==58) then
        numspesl = 1 -- explicit morpheme (seize it later)
      else
        numspesl = string.byte (strtmp,3,3) -- number 1...5 after the dash "-"
        if ((numlong~=3) or (numspesl<49) or (numspesl>53)) then
          numerr = 5 -- #E05 only "1"..."5" legal take -- adjusted pagename
          if (bootrace) then
            lftracemsg ('Invalid length <>3(+2) with dash or invalid number after dash') -- "qstrtrace"
          end--if
          break
        end--if
      end--if (numoct==58) else
    end--if (numlong>=3) then
    if (numspesl==1) then
      strmurf = string.sub (strtmp,3,numlong) -- explicit, at least 1 ch left
    else
      strmurf = strpagenam -- not explicit as-is or with special code
    end--if
    numlong = string.len (strmurf) -- refresh !!!
    if ((numspesl==49) or (numspesl==51) or (numspesl==53)) then
      strmurf = string.sub (strmurf,1,(numlong-1)) -- saw off last lett 1 3 5
    end--if
    if ((numspesl==50) or (numspesl==51)) then
      strmurf = lfxcaseult (strmurf,false,false) -- lc for 2 and 3
    end--if
    if ((numspesl==52) or (numspesl==53)) then
      strmurf = lfxcaseult (strmurf,true,false) -- uc for 4 and 5
    end--if
    tabtriple [numtump]    = nummrtyp -- store mortyp
    tabtriple [numtump+10] = strmurf  -- store morpheme
    tabtriple [numtump+20] = numskrip -- store script
    numtump = numtump + 1
    if (nummrtyp==87) then -- "W" counts double: grupo + frazo
      tabtriple [numtump]    = 89       -- store pseudo-type "Y" frazo
      tabtriple [numtump+10] = strmurf  -- store morpheme
      tabtriple [numtump+20] = numskrip -- store script
      numtump = numtump + 1
    end--if
  end--while

  if (bootrace) then
    lfshowvar (tabtriple,'tabtriple','after parameters seized',29) -- "qstrtrace"
    lfshowvar (numerr,'numerr') -- "qstrtrace"
  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 = lfbrewerror(numerr)
  end--if

  ---- BREW THE LIST ----

  -- Need string "constrkatp" and it is cat prefix and includes the colon
  -- ":" but must NOT be fed into "mw.site.stats.pagesInCategory"
  -- nor into "<categorytree>"

  -- "Vorto -zh- enhavanta morfemon M (#) S" ("#" is Chinese simplified lett)
  -- "Vorto -id- enhavanta morfemon M (api)"
  -- "Vortgrupo -id- enhavanta (api)"
  -- "Vortgrupo -id- enhavanta (kereta api)"
  -- "Frazo -id- enhavanta vorton (api)"

  -- for >=19 brew    : "constrdivxx" + "constrdivbg" + link to cat with
  --                    description and number + "constrdiven"
  -- for 1 or 2 ...   : "constrdivxx" + "constrdivbk" + description and
  -- 18 brew            number + "<br>" + "constrcatbk" +
  --                    raw cat name + "constrcaten" + "constrdiven"
  -- for <1 or 2 brew : nothing for now

  -- if nothing else brewed after exhaustive
  -- search, then finally brew ":(neniuj)" via "constrbuggerall"

  -- we have a pseudo-type 89 AKA "Y" for "frazo" whereas 87 AKA
  -- "W" is restricted to "vortgrupo"

  -- assign "boospaceflight" to true if we have space in the pagename

  -- this can later result in rising the minimum "numonetwo" from ONE to TWO

  if (numerr==0) then

    numtamp = 0 -- ZERO-based index in "tabtriple"
    strvisgud = ''
    boospaceflight = (string.find(strpagenam," ",1,true)~=nil) -- affects mini
    lfshowvar (boospaceflight,'boospaceflight','from pagename') -- "qstrtrace"

    while (true) do
      if (bootrace) then
        lfshowvar (numtamp,'numtamp','begin iteration trying to brew a box') -- "qstrtrace"
      end--if
      vartmp   = tabtriple [numtamp   ] -- pick mortyp with risk of type "nil"
      if (type(vartmp)~="number") then
        if (bootrace) then
          lftracemsg ('Done due to type "nil"') -- "qstrtrace"
        end--if
        break -- done
      end--if
      nummrtyp = vartmp
      strmurf  = tabtriple [numtamp+10] -- pick morpheme
      numskrip = tabtriple [numtamp+20] -- pick script
      numtamp = numtamp + 1
      if (bootrace) then
        lfshowvar (nummrtyp,'nummrtyp') -- "qstrtrace"
        lfshowvar (strmurf,'strmurf') -- "qstrtrace"
        lfshowvar (numskrip,'numskrip') -- "qstrtrace"
      end--if
      strkato = "??" -- fed into "mw.site.stats.pagesInCategory" and more
      strtmp  = "??" -- visible text
      if (nummrtyp<87) then
        strkato = "Vorto"
        strtmp  = "Vortfaradoj"
      end--if
      if (nummrtyp==87) then
        strkato = "Vortgrupo"
        strtmp  = "Vortgrupoj"
      end--if
      if (nummrtyp==89) then
        strkato = "Frazo"
        strtmp  = "Frazoj"
      end--if
      strkato = strkato .. ' -' .. strkodbah .. '- enhavanta'
      strtmp  = strtmp  .. ' kun'
      if (nummrtyp==89) then
        strkato = strkato .. ' vorton' -- YES for "frazo" NO for "vortgrupo"
      end--if
      if (nummrtyp<87) then
        strkato = strkato .. ' morfemon ' .. string.char(nummrtyp)
        strtmp  = strtmp  .. ' ' .. string.char(nummrtyp)
      end--if
      strkato = strkato .. ' (' .. strmurf .. ')'
      strtmp  = strtmp  .. ' "' .. strmurf .. '"'
      if (numskrip~=0) then
        strkato = strkato .. ' ' .. string.char(numskrip)
        strtmp  = strtmp  .. ' ' .. string.char(numskrip)
      end--if
      if (bootrace) then
        lfshowvar (strkato,'strkato','cat name brewed') -- "qstrtrace"
        lfshowvar (strtmp,'strtmp','boasting text brewed') -- "qstrtrace"
      end--if
      vartmp = mw.site.stats.pagesInCategory(strkato,'pages')
      if (bootrace) then
        lfshowvar (vartmp,'vartmp/numpaink','after "mw.site.stats.pagesInCategory"') -- "qstrtrace"
      end--if
      numpaink = 0
      if (type(vartmp)=="number") then
        numpaink = vartmp -- risk for negative values due to stupid MediaWiki
      end--if
      numonetwo = 1
      if ((nummrtyp==77) or ((nummrtyp==78) and (strpagenam~=strmurf)) or ((nummrtyp==87) and boospaceflight)) then
        numonetwo = 2 -- cases "ab)" "d)" "f)" see far above
        if (bootrace) then
          lftracemsg ('Rising limit from 1 to 2') -- "qstrtrace"
        end--if
      end--if
      if (numpaink>=numonetwo) then
        strtmp = strtmp .. ' (' .. tostring (numpaink) .. ')' -- visible text
        if (numpaink>=19) then
          strkato = '[[:' .. constrkatp .. strkato .. '|' .. strtmp .. ']]'
          strvisgud = strvisgud .. constrdivxx .. constrdivbg .. strkato .. constrdiven
        else
          strvisgud = strvisgud .. constrdivxx .. constrdivbk .. strtmp .. '<br>'
          strkato = arxframent:preprocess (constrcatbk .. strkato .. constrcaten)
          strvisgud = strvisgud .. strkato .. constrdiven
        end--if
      end--if
    end--while

    if (strvisgud=='') then
      strvisgud = constrbuggerall -- nothing found
    end--if

  end--if

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

  strret = strviserr .. strvisgud
  if (bootrace) then
    strret = "<br>" .. qstrtrace .. "<br><br>" .. strret
  end--if
  return strret

end--function

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

return grup