Modulo:mpiktbllki

StopsignIndonesia.png Ĉ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.
Memtesto disponeblas sur la dokumentaĵa subpaĝo

--[===[

MODULE "MPIKTBLLKI" (pick from table lingvokodo al info)

"eo.wiktionary.org/wiki/Modulo:mpiktbllki" <!--2021-Feb-06-->
"id.wiktionary.org/wiki/Modul:mpiktbllki"

Purpose: picks an element from a raw two-dimensional table, or more
         precisely from "tbllingvoj" (EO) or "tblbahasa" (ID)

Utilo: ellegas elementon el kruda dudimensia tabelo, aux pli
       precize el "tbllingvoj" (EO) aux "tblbahasa" (ID)

Manfaat: membaca elemen dari tabel berdimensi dua

Syfte: laeser ut ett element fraan en tvaadimensionell tabell

Used by templates / Uzata far sxablonoj /
Digunakan oleh templat / Anvaent av mallar:
- "Lingvo" "auxdo" "deveno3" ...
- "Modulo:mlawc" siavice uzata far sxablono "livs" (EO) /
  "Modul:mlawc" digunakan oleh templat "bakk" (ID)

Required submodules / Bezonataj submoduloj / Submodul yang diperlukan:
- "mbllingvoj" in turn requiring template "tbllingvoj" (EO)
- "mtblbahasa" in turn requiring template "tblbahasa" (ID)
We do NOT need "mpiksubstrind" since we contain a copy of a part of its code.

Incoming: - 2 obligatory anonymous parameters
            - requested y-index (access code of the
              language, 2 or 3 lowercase chars) string indexing
            - requested x-index (0...9) (soon 0...10) or special
              value "+" or "-" numerical indexing
          - 2 optional anonymous parameters (1 or 2 can be given,
              but none can be skipped, specify default value if needed)
            - "1" if removal of rl-lr-enclosement (for non-latin scripts) is
              desired, or "0" to keep it (default is "1")
            - alternative output string to replace "-" coming literally
              from the source table (1...64 char:s, default is "-")
          - 2 optional named parameters
            - "err=" alternative output string to replace the "=" error
              message (1...64 char:s, default is "=") (this works even
              for errors coming from anonymous parameters)
            - "detrc=" value "true" to create trace debug report

Returned: - one string
            - for index number 0...9 (soon 0...10) the found element or "=" if
              the element was not found (due to y or x) or an error occurred
            - for special input value "+" the complete table line including
              the y-index marker (but excessive whitespace reduced), or "="
              if the y-index marker was not found or an error occurred
            - for special input value "-" binary output either "1" if the
              y-index marker was found (not checking the table line), or
              "0" if the requested y-index was not found or an error occurred

This module is unbreakable (when called with correct module name
and function name). Every imaginable input will output either a useful
result or the string "=" (not found or error).

Cxi tiu modulo estas nerompebla (kiam vokita kun gxustaj nomo de modulo
kaj nomo de funkcio). Cxiu imagebla enigo eldonas aux utilan
rezulton aux signocxenon "=" (ne trovita aux eraro).

The function of this module is governed by
the "Specification of the 2-dimensional table picking system".
- y-index markers are obligatory and
  lowercase (numbers and "-" not yet allowed) !!!FIXME!!!
- x-index markers are disallowed

Column numbering in the source table:
- numbering starts from ZERO
- "cy" does not count (it has theoretically index "-1")
- note that there is no comma between "cy" and "c0"
- "mpiktbllki" with special input value "+" returns the complete table line
  including the y-index marker "cy", this means that unless the "cy" part is
  trimmed off, index ZERO fed into "mpiksubstrind" will return both "cy"
  and "c0", thus the indexes used by "mpiksubstrind" will NOT be OFF-by-ONE

y-index marker (row index marker) placed in the
table content area (here lowercase)

x-index marker (column index marker) placed in the
table content area (here prohibitied)

digit index marker (only digits) (here NOT supported)
lowercase index marker (lowercase letters and maybe a bit more)

incoming requested index (per spec may be evaluated to string indexing
                          or numerical indexing, but here hardcoded)

string indexing     <-->  lowercase index marker
numerical indexing  <-->  digit index marker or no index
                          marker and counting (here only counting)

General rules and specific limits (see spec):

- double square brackets "[[" and "]]" are reserved for y-index markers
  and such may occur only at the beginning of line (here we assume this)
- usage of single square brackets "[" and "]" is not limited
- double round brackets "((" and "))" are reserved for x-index markers
  (here we don't support x-index markers at all)
- usage of single round brackets "(" and ")" is not limited
- more than two subsequent brackets ie for
  example "[[[" or ")))" are prohibited
- no comma "," following [[...]] and ((...)), inline whitespace
  is recommended but not required
- index markers are optional, it's possible (and potentially useful) to mix
  elements with index markers and elements without index markers, and it's
  possible (but probaly less useful) to mix lines with index markers and
  lines without index markers (here we require index markers for lines
  and disallow them for elements)
- index markers must be 2 or 3 or 4 chars long and consist of either
  only lowercase letters (lowercase index marker) or only digits
  (digit index marker), it's possible (but maybe barely useful) to
  mix the 2 types (here we don't support digit index markers at all,
  and limit lowercase index markers to 2 or 3 chars)
- if digit index markers are used then they must match the index numbers for
  both lines and columns, ie a digit index marker inside [[...]] and ((...))
  is optional but cannot hold a value different from what it would be
  anyway based on ZERO-based counting (here we don't support digit
  index markers at all)
- if y-index markers are used then they must be sorted, with a privileged role
  for 1 entry at the beginning and 1 entry at the end of the table that
  are guaranteed to be found even if they are misplaced (here we
  require y-index markers, sorting rule holds)
- redundant whitespace is not a problem as long as "maxwidth" is not exceeded
  (this applies also to the beginning of the table) and no inline whitespace
  (TAB or SPACE) follows EOL whitespace (ASCII 0...8 and 10...31),
  this applies both to table lines and empty lines, ie an EOL
  must be followed by either EOL or a table line (ie [[...]] or ((...))
  or raw table element data)
- an empty table not containing any lines is valid as long as whitespace
  rules are observed, this means EOL's are tolerable while inline whitespace
  is not (here we require at least 1 line)
- a table line may not be empty, there must be at least 1 element
  (note that allowing empty lines would cause inconsistencies between
  lines with [[...]] and those without it)
- truncated table lines, ie lines shorter than other lines are tolerable, they
  can end with element data or inline whitespace following element data, but
  not with a comma ","
- a table element may not empty, dash "-" is the recommended filler for
  dummy elements, or ((...)) can be used to "jump" over columns, even an
  x-index marker ((...)) must be followed by at least one char of table
  data (here we don't support x-index markers at all)
- a table element may not contain the string "=", if this string is found it
  is translated to "-", the string "=" is reserved to indicate "not found or
  error" (this rule holds even if alternative output string is requested)

Strategy:

- read out "minwidth" and "maxwidth" as suggested by
  spec branch "y-index markers are obligatory" and check their validity
- size of the table block in octets must be 320 ... 1'000'000 (checked)
- size of the table content area must be >= (2 * "maxwidth") (checked)
- we support only lowercase index markers, not digit index markers,
  the incoming requested y-index string must have 2 or 3 chars (checked)
- the incoming requested x-index number must be 0...9 (soon 0...10) (checked)
- we support only index marker for "y" and expect [[...]] in every line
- we support only counting for "x" and expect ((...)) not to occur
- we give a f**k in UTF8 and consider the table to be an array of octets
- for "y" indexing we apply a mixed strategy of linear forward and backward
  search with step 2 and binary search
- for "y" indexing we begin with checking 0th entry, then last entry, then
  continue with binary search, and switch back to linear search as soon as
  the remaining size becomes dangerously low
- for "x" indexing we perform a linear forward search with step 1
- we give a f**k in closing "]]" and compare only opening "[["
  and 2 or 3 chars

This is how "minwidth" and "maxwidth" constants work:

- they restrict the legth of a complete table line
  including the obligatory EOL in octets
- "maxwidth" is the maximal distande between two line beginnings, and
  includes possible trailing spaces and blank lines (no other
  redundant whitespace is permitted)
- there is a special rule for the earliest line allowing up to "maxwidth"
  extra area preceding the table if "[[" is used in all lines, otherwise
  up to "maxwidth" of blank lines
- there is a special rule for the last line allowing up to "maxwidth"
  of garbage following the table if "[[" is used in all lines, otherwise
  up to "maxwidth" of blank lines
- the minimal teoretically possible "minwidth" value
  per spec is 2 (one char + EOL)
- here the minimal sane "minwidth" value is
  11 ("[[..]]" (6) + space + "Ido" (3) + EOL

Special rule about enclosing of elements written with
non-latin scripts (rl-lr-enclosement):

- non-ASCII, non-latin and even RTL (right-to-left) scripts are permitted
  in the table
- a table element longer than 2 Octet:s may be enclosed in "rl " and " lr"
  strings on both sides, this is useful since many editors would otherwise
  break the element order if an RTL script is used
- by default the enclosing strings are not stripped off, but an extra
  parameter "1" can enable this for ordinary x-index requests (0...9)
  (soon 0...10), for special values "+" or "-" this extra parameter is ignored

Usage examples with results / Ekzemploj de uzo
kun rezultoj / Contoh penggunaan dengan hasil:

(assuming that the huge table seized via "mw.loadData" contains the silly
line "[[id]]  indonezia       , Q999 , rl bahasa Indonesia lr , 1 , id , i
nd , id  , - , Indonezia lingvo , Kategori:Bahasa Esperanto ,   100"
but no line with y-index marker "zzz", note that it is pointless to
rl-lr-enclose the string "bahasa Indonesia")

: ---------------------------------------

* #T00 {mpiktbllki|ek} (obligatory params missing, 0 given)
* expected result: "="
* actual result: "{{#invoke:mpiktbllki|ek}}"

::* #T01 {mpiktbllki|ek|eo} (obligatory params missing, only 1 given)
::* expected result: "="
::* actual result: "{{#invoke:mpiktbllki|ek|eo}}"

* #T02 {mpiktbllki|ek|err=fatal error} (obligatory params missing, 0 given, "err=" param used)
* expected result: "fatal error"
* actual result: "{{#invoke:mpiktbllki|ek|err=fatal error}}"

::* #T03 {mpiktbllki|ek|id|2} (strip garbage by default)
::* expected result: "bahasa Indonesia" (possibly useless in practise)
::* actual result: "{{#invoke:mpiktbllki|ek|id|2}}"

* #T04 {mpiktbllki|ek|ar|2} (strip garbage by default)
* expected result: ....... (maybe better attempt to test stripping)
* actual result: "{{#invoke:mpiktbllki|ek|ar|2}}"

::* #T05 {mpiktbllki|ek|id|2|0} ("c2", keep rl lr)
::* expected result: "rl bahasa Indonesia lr" (possibly failure in practise)
::* actual result: "{{#invoke:mpiktbllki|ek|id|2|0}}"

* #T06 {mpiktbllki|ek|ar|2|0} ("c2", keep rl lr)
* expected result: "rl "......." lr" (maybe better attempt to test "keep rl lr")
* actual result: "{{#invoke:mpiktbllki|ek|ar|2|0}}"

::* #T07 {mpiktbllki|ek|id|2|1} ("c2", explicit strip by "1")
::* expected result: "bahasa Indonesia" (possibly useless in practise)
::* actual result: "{{#invoke:mpiktbllki|ek|id|2|1}}"

* #T08 {mpiktbllki|ek|ar|2|1} ("c2", explicit strip by "1")
* expected result: ....... (maybe better attempt to test stripping)
* actual result: "{{#invoke:mpiktbllki|ek|ar|2|1}}"

: ---------------------------------------

* #T10 {mpiktbllki|ek|id|3} ("c3")
* expected result: "1"
* actual result: "{{#invoke:mpiktbllki|ek|id|3}}"

::* #T11 {mpiktbllki|ek|id|5} ("c5")
::* expected result: "ind"
::* actual result: "{{#invoke:mpiktbllki|ek|id|5}}"

* #T12 {mpiktbllki|ek|id|7} ("c7", keep found "-")
* expected result: "-"
* actual result: "{{#invoke:mpiktbllki|ek|id|7}}"

::* #T13 {mpiktbllki|ek|id|7|1|N/A} ("c7", replace found "-" by "N/A")
::* expected result: "N/A"
::* actual result: "{{#invoke:mpiktbllki|ek|id|7|1|N/A}}"

* #T14 {#invoke:mpiktbllki|ek|zzz|3} (error: unknown y-index "zzz")
* expected result: "="
* actual result: "{{#invoke:mpiktbllki|ek|zzz|3}}"

::* #T15 {#invoke:mpiktbllki|ek|zzz|3|err=bugger} (error: unknown y-index "zzz", "err=" param used)
::* expected result: "bugger"
::* actual result: "{{#invoke:mpiktbllki|ek|zzz|3|err=bugger}}"

: ---------------------------------------

* "{{#invoke:mpiktbllki|ek|id|11}}"
* expected result: "="

* "{{#invoke:mpiktbllki|ek|id|A}}" (invalid x-index)
* expected result: "="

* "{{#invoke:mpiktbllki|ek|id|A|1}}" (invalid x-index)
* expected result: "="

* "{{#invoke:mpiktbllki|ek|id|A|err=bugger}}" (invalid x-index, "err=" param)
* expected result: "bugger"

: ---------------------------------------

* #T30 {mpiktbllki|ek|grc|-} (binary request, assuming that "grc" exists)
* expected result: "1"
* actual result: "{{#invoke:mpiktbllki|ek|grc|-}}"

::* #T31 {mpiktbllki|ek|xxx|-} (binary request, assuming that "xxx" does not exist)
::* expected result: "0"
::* actual result: "{{#invoke:mpiktbllki|ek|xxx|-}}"

* #T32 {mpiktbllki|ek|id|+} (complete line, remove excessive whitespace)
* expected result: <nowiki> "[[id]] indonezia , Q999 , rl bahasa Indo
                             nesia lr , 1 , id , ind , id , - , Ind
                             onezia lingvo , Kategori:Bahasa Esperanto , 100" </nowiki> (possibly failure in practise)
* actual result via debu: "{{debu|{{#invoke:mpiktbllki|ek|id|+}}|0020}}"

::* #T33 {mpiktbllki|ek|xxx|+} (complete line, assuming that "xxx" does not exist)
::* expected result: "="
::* actual result: "{{#invoke:mpiktbllki|ek|xxx|+}}"

* #T34 {mpiktbllki|ek|xxx|+|detrc=true} (complete line, assuming that "xxx" does not exist, "detrc=true")
* expected result: long text followed by the short shameful verdict "="
* actual result: "{{#invoke:mpiktbllki|ek|xxx|+|detrc=true}}"

: ---------------------------------------

* note that test #T32 depends on "debu"
* note that test #T32 cannot be reasonably executed on the docs subpage without help of "pate" or "debu"

: ---------------------------------------

Note that output from this module when sent to screen directly as wikitext
will be subject to partial wiki parsing and full HTML parsing. This will
result in "[[id]" turned into a wikilink and in whitespace reduction. It
is not possible to call a module between "<pre>". In order to test this
module more thoroughly the output can be fed into another module ("debu"
or "pate") that analyzes the string and diplays ASCII codes or similar.

]===]

local piktbllki = {}

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

---- CONSTANTS ----

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

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

  local construsmo = "Modulo:mtbllingvoj"  -- EO
  -- local construsmo = "Modul:mtblbahasa"    -- ID

  -- constant table translating requested x-indexes and defining maximum
  -- valid indexes 0...9 covering totally 10 columns "c0"..."c9"

  local contabrxi = {1,2,3,4,5,6,7,8,9}
  contabrxi[0] = 0

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

  local conboomiddig  = false  -- assign to "true" to allow middle digit "s7a"

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

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

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

---- VAR:S ----

local qtbllingbaha = {}  -- type "table" with type "string" inside
local qstrtrace = ""     -- for main & sub:s, debug report request by "detrc="
local qbooguard = false  -- only for the guard test, pass to other var ASAP
local qconstrsr = ""     -- huge source table (320 ... 1'000'000 chars)

---- GUARD AGAINST INTERNAL ERROR & ONE IMPOR VIA LOADDATA HUGE SRC TABLE ----

-- maybe can delete "qtbllingbaha" in favor of "qconstrsr" but it is static

if (type(construsmo)~="string") then
  qbooguard = true -- "true" on error
else
  qtbllingbaha = mw.loadData(construsmo) -- can crash here despite guarding ??
  if (type(qtbllingbaha)~="table") then
    qbooguard = true
  else
    qconstrsr = qtbllingbaha [1] -- convert table -> string (with small risk)
    qtbllingbaha = nil -- {{DELETE}}
  end--if
end--if

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

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

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

local function mathmin (xmindd, xminee)
  local resultmin = 0 -- min operator lacks in LUA :-(
  resultmin = xmindd
  if (resultmin>xminee) then
    resultmin = xminee
  end--if
  return resultmin
end--function mathmin

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

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

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

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

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

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

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

-- Local function LFCOUNTNDG

-- Count occurrences of non-digits (from a customizable set) in a string

-- Tolerable char:s that do not count as evil are:
-- * digits "0"..."9"
-- * maybe apo for (trictl=1) or (trictl=2)
-- * maybe space for (trictl=2)

local function lfcountndg (strzzz,trictl)
  local numjrezalt = 0
  local numcair = 0
  local numjukuran = 0
  local numjindxe = 0 -- ZERO-based
  numjukuran = string.len(strzzz)
  while (true) do
    if (numjindxe==numjukuran) then
      break
    end--if
    numcair = string.byte(strzzz,(numjindxe+1),(numjindxe+1))
      if ((numcair==39) and (trictl~=0)) then
        numcair=48 -- apo OK
      end--if
      if ((numcair==32) and (trictl==2)) then
        numcair=48 -- space OK
      end--if
    if ((numcair<48) or (numcair>57)) then
      numjrezalt = numjrezalt + 1
    end--if
    numjindxe = numjindxe + 1
  end--while
  return numjrezalt
end--function lfcountndg

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

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

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

-- Local function LFDEC1DIGIT

-- Convert 1 decimal digit to integer 0...9 (255 if invalid)

local function lfdec1digit (num1digit)
  num1digit = num1digit - 48 -- may become invalid
  if ((num1digit<0) or (num1digit>9)) then
    num1digit = 255
  end--if
  return num1digit
end--function lfdec1digit

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

-- Local function LFSTR12DIG2INT

-- Convert 1 or 2 decimal digits to integer 0...99 (255 if invalid)

local function lfstr12dig2int (strmasuk)
  local numleen = 0
  local numaa   = 255 -- preassign to "error"
  local numbb   = 0
  numleen = string.len (strmasuk)
  if ((numleen==1) or (numleen==2)) then
    numaa = string.byte (strmasuk,1,1)
    numaa = lfdec1digit (numaa) -- 255 if invalid, ZERO would be valid
  end--if
  if ((numaa~=255) and (numleen==2)) then
    numbb = string.byte (strmasuk,2,2)
    numbb = lfdec1digit (numbb) -- 255 if invalid, ZERO would be valid
    if (numbb==255) then
      numaa = 255 -- 255 is invalid, note that ZERO would be valid
    else
      numaa = numaa * 10 + numbb -- valid integer number 0...99 now
    end--if
  end--if
  return numaa
end--function lfstr12dig2int

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

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

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

-- Local function LFTESTLNGKOD (see also LFCHKKODINV)

-- Test whether language code is valid depending on "boodigit".

-- Incoming empty string is safe but "nil" type is NOT

local function lftestlngkod (strlikodo,boodigit)
  local boovalid   = true
  local numlencxcx = 0
  local numchaa = 0
  numlencxcx = string.len (strlikodo)
  if ((numlencxcx<2) or (numlencxcx>3)) then
    boovalid = false
  else
    numchaar = string.byte (strlikodo,1,1)
    if (lftestlc(numchaar)==false) then
      boovalid = false
    end--if
    numchaar = string.byte (strlikodo,2,2)
    if (lftestlc(numchaar)==false) then
      if ((numlencxcx==3) and boodigit) then
        if (lfdec1digit(numchaar)>9) then
          boovalid = false -- neither lowercase nor digit
        end--if
      else
        boovalid = false -- digits NOT permitted in middle position
      end--if
    end--if
    if (numlencxcx==3) then
      numchaar = string.byte (strlikodo,3,3)
      if (lftestlc(numchaar)==false) then
        boovalid = false
      end--if
    end--if
  end--if
  return boovalid
end--function lftestlngkod

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

-- Local function LFRLSTRIP

-- Strip "rl .. lr" if present and string length is at least 8 octet:s

local function lfrlstrip (strrlinut)
  local numsoct = 0
  local numsodt = 0
  local numsoet = 0
  local numsoft = 0
  local numsogt = 0
  local numsoht = 0
  local numlaengd = 0
  numlaengd = string.len(strrlinut)
  if (numlaengd>=8) then -- at least 2 Octet:s length "rl .. lr" after strip
    numsoct = string.byte(strrlinut,1,1)
    numsodt = string.byte(strrlinut,2,2)
    numsoet = string.byte(strrlinut,3,3)
    numsoft = string.byte(strrlinut,(numlaengd-2),(numlaengd-2))
    numsogt = string.byte(strrlinut,(numlaengd-1),(numlaengd-1))
    numsoht = string.byte(strrlinut,(numlaengd  ),(numlaengd  ))
    if ((numsoct==114) and (numsodt==108) and (numsoet==32) and (numsoft==32) and (numsogt==108) and (numsoht==114)) then
      strrlinut = string.sub(strrlinut,4,(numlaengd-3)) -- stri off 3+3 char:s
    end--if
  end--if
  return strrlinut
end--function lfrlstrip

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

---- MAIN EXPORTED FUNCTION ----

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

function piktbllki.ek (arxframent)

  -- general unknown type

  local vartmp = 0     -- variable without type

  -- special type "args"

  local arxourown = 0  -- metaized "args" from our own "frame" (NOT caller's)

  -- general str (cool "qstrtrace" declared separately outside main function)

  local strtmp    = ""   -- temp
  local strtpm    = ""   -- temp
  local stroutdsh = "-"  -- alternative string to replace "-" literal fr table
  local strerrequ = "="  -- default or alterna string to replace "=" on error
  local strret    = ""   -- output string

  -- general num

  local numoct = 0     -- temp
  local numlong = 0    -- temp for parameter evaluation, length of parameter
  local numinxlen = 0  -- length of y-index word f arxframent.args[1] (2 or 3)
  local numfoulen = 0  -- length of found y-index marker (2 or 3)
  local numiwo0 = 0    -- searched y-index marker
  local numiwo1 = 0    -- searched y-index marker
  local numiwo2 = 0    -- searched y-index makker, ZERO if only 2 chars are used
  local numxin = 255   -- x-inx f arxframent.args[2] (0...9, 0...10, 177, 199)

  -- table positions "num"

  local numtbllen   = 0  -- length of the huge table seized via "mw.loadData"
  local numposirr   = 0
  local numposiss   = 0
  local numminwidth = 0  -- safe values 8...1/2 * "maxwidth" def 11
  local nummaxwidth = 0  -- safe values 40...40'000 and lower is bettr def 216
  local numtblbeg   = 0  -- size of extra area before the table, begin of table
  local numipos     = 0  -- octet position in "qconstrsr", ZERO-based
  local numfndpos   = 0  -- position of found line with [[...]], ZERO-based
  local numbinlow   = 0  -- lower limit for binary search, ZERO-based positi
  local numbinhaj   = 0  -- higher limit for binary search, ZERO-based positi

  -- general boo

  local bootrace = false  -- from "detrc=true"
  local booerr   = false  -- overall error flag
  local boostrip = true   -- flag for stripping rl-lr-enclosement
  local booyxfnd = false  -- multipurpose found flag
  local boospc   = false  -- flag for whitespace reduction feature
  local boouch   = false  -- flag for found useful char when skipping element
  local bootimp  = false  -- temp

  -- only for built-in function "searchdblchr"

  local isubnumlim = 0     -- octets to check (2 * "number of iterations")
  local isubnumchr = 0     -- char to search (91 for "[[" or 40 for "((")
  local isubboodwn = false -- "true" for downward search
  local osubboofnd = false -- "true" if the dblchar was found

  -- only for built-in function "cmp2or3wordeq"

  local osubbooequal = false -- "true" if found idx word equal to "numiwo"

  -- only for built-in function "cmp2or3wordbi"

  local osubboobiger = false -- "true" if found idx word bigger than "numiwo"

  ---- BUILT-IN GOSUB-STYLE FUNCTION "SEARCHDBLCHR" ----

  -- PURPOSE: searches for a dblchar (91 for "[[" or 40 for "((")
  --          upwards or downwards

  -- IN  : isubnumlim, isubnumchr, isubboodwn (unchanged)
  -- OUT : osubboofnd (not in)
  -- USE : qconstrsr (acts as a constant in the module)
  -- CHG : numipos (in and out)

  -- NOTE: if "isubboodwn" is "false" then we run upwards and check the limit
  --       upwards, thus with incoming position "numipos"=10 we start checking
  --       at positions 11&(12,10), with incoming "isubnumlim"=10 we will no
  --       longer check positions 23&(24,22) but will check 21&(22,20) ... we
  --       need 3 chars margin at the top of range

  -- NOTE: if "isubboodwn" is "true" then we run downwards but check the limit
  --       by separately counting upwards, thus with incoming position
  --       "numipos"=10 we start checking at
  --       positions 11&(12,10), with incoming "isubnumlim"=10 we will no
  --       longer check positions -01&(00,-02) but will check 01&(02,00) ... we
  --       need 3 chars margin at the top of range but nothing at the bottom

  -- NOTE: we are supposed to safely ignore single occurrences (ie continue
  --       searching), as opposed to the fact that triple and longer
  --       occurrences are prohibited

  local searchdblchr = function ()
    local usubnumsrch = 0 -- search position checker counting always up
    local usubnumok = 0   -- single char (giving a f**k about UTF8)
    local usubnumincr = 2 -- de facto constant "+2" or "-2"
    osubboofnd = true     -- pre-assume that we will find our dblchr
    if (isubboodwn) then
      usubnumincr = -2
    end--if
    while (true) do -- iterate through octets searching for dblchr (up or dwn)
      if (usubnumsrch>isubnumlim) then
        osubboofnd = false -- give up, table broken, "numipos" irrelevant now
        break
      end--if
      usubnumok = string.byte (qconstrsr,(numipos+2),(numipos+2)) -- "+1" actu
      if (usubnumok==isubnumchr) then
        usubnumok = string.byte (qconstrsr,(numipos+3),(numipos+3)) -- "+2" ab
        if (usubnumok==isubnumchr) then
          numipos = numipos + 1
          break -- found and "numipos" valid ... otherwise try to look below
        end--if
        usubnumok = string.byte (qconstrsr,(numipos+1),(numipos+1)) -- "+0" bl
        if (usubnumok==isubnumchr) then
          break -- found and "numipos" valid ... otherwise continue search
        end--if
      end--if
      usubnumsrch = usubnumsrch + 2 -- always upwards
      numipos = numipos + usubnumincr -- "+2" or "-2"
    end--while
  end--function searchdblchr

  ---- BUILT-IN GOSUB-STYLE FUNCTION "CHK2OR3LIM" ----

  -- PURPOSE: performs a wild guess whether the found y-index marker
  --          has 2 letters or 3 letters, lacks thorough checks,
  --          result will be ZERO if we would hit the limit

  -- IN  : numipos, numtbllen
  -- OUT : numfoulen
  -- USE : qconstrsr (acts as a constant in the module)
  -- CHG : N/A

  local chk2or3lim = function ()
    local usubnum1ch = 0 -- single char
    if ((numipos+6)>=numtbllen) then
      numfoulen = 0 -- peek +4 only but need 6 for [[aa]] or 7 for [[aaa]]
    else
      usubnum1ch = string.byte (qconstrsr,(numipos+5),(numipos+5)) -- "+4" act
      if (lftestlc(usubnum1ch)==true) then
        numfoulen = 3
      else
        numfoulen = 2
      end--if
    end--if
  end--function chk2or3lim

  ---- BUILT-IN GOSUB-STYLE FUNCTION "CMP2OR3WORDEQ" ----

  -- PURPOSE: performs a comparison of 2 or 3 char:s testing for equality only

  -- IN  : numipos, numinxlen, numfoulen
  -- OUT : osubbooequal ("true" if equal)
  -- USE : qconstrsr, 3 numiwo (act as constants in the module)
  -- CHG : N/A

  -- NOTE: it is the caller's responsibility to ensure
  --       that "numinxlen" and "numfoulen" are 2 or 3 !!!

  -- NOTE: it is the caller's responsibility to ensure
  --       that we don't hit the limit here in

  local cmp2or3wordeq = function ()
    osubbooequal = true -- pre-assume found, likely abort after 1 or 2 compa
    if (numinxlen~=numfoulen) then
      osubbooequal = false -- NOT found
    else
      if ((string.byte (qconstrsr,numipos+3,numipos+3)) ~= numiwo0) then
        osubbooequal = false ; -- NOT found
      else
        if ((string.byte (qconstrsr,numipos+4,numipos+4)) ~= numiwo1) then
          osubbooequal = false ; -- NOT found
        else
          if (numinxlen==3) then
            if ((string.byte (qconstrsr,numipos+5,numipos+5)) ~= numiwo2) then
              osubbooequal = false ; -- NOT found
            end--if
          end--if
        end--if
      end--if
    end--if
  end--function cmp2or3wordeq

  ---- BUILT-IN GOSUB-STYLE FUNCTION "CMP2OR3WORDBI" ----

  -- PURPOSE: performs a comparison of 2 or 3 char:s testing for "bigger"

  -- IN  : numipos, numinxlen, numfoulen, numiwo
  -- OUT : osubboobiger ("true" if found index word is bigger than "numiwo")
  -- USE : qconstrsr (acts as a constant in the module)
  -- CHG : N/A

  -- NOTE: it is the caller's responsibility to ensure
  --       that "numinxlen" and "numfoulen" are 2 or 3 !!!

  -- NOTE: it is the caller's responsibility to ensure
  --       that we don't hit the limit here in

  -- NOTE: the 2 strings are expected not to be equal on entry here,
  --       but single letters may be, thus ("grc" and "grc") is impossible,
  --       but ("grc" and "gro") or ("grc" and "gr") can occur, actually,
  --       the code here itself tests for "bigger" vs "smaller or equal"

  local cmp2or3wordbi = function ()
    local usubnum1char = 0 ; -- single char
    osubboobiger = false ; -- pre-assume smaller or equal, likely abort soon
    usubnum1char = (string.byte (qconstrsr,numipos+3,numipos+3)) ;
    if (usubnum1char>numiwo0) then
      osubboobiger = true ; -- bigger
    else
      if (usubnum1char==numiwo0) then
        usubnum1char = (string.byte (qconstrsr,numipos+4,numipos+4)) ;
        if (usubnum1char>numiwo1) then
          osubboobiger = true ; -- bigger
        else
          if ((usubnum1char==numiwo1) and ((numinxlen+numfoulen)>4)) then
            if (numfoulen>numinxlen) then
              osubboobiger = true -- (3:2 - found:previous) and bigger
            else
              if (numfoulen==3) then
                usubnum1char = (string.byte (qconstrsr,numipos+5,numipos+5)) ;
              else
                usubnum1char = 0
              end--if
              if (usubnum1char > numiwo2) then
                osubboobiger = true -- bigger
              end--if
            end--if
          end--if
        end--if (usubnum1char>numiwo1) else
      end--if
    end--if
  end--function cmp2or3wordbi

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

  strtmp = 'This is "mpiktbllki", requested "detrc" report.'
  qstrtrace = qstrtrace .. "<br>" .. strtmp
  strtmp = 'Boo "conboomiddig" is "' .. tostring(conboomiddig) .. '".'
  qstrtrace = qstrtrace .. "<br>" .. strtmp

  booerr = qbooguard

  ---- CHECK THE HUGE TABLE ----

  -- we do NOT have "nummaxwidth" yet
  -- spec allows 32 ... 8'000'000 but here we restrict to 320...1'000'000

  bootimp = false
  if (type(qconstrsr)=="string") then -- important check
    numtbllen = string.len (qconstrsr)
    if ((numtbllen>=320) and (numtbllen<=1000000)) then
      bootimp = true -- got it -- was preassigned to "false"
      strtmp = "Passed 320...1'000'000 check with length " .. tostring (numtbllen)
    else
      strtmp = "Failed 320...1'000'000 check"
    end--if
    qstrtrace = qstrtrace .. "<br>" .. strtmp
  end--if
  if (bootimp==false) then
    booerr = true
  end--if

  ---- SEIZE 2 OBLIGATORY ARGUMENTS FROM THE CALLER (2 MORE ARE OPTIONAL) ----

  -- as output for the index string from arxourown[1] we use 3 separate
  -- var:s (numiwo0, numiwo1, numiwo2) and "numinxlen" equal 2 or 3

  -- from arxourown[2] var "numxin" (0...9, soon 0...10,
  -- special 177, special 199, invalid 255

  -- here we access "conboomiddig" and "contabrxi"

  arxourown = arxframent.args -- "args" from our own "frame"

  if ((arxourown[1]==nil) or (arxourown[2]==nil)) then
    booerr = true
    strtmp = "Lack 2 obligatory anonymous parameters."
    qstrtrace = qstrtrace .. "<br>" .. strtmp
  end--if

  if (booerr==false) then
    strtpm = arxourown[1] -- requested y-index string (2 or 3 lowercase chars)
    numinxlen = string.len (strtpm)
    if (lftestlngkod(strtpm,conboomiddig)==false) then
      booerr = true -- not valid language code
      strtmp = "Requested y-index is invalid."
      qstrtrace = qstrtrace .. "<br>" .. strtmp
    else
      numiwo0 = string.byte (strtpm,1,1)
      numiwo1 = string.byte (strtpm,2,2)
      if (numinxlen==3) then
        numiwo2 = string.byte (strtpm,3,3) -- otherwise ZERO
      end--if
    end--if
  end--if

  if (booerr==false) then
    numxin = 255 -- preassume failure
    strtpm = arxourown[2] -- x-index (1 or 2 digit, 0...9, 0...10, ZERO-based)
    numlong = string.len (strtpm) -- temp for decimal conversion
    if (numlong==1) then
      numoct = string.byte (strtpm,1,1)
      if (numoct==43) then
        numxin = 177 -- special "+" (complete) special value 177
      end--if
      if (numoct==45) then
        numxin = 199 -- special "-" (binary) special value 199
      end--if
    end--if
    if ((numxin==255) and ((numlong==1) or (numlong==2))) then
      numoct = lfstr12dig2int (strtpm) -- 255 if invalid
      vartmp = contabrxi [numoct] -- risk of type "nil"
      strtmp = 'Translated requested x-index from "' .. strtpm .. '" to "' .. tostring(vartmp) .. '".'
      qstrtrace = qstrtrace .. "<br>" .. strtmp
      if (type(vartmp)=="number") then
        numxin = vartmp -- otherwise keep the preassigned 255
      end--if
    end--if
    if (numxin==255) then
      booerr = true -- damn: no valid x-index
      strtmp = "Requested x-index is invalid."
      qstrtrace = qstrtrace .. "<br>" .. strtmp
    end--if
  end--if

  if (booerr==false) then
    if (type(arxourown[3])=="string") then
      strtpm = arxourown[3] -- optional "0" or "1" (default) strip flag
      numlong = string.len (strtpm) -- temp
      if (numlong==1) then
        numoct = string.byte (strtpm,1,1)
        if (numoct==48) then
          boostrip = false -- was preassigned to true
        end--if
        if ((numoct~=48) and (numoct~=49)) then
          qstrtrace = qstrtrace .. "<br>strip flag invalid not 0 or 1"
          booerr = true -- damn
        end--if
      else
        qstrtrace = qstrtrace .. "<br>strip flag invalid length"
        booerr = true -- damn
      end--if
    end--if (type(arxourown[3])=="string") then

    if (type(arxourown[4])=="string") then
      strtpm = arxourown[4] -- 1...64 char:s alterna string to replace "-"
      numlong = string.len (strtpm) -- temp
      if ((numlong<1) or (numlong>64)) then
        qstrtrace = qstrtrace .. "<br>anon dash alterna string invalid"
        booerr = true -- damn
      else
        stroutdsh = strtpm -- was preassigned to "-"
      end--if
    end--if (type(arxourown[4])=="string") then
  end--if

  ---- SEIZE NAMED PARAMETER "ERR" EVEN IF WE ALREADY SUCK ----

  -- try to pick named param "err=" even if we already have "booerr==true"
  -- 1...64 char:s alternative string to replace "="

  vartmp = arxourown["err"] -- can be type "nil"
  if (type(vartmp)=="string") then
    numlong = string.len (vartmp)
    if ((numlong<1) or (numlong>64)) then
      booerr = true -- damn -- maybe redundant -- keep "strerrequ" == "="
      strtmp = "err equ alterna string invalid length"
    else
      strerrequ = vartmp -- was preassigned to "="
      strtmp = "err equ alterna string seized"
    end--if
    qstrtrace = qstrtrace .. "<br>" .. strtmp
  end--if

  ---- SEIZE NAMED PARAMETER "DETRC" EVEN IF WE ALREADY SUCK ----

  -- try to pick named param "detrc=" even if we already have "booerr==true"
  -- so far we collected to "qstrtrace" unconditionally

  if (arxourown["detrc"]=="true") then
    bootrace = true -- was preassigned to "false"
  end--if

  ---- PREASSIGN ----

  booyxfnd = false -- assign to "true" as soon as specified y-index marker fnd

  ---- SEARCH FOR TABLE BEGIN AKA THE 0TH Y-INDEX MARKER AND MINW MAXW ----

  -- * start at position ZERO
  -- * check char after char
  --   * if end of table block or 80'000 octet:s reached then
  --     abort search (S-position is still "invalid", bad)
  --   * if string "[[" found then assign S-position to index and abort
  --     search (good so far)
  -- * if S-position is "valid" then
  --   * restart at position ZERO
  --   * check char after char
  --     * if (S-position - 21) reached then abort (good so far)
  --     * if string "minw=" found at earliest hit assign R-position to
  --       index, at further hit assign R-position to "invalid"
  --       and abort (bad)

  -- * if we do not have both R-position and S-position then give up
  -- * if ( ("R-position") + 22 > "S-position") then give up
  -- * check and evaluate the line found at R-position, if good then assign
  --   "minwidth" and "maxwidth", otherwise give up
  -- * if ("S-position" > "maxwidth") then give up
  -- * done: table content area begins at "S-position", "minwidth" and
  --   "maxwidth" are assigned (garbage area following the last table
  --   line must be evaluated separately)

  -- "minw=00010 maxw=00200" the length is 21 char:s plus EOL

  -- note that table size "numtbllen" guaranteed from above is at least
  -- 320 (44 by spec) but the table may contain an extra area and a garbage
  -- area and shrink considerably after those are trimmed off or disregarded

  -- search limit will be min(80000,("numtbllen"-3)), 80K is per spec, we need
  -- a safety margin of 3 char:s, and the subtraction cannot underflow

  if (booerr==false) then
    numposirr  = -1
    numposiss  = -1
    numipos    = 0 -- ZERO-based position in "qconstrsr"
    isubnumlim = mathmin(80000,(numtbllen-3))
    isubnumchr = 91 -- look for "[["
    isubboodwn = false -- upwards
    searchdblchr () -- OUT: osubboofnd | USE: qconstrsr | CHG: numipos
    if (osubboofnd) then
      numtblbeg = numipos -- else ZERO
      numposiss = numipos -- else "-1" invalid
    end--if
  end--if

  if ((booerr==false) and (numposiss~=-1)) then
    numipos = 0 -- ZERO-based position in "qconstrsr"
    while (true) do
      if ((numipos+21)>=numposiss) then
        break
      end--if
      numoct = string.byte (qconstrsr,(numipos+1),(numipos+1))
      if (numoct==109) then -- ASCII lowercase "m"
        strtpm = string.sub (qconstrsr,(numipos+2),(numipos+5))
        if (strtpm=="inw=") then
          if (numposirr==-1) then
            numposirr = numipos -- good hit
          else
            numposirr = -1 -- more hits prohibited per spec
            break
          end--if
        end--if (strtpm=="inw=") then
      end--if
      numipos = numipos + 1
    end--while
  end--if

  if (booerr==false) then
    while (true) do -- fake loop
      if ((numposirr==-1) or (numposiss==-1)) then
        booerr = true -- give up, line is not found
        break -- to join mark
      end--if
      if ((numposirr+22) > numposiss) then
        booerr = true -- give up, line is not valid
        break -- to join mark
      end--if
      strtpm = string.sub (qconstrsr,(numposirr+1),(numposirr+22)) -- 22 cha
      numoct = string.byte (strtpm,22,22) -- need EOL
      if (numoct~=10) then
        booerr = true -- give up, line is not valid
        break -- to join mark
      end--if
      strtmp = string.sub (strtpm,11,16)
      if (strtmp~=" maxw=") then
        booerr = true -- give up, line is not valid
        break -- to join mark
      end--if
      strtmp = string.sub (strtpm,6,10)
      if (lfcountndg(strtmp,0)~=0) then -- valid number ?
        booerr = true -- give up, number is not valid
        break -- to join mark
      end--if
      numminwidth = tonumber(strtmp)
      strtmp = string.sub (strtpm,17,21)
      if (lfcountndg(strtmp,0)~=0) then -- valid number ?
        booerr = true -- give up, number is not valid
        break -- to join mark
      end--if
      nummaxwidth = tonumber(strtmp)
      if ((numposiss>nummaxwidth) or (numminwidth<8) or ((numminwidth*2)>nummaxwidth) or (nummaxwidth<40) or (nummaxwidth>40000) or ((nummaxwidth*2)>numtbllen)) then
        booerr = true -- give up, number is not valid
        break -- to join mark
      end--if
      break -- finally to join mark
    end--while -- fake loop -- join mark
    if (bootrace) then
      if (booerr) then
        strtmp = "failure to seize min&max"
      else
        strtmp = "min&max seized: " .. tostring(numminwidth) .. "," .. tostring(nummaxwidth)
      end--if
      qstrtrace = qstrtrace .. "<br>" .. strtmp
    end--if
  end--if

  -- now ZERO-based "numtblbeg" maybe points to the found "[["
  -- we will not touch "numtblbeg" since here but will update "numipos"

  ---- COMPARE THE 0TH Y-INDEX MARKER ----

  if (booerr==false) then
    numipos = numtblbeg
    chk2or3lim () -- IN: numipos, numtbllen | USE: qconstrsr | CHG: numfoulen
    if (numfoulen==0) then
      booerr = true -- barely can occur
    else
      cmp2or3wordeq () -- OUT: osubbooequal | USE: qconstrsr, numiwo
      if (osubbooequal==true) then
        numfndpos = numipos
        booyxfnd = true -- found the y-index marker !!!
      else
        numbinlow = numipos -- points to found "[[", will need this later
      end--if
    end--if
  end--if

  -- now ZERO-based "numfndpos" maybe points to the found "[[...]]"
  -- now "numipos" is irrelevant

  ---- SEARCH FOR THE LAST Y-INDEX MARKER ----

  -- will succeed if we find "[[" within "maxwidth"

  -- if we have only one line then found last index marker will
  -- be the same as the found 0th index marker

  if ((booerr==false) and (booyxfnd==false)) then
    numipos = numtbllen - numminwidth          -- ZERO-based positi in "qconstrsr"
    isubnumlim = 2 * nummaxwidth - numminwidth -- allow full "maxwidth" of garb
    isubnumchr = 91                            -- look for "[["
    isubboodwn = true                          -- downwards
    searchdblchr () -- OUT : osubboofnd | USE : qconstrsr | CHG : numipos
    if (osubboofnd==false) then
      booerr = true -- give up, table is broken, "numipos" irrelevant now
    end--if
  end--if

  -- now ZERO-based "numipos" maybe points to the found "[["

  ---- COMPARE THE LAST Y-INDEX MARKER ----

  if ((booerr==false) and (booyxfnd==false)) then
    chk2or3lim () -- IN: numipos, numtbllen | USE: qconstrsr | CHG: numfoulen
    if (numfoulen==0) then
      booerr = true -- barely can occur
    else
      cmp2or3wordeq () -- OUT: osubbooequal | USE: qconstrsr, numiwo
      if (osubbooequal==true) then
        numfndpos = numipos
        booyxfnd = true ; -- found the y-index word !!!
      else
        numbinhaj = numipos ; -- points to found "[[", will need this later
      end--if
    end--if
  end--if

  -- now ZERO-based "numfndpos" maybe points to the found "[[...]]"
  -- now "numipos" is irrelevant

  ---- PERFORM THE BINARY SEARCH ----

  -- ZERO-based "numbinlow" and "numbinhaj" always point to begi of "[[...]]"

  -- note that here SUB "searchdblchr" can fail with "osubboofnd==false"
  -- if the table exceeds "maxwidth", this is an error

  -- the other fisk with SUB "searchdblchr" is that could hit "numbinhaj"
  -- and find the "[[" there, this is highly undesirable as this would
  -- cause a subsequent invalid comparison with random result, we prevent
  -- this by aborting the binary search at this point

  if ((booerr==false) and (booyxfnd==false)) then
    while (true) do -- reduce size by halving until it becomes too small
      numipos = mathdiv((numbinlow + numbinhaj),2) -- ZERO-based positi
      if (bootrace) then
        strtmp = 'Beginning binary search iteration with ' .. tostring(numbinlow) .. ',' .. tostring(numbinhaj) .. ',' .. tostring(numipos)
        qstrtrace = qstrtrace .. "<br>" .. strtmp
      end--if
      if ((numipos+nummaxwidth+numminwidth)>numbinhaj) then
        break -- NOT found, continue with desperate last step linear search
      end--if
      isubnumlim = nummaxwidth -- do NOT subtract haer
      isubnumchr = 91          -- search for "[["
      isubboodwn = false       -- upwards
      searchdblchr () -- OUT : osubboofnd | USE : qconstrsr | CHG : numipos
      if (osubboofnd==false) then
        booerr = true
        break -- give up, not found or table brokn, "numipos" irrelevant now
      end--if
      chk2or3lim () -- IN: numipos, numtbllen | USE: qconstrsr | CHG: numfoulen
      cmp2or3wordeq () -- OUT: osubbooequal | USE: qconstrsr, numiwo
      if (osubbooequal==true) then
        numfndpos = numipos
        booyxfnd = true ;
        break ; -- found the y-index word !!!
      end--if
      cmp2or3wordbi () -- OUT: osubboobiger | USE: qconstrsr, numiwo
      if (osubboobiger==true) then -- "true" if found y-index word > "numiwo"
        numbinhaj = numipos -- was too high, check below, reduce "numbinhaj"
      else
        numbinlow = numipos -- was too low, check above, increas "numbinlow"
      end--if
    end--while
  end--if

  -- below look at "booerr", then at "booyxfnd",
  -- then at "numfndpos" or ("binlow" and "binhaj")

  ---- PERFORM THE DESPERATE LAST STEP LINEAR SEARCH ----

  -- search through range "binlow" to "binhaj", this can be more than
  -- "maxwidth" (almost double size in worst case)

  -- there definitely is "[[" at both "binlow" and "binhaj" and there can
  -- be ZERO, ONE or several further "[[" in the range

  -- so we indeed subtract "minwidth" from the range size two times

  if ((booerr==false) and (booyxfnd==false)) then
    numipos = numbinlow ;
    while (true) do -- there can be several lines left
      numipos = numipos + numminwidth ;
      isubnumlim = numbinhaj - numipos - numminwidth ; -- is "[[" at "binhaj"
      if (isubnumlim<numminwidth) then
        booerr = true
        break ; -- give up, not found or table brokn, "numipos" irrelevant now
      end--if
      isubnumchr = 91    -- search for "[["
      isubboodwn = false -- upwards
      searchdblchr ()    -- OUT : osubboofnd | USE : qconstrsr | CHG : numipos
      if (osubboofnd==false) then
        booerr = true
        break ; -- give up, not found or table brokn, "numipos" irrelevant now
      end--if
      chk2or3lim () ; -- IN: numipos, numtbllen | USE: qconstrsr | CHG: numfoulen
      cmp2or3wordeq () ; -- OUT: osubbooequal | USE: qconstrsr, numiwo
      if (osubbooequal==true) then
        numfndpos = numipos
        break -- found the y-index word but "booyxfnd" not needed anymore !!
      end--if
    end--while
  end--if

  -- below not look at "booyxfnd", look at "booerr" only and maybe "numfndpos"

  ---- RETURN ONE ELEMENT PICKED ACCORDING TO X-INDEX NUMBER IF 0...9x10 ----

  -- This is done in 3 + 1 steps:
  -- * skip possible unsuitable elements
  -- * skip possible whitespace preceding the searched element
  -- * copy the searched element (takes a char from previous step)
  -- * strip off rl-lr-enclosement if requested (boostrip=true) and it is present

  -- this is a copy of module "mpiksubstrind" with minimal modifications

  -- "numxin" can be 0...9 (soon 0...10) and values > 100 are reserved

  if ((booerr==false) and (numxin<100)) then
      booxyfnd = false
      numipos = numfndpos + 4 + numinxlen -- ZERO-based index in "qconstrsr"
      numite = numxin -- count down
      while (true) do -- iterate through elements in order to skip them
        if (numite==0) then -- nothing to skip anymore
          booxyfnd = true ;
          break ; -- exit the outer loop, nothing to skip anymore
        end--if
        numite = numite - 1
        booxyfnd = false
        boouch = false
        while (true) do -- iterate through whspc & chars mix looking for comma
          if (numipos>=numtbllen) then
            break ; -- exit the inner loop, NOT found, give up
          end--if
          numipos = numipos + 1 ;
          numoct = string.byte (qconstrsr,numipos,numipos) ;
          if ((numoct<32) and (numoct~=9)) then
            break -- EOL: exit the inner loop, NOT found, give up
          end--if
          if ((numoct>32) and (numoct~=44)) then
            boouch = true ; -- found a valid char (empty field is an error)
          end--if
          if (numoct==44) then
            if (boouch==true) then
              booxyfnd = true ; -- field was not empty
            end--if
            break ; -- exit the inner loop, found a comma, this is good or bad
          end--if
        end--while
        if (booxyfnd==false) then
          break -- exit the outer loop, NOT found or empty field, give up
        end--if
      end--while
      if (booxyfnd==true) then
        booxyfnd = false ;
        while (true) do -- iterate through chars skipping whitespace
          if (numipos>=numtbllen) then
            break ; -- exit the loop, NOT found, give up
          end--if
          numipos = numipos + 1 ;
          numoct = string.byte (qconstrsr,numipos,numipos)
          if ((numoct<32) and (numoct~=9)) then
            break -- EOL: exit the loop, NOT found, give up
          end--if
          if (numoct>32) then
            if (numoct==44) then
              break ; -- exit loop, found comma, give up according to rules
            end--if
            booxyfnd = true ;
            break ; -- exit the loop, found valid char, start copying now
          end--if
        end--while
      end--if
      if (booxyfnd==true) then
        while (true) do -- iterate through chars copying the found element
          if ((numoct==32) or (numoct==9)) then
            boospc=true
          else
            if (boospc==true) then
              strret = strret .. " " -- was preassigned to empty
              boospc = false
            end--if
            strret = strret .. string.char (numoct)
          end--if
          if (numipos>=numtbllen) then
            break ; -- exit the loop, done
          end--if
          numipos = numipos + 1
          numoct = string.byte (qconstrsr,numipos,numipos)
          if ((numoct<32) and (numoct~=9)) then
            break -- EOL: exit the loop, done
          end--if
          if (numoct==44) then
            break -- comma: exit the loop, done
          end--if
        end--while
      end--if
      if (booxyfnd==false) then
        booerr = true -- damn: failed in the x-search
      end--if
  end--if

  -- strip off rl-lr-enclosement if requested (boostrip=true) and it is present

  if ((booerr==false) and (numxin<100) and (boostrip==true)) then
    strret = lfrlstrip (strret)
  end--if

  ---- RETURN COMPLETE LINE IF SPECIAL "+" AKA 177 ----

  -- we do reduce excessive whitespace

  if ((booerr==false) and (numxin==177)) then
      boospc = false
      numipos = 0 -- limit width again
      while (true) do -- iterate through char:s copying the found line
        if (numipos>=nummaxwidth) then
          booerr = true
          break -- damn: violated "maxwidth"
        end--if
        numoct = string.byte (qconstrsr,(numfndpos+1),(numfndpos+1)) ;
        if ((numoct<32) and (numoct~=9)) then
          break -- done (anything < 32 other than TAB counts as EOL)
        end--if
        if ((numoct==32) or (numoct==9)) then
          boospc=true
        else
          if (boospc==true) then
            strret = strret .. " " -- was preassigned to empty
            boospc = false
          end--if
          strret = strret .. string.char (numoct) -- was preassigned to empty
        end--if
        numipos = numipos + 1
        numfndpos = numfndpos + 1
      end--while
  end--if

  ---- CARE ABOUT PROHIBITED EQUAL SIGN ----

  if (strret=='=') then
    strret = '-' -- string "=" is prohibited, punishment is conversion to "-"
  end--if

  ---- CARE ABOUT ERRORS ----

  -- this is NOT final or valid for input x-index number "-" AKA 199

  if (booerr==true) then
    strret = strerrequ -- string "strret" was preassigned to empty
  end--if

  ---- CARE ABOUT DASH TRANSLATION ----

  if (strret=='-') then
    strret = stroutdsh
  end--if

  ---- RETURN BINARY DIGIT IF SPECIAL X-INDEX NUMBER "-" AKA 199 ----

  if (numxin==199) then
    if (booerr==true) then
      strret = "0" -- y-index word NOT found or error
    else
      strret = "1" -- y-index word found
    end--if
  end--if

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

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

end--function

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

return piktbllki