Modulo:mind12dim

El Vikivortaro
Salti al navigilo Salti al serĉilo
Memtesto disponeblas sur la dokumentaĵa subpaĝo

--[===[

MODULE "MIND12DIM" (index 1 or 2 dimensions)

"eo.wiktionary.org/wiki/Modulo:mind12dim" <!--2020-Jan-19-->
"id.wiktionary.org/wiki/Modul:mind12dim"

Purpose: creates a 1-dimensional or 2-dimensional index list with or
         without table, for a category or "Special:AllPages" or
         "Special:Prefixindex", with various types of links (with
         "from=" queries or to a section "#"... or with raw
         letter postfix)

Utilo: kreas 1-dimensian aux 2-dimensian enhavsuperrigardon kun aux
       sen tabelo, por kategorio aux "Special:AllPages" aux
       "Special:Prefixindex", kun ...

Manfaat: membuat daftar indeks berdimensi 1 atau 2 ...

Syfte: skapar en 1-dimensionell eller 2-dimensionell indexlista ...

Used by templates / Uzata far sxablonoj:
- EO: "eht-kat-alfa", "eht-pagxo-alfa", "ind12dim"

Required submodules / Bezonataj submoduloj:
- none / neniuj

This module is special in that it can take parameters both those sent
to itself (own frame) and those sent to the caller (caller's frame).

Incoming: - 11 named optional parameters (empty parameters are treated as
            exactly equivalent to inexistent ones, this allows empty
            forwarding like "tit={{{tit|}}}")
            - "fpn=" -- fullpagename (empty or 5...100 octet:s, non-empty
              and must contain no or exactly one colon ":" (not
              beginning nor last), for example "Special:AllPages" or
              "Kategorio:Volapuko" or "Daftar unsur kimia", using this module
              from namespace ZERO can be useful on wikipedia but probably NOT
              on wiktionary, fullpagename taken if this parameter is missing
              or empty, no "<b>" and no "'''" (ie bolding) and no links
              tolerable in this parameter, singe inner spaces and single
              apo:s anywhere permitted, note that wiki software trims away
              leading and trailing spaces even if they are "encoded"
              with underscore "_", if pagenames look like
              ("Dictionary a", "Dictionary b", etc) then submit only
              "Dictionary" without final space and use link type 2 with
              tuning options, or special value "fpn=-" for section links
              (same page) base link type guessed to 4 then and no
              other value is legal)
            - "tit=" -- title of the table (empty or 5...120 octet:s, for
              example <<INDEKSO DE KATEGORIO "SVEDA">>, copy of parameter
              "fpn" (in turn received from caller or peeked fullpagename)
              taken if this parameter is missing or empty, or hardcoded
              text "Index" if "fpn=-", special value "-" to suppress the
              table in favor of a raw list, then
              the caller must provide <<class="plainlinks">>, no "<b>"
              and no "'''" (ie bolding) and no links tolerable
              in this parameter)
            - "dud=1" -- causes a 2-dimensional table to be
              created (default is 1-dimensional, "dud=1" is prohibited
              with "tit=-", "dud=1" is prohibited with base type
              (from "typ=") other than 0 or 1 or 5 or 6, conversely base
              types 5 and 6 require "dud=1")
            - "typ=" -- type of link (empty or 3 DEC digits, base types
              are "0"..."6" (see below) and two extra digits are tuning
              options, by default base type is auto-guessed from "fpn=")
            - "mwp=1" -- causes <<#mw-pages>> to be attached to the URL
              (default is false, "mwp=1" works and is tolerable only with
              categories, does NOT work with "Special:AllPages")
            - "ans=" -- namespace for "Special:AllPages" (empty or 1...5
              DEC digits 0...5'000, attached to the request as for example
              "namespace=108", default is empty resulting in main ie NS=0,
              tolerable only for "Special:", works only (??) for "AllPages")
            - "alf=" -- alphabet (empty or 5...400 octet:s, default is
              "ABCDEFGHIJKLMNOPQRSTUVWXYZ", may contain uppercase letters
              A...Z, numbers 0...9, dash "-", apo "'", and unicode, may NOT
              contain "~" nor other non-letters, lowercase is prohibited for
              categories, order is irrelevant but may NOT contain dupes,
              special syntax with brackets "ijkl(ll)mn" to define a
              "multi-char letter" for some silly languages or special cases,
              must contain 2...9 octet:s and 2...9 UTF8 char:s, at the
              end the alphabet must contain at least 3 elements AKA
              UTF8 char:s or "multi-char letter":s)
            - "fla=" -- two extra links "top" and "bottom" (for link type 0
                or 1 better named as "all" and remainder")
              - default if the parameter is not given is to auto-guess:
                - for base type (from "typ=") of 0 or 1: show them both with
                  visible texts "all" and "remainder" and link texts "^"
                  and "~" (equivalent four words "^", "all", "~", "remainder"
                  ie parameter "fla=^,all,~,remainder")
                - for base type (from "typ=") of 2...6 : suppress them both
               - possible values:
                - "-" suppress them both
                - two words (5...80 octet:s), string must contain exactly
                  one comma "," and none of the substrings may be empty,
                  for example ("cxio,restajxo" or "semua,tertinggal"
                  or "allt,rest"), link texts will be:
                  - for base "typ=" of 0 or 1 : "^" and "~"
                  - for base "typ=" of 2...6 : copies of the visible texts
                - four words (5...80 octet:s), string must contain exactly
                  three commas and none of the substrings may be empty,
                  intended for base "typ=" of 1, the order is:
                  - link text top ("^" for empty)
                  - visible text top
                  - link text bottom
                  - visible text bottom
                note that "^" and "~" can be used for link types 0 and
                1 only, they are probihited even for type 5 (combo of 1+3)
            - "ctb=" -- background colour theme of the table (0...5, for 6
              permutations of RGB, default is 0, prohibited with "tit=-")
            - "cos=" -- 6 ext hex digits (tuning of text colour, (size),
              enclosing and separators, default "000000", can be used
              even with "tit=-")
              - always available 2 colours (0 default, 1...3 RGB, 4...6 CMY,
                7 grey (7 posible values 1...7), add 7 for light (8...14/E),
                add 14 for dark (15/F...21/L)
                - (pos 0) font colour
                - (pos 1) text background colour
                those 2 colour values may NOT be both non-ZERO and same
              - (pos 2) enclosing control ("0" -- add []
                brackets | "1" -- do not add them)
              - (pos 3) reserved and ignored
              - in 1-dimensional table or if no table with "tit=-"
                 - (pos 4) "0"..."6" type of separator (0 for NBSP or 1...6,
                   default NBSP)
                 - (pos 5) bool "0" or "1" ("1" to enhance the separator
                    with NBSP on both sides, not valid for separator "0",
                    default false)
              - in 2-dimensional table
              - (pos 4) reserved and ignored
              - (pos 5) reserved and ignored
            - "pre=" -- prefix for items of the alphabet, 1...9 octet:s
              (9 octet:s can be as few as 2 UTF8 char:s + 1 ASCII char),
              long prefixes (more than 2 UTF8 char:s) are nonrecommended,
              "pre=" is prohibited together with "dud=1"
            Note that "mwp=1" and "ans=" can NEVER be used at same time.
            Note that "ans=" requires base link type ZERO, "typ=000" can be
            used but is redundant, no other value than "000" is legal.

Returned: - large text with wikicode for the desired table

Special trick: if there is a parameter "caller=true" on the own frame then
               the own frame is discarded in favor of the caller's one.

Parameters "dud=1" and "mwp=1" are bool.

Terminology:
- "http link": link brewed via "mw.uri.canonicalUrl" due to query part
  but here always remaining inside the wiki, to other or the already
  displayed page, single brackets, space as separator, can be a "selflink"
  at same time
- "wiki link": link to a wiki page in same or other namespace without "http"
  stuff but with fullpagename, here we do not use project or language
  prefixes, double brackets, wall as separator, can be a "link to
  section" at same time
- "section link": link beginning with "#" linking to a section on the
  displayed page itself, lacks fullpagename, double brackets, wall
  as separator
- "selflink": link the the page itself with a query part making it useful,
  special type of "http link", there is no point with a "wiki link" to
  point to the displayed page itself
- "link to section": type of "wiki link" containing both fullpagename
  and a section part

Wikiprojects:
Calling this module from namespace ZERO can be useful on wikipedia but
probably NOT on wiktionary. The module itself works with namespace ZERO.
Whatever is said about appendix pages is based on the assumption that
we are on wiktionary, but applies equally to appendix pages on wikipedia,
and article pages in namespace ZERO on wikipedia.

But we need further separate 6 bools to mark presence of following
string and integer parameters, even if default value is supplied:
- "typ=" (empty means auto-guess and tuning options OFF, NOT
          the same as explicit ZERO by "000")
- "ans=" (empty is default ZERO, explicit ZERO may be illegal)
- "fla=" (empty means auto-guess)
- "ctb=" (empty is default ZERO, explicit ZERO may be illegal)
- "cos=" (empty is default ZERO, explicit ZERO by "000000" may be illegal)
- "pre=" (default is empty, explicit empty is NOT possible)

Following 5 parameters (5 strings processed in different ways) are seized
elsewhere, auto-guessed, copied or filled with non-empty stuff rather
than kept empty if not supplied by the caller:
- "fpn=" (empty means read "fullpagename", separate bool "boohavfpn")
- "tit=" (empty means copy from "fpn=" that in turn can
          come from "fullpagename", no bool)
- "typ=" (empty means auto-guess, separate bool "boohavtyp")
- "alf=" (empty means use default "ABCDEFGHIJKLMNOPQRSTUVWXYZ", no bool)
- "fla=" (empty means auto-guess, 1 + 4 string
          variables and a separate bool)

Following 3 string parameters allow for special value "-" NOT
stored in the string but in a separate bool:
- "fpn=-" (bool is "boofpns")
- "tit=-" (bool is "bootits")
- "fla=-" (bool is "booflas")

Dependencies:
- "tit=" depends from "fpn="
- "typ=" depends from "fpn="
- "fla=" depends from "typ=" ("fpn=" -> "typ=" -> "fla=")

This module is unbreakable (when called with correct module name
and function name).

Cxi tiu modulo estas nerompebla (kiam vokita kun gxustaj nomo de modulo
kaj nomo de funkcio).

Single apo:s are permitted in wiki pagenames and some wiktionaries use them,
even as beginning or last char. Multiple consecutive apo:s are deprecated
and cause trouble, and only sysops may create such broken pages. This module
will replace a double apo coming from a 2-dimensional table by "N/A".

Wiki software allows for short section links formatted like "[[#Z|Z]]", the
effect is very same as with "[[{{FULLPAGENAME}}#Z|Z]]", a full URL is shown
in the browser's preview area, but nothing is loaded, the page just jumps
to the anchor when the link is clicked. This does NOT work with edit preview.

Listing of pages in categories is inherently case insensitive even on
wiktionary, but the order is dubious (maybe upper -> lower -> mixed, for
example "FAN" -> "fan" -> "Fan").

Listing in "Special:AllPages" (in EO called "Specialajxo:CXiuj_pagxoj") and
"Special:Prefixindex" (in EO called "Specialajxo:Indekso_de_prefiksoj") is
case sensitive on wiktionary, and case sensitive except earliest letter on
wikipedia.

There are two equivalent syntaxes for "Special:Prefixindex"
and "Special:AllPages":
- "Special:Prefixindex/a" and "Special:Prefixindex?from=a"
- "Special:AllPages/a" and "Special:AllPages?from=a"
The former one with a slash has the crucial benefit that it does NOT
need a URL query part and can thus be accessed via wiki links instead
of more bloated http links.

The URL is formed like this:
- https://en.wiktionary.org/w/index.php?title=Category:English_lemmas
- https://eo.wiktionary.org/w/index.php?title=Speciala%C4%B5o:%C4%88iuj_pa%C4%9Doj
- index.php?title=Category:English_lemmas
- index.php?title=Category:English_lemmas#mw-pages
- index.php?title=Category:English_lemmas&from=QQ#mw-pages
- index.php?title=Special:AllPages
- index.php?title=Special:AllPages?from=ZAKAR&namespace=108
- index.php?title=Speciala%C4%B5o:%C4%88iuj_pa%C4%9Doj&from=W
- index.php?title=Speciala%C4%B5o:%C4%88iuj_pa%C4%9Doj&from=~

Notes:
- Use parameter value "from=~" to point content after ASCII
  range (works with both categories and "Special:AllPages").
- Optional string "#mw-pages" can be attached to the URL to present
  the page scrolled down to the listing, this works with categories,
  but does NOT work with "Special:AllPages".
- Optional parameter "namespace=" can select namespace for
  "Special:AllPages", default is ZERO (NOT all namespaces), the
  possible namespace prefix is showed, the "from=" string is applied
  AFTER the namespace prefix.
- Note that for "Special:Prefixindex" the default namespace is
  all namespaces, NOT ZERO.
- Do NOT use "namespace=" with "Special:Prefixindex" because it gives
  messy results (indeed restricts to desired namespace, but switches
  behaviour to "Special:AllPages" (can list too much) and does NOT
  show the namespace prefix).

- UTF8 is defined by "RFC 3629" from 2003-Nov (but already used to
  exist before, though)
- UTF8 sigi AKA BOM : HEX: $EF $BB $BF | DEC: 239 187 191 | ABS: $FEFF
- Absolute unicode range has 17 (seventeen !!!) planes per 65'536 values,
  total 1'114'112, most of them are unused, plane ZERO is somewhat full, other
  ones are almost or totally empty, official notation: "U+0000..U+10FFFF"
- Range ZERO to 31 is valid by RFC but useless, same for 127,
  128 to 159, and 160 (AKA nbsp)
- Range "U+D800" to "U+DFFF" is invalid by RFC
- UTF8 starting octet can be only $C2 to $DF , $E0 to $EF , $F0 to $F4
  giving a continuous range from $C2 to $F4 of size $33 = #51 values
- UTF8 subsequent octet's (1 or 2 or 3) can be only $80 to $BF
  (6 bit:s, 64 possible values)
- The octet values $C0, $C1 and $F5 to $FF may never appear in a UTF8 file

- $00'FFFF -> $EF,$BF,$BF
- $01'0000 -> $F0,$90,$80,$80
- $0F'FFFF -> $F3,$BF,$BF,$BF
- $10'0000 -> $F4,$80,$80,$80
- $10'FFFF -> $F4,$8F,$BF,$BF

Abs. char number range |      UTF8 octet sequence       | beginning octet
   (hexadecimal)       |            (binary)            |
-----------------------+--------------------------------+------------------
0000'0000 to 0000'007F | 0xxxxxxx                       | $00        to $7F
0000'0080 to 0000'07FF | 110xxxxx 10xxxxxx              | $C0 -> $C2 to $DF
0000'0800 to 0000'FFFF | 1110xxxx 10xxxxxx 10xxxxxx     | $E0        to $EF
0001'0000 to 0010'FFFF | 11110xxx 10xxxxxx 10xxxxxx ... | $F0 to $F7 -> $F4

Base color for "border" is #2060A0 and for "background-color" #D0E0F0 with
permutation index ZERO.

Brew table (unless "tit=-" is used) with:
- '<table class="pla
   inlinks" style="margin:0.5em auto 0.5em auto; border:2px solid #'
- '; background-color:#'
- '; padding:5px; text-align:center;">'

For 1-dimensional table the content is:
- "tr" and "td" with title from "tit="
- "tr" and "td" with "br"
- "tr" and "td" with the list with links (see below) including the
  2 possible extra items "top" and "bottom", there is only
  one shared "td" for the complete list

For 2-dimensional table (link types 0 or 1 or 5 or 6 only) the content is:
- "tr" and "td" with title from "tit=" with appropriate colspan
- "tr" and "td" with "br" with appropriate colspan
- many "tr" rows and every of them containing many "td" cells with the
  elements of the lists with links (see below), every link has a private
  cell, the 2 extra items "all" and "remainder" NOT included here
- "tr" with the 2 extra items "all" and "remainder" are in 2 separate
  "td" cells in a separate row at the botom, both have appropriate colspan

Structure of the list with links is:
- maybe extra "top" item (unless "fla=-" us used)
- alphabet items
- maybe extra "bottom" item (unless "fla=-" us used)

The types of links ("typ=") are:
- 0 "from="               (http link, for categories and special pages, for
                          categories link to other page or selflink, for
                          special pages link to other page only, possible but
                          deprecated unless "ans=" is used)
- 1 wiki link + "/"       (for special pages, unless "ans=" is used,
                          prohibited for categories)
- 2 wiki link + raw pofi  (for appendixes, wiki link to other page only,
                          link to section not possible)
- 3 wiki link + "#"            (for appendixes, wiki link to other page
                               can be a link to section or not)
- 4 "#" section link           (for appendixes)
- 5 combo 2-part link "/"+"#"  (1+3, prohibited for categories)
- 6 combo 2-part link raw+"#"  (2+3, prohibited for categories)

Guessing defaults to type 4 section link if "fpn=-" is used, otherwise to 1
if the page is special and "ans=" is ZERO (default or explicit), otherwise to
0 if the page is category or special, otherwise to 3. The types 1 and 2 and 3
are very similar, the point with having them separate is that we do not have
to guess tuning with "#" or "/" that we otherwise would need to if we merged
those 3 types. Note that is is NOT possible to combine 0 "from=" with 3 "#".

There are several crucial differences between link type 0 (http
link) and types 1...3 (wiki link):
- http links are always blue, wiki links can be red
- queries like "from=" are possible with http links only
- wiki links to categories can cause malicious problems in the form of adding
  the page into a category instead of showing a link, we have to auto-add
  ":" in such cases (note that "fpn=" cannot begin with a colon)
- http links are substantially more bloated, this can cause trouble
  especially with big 2-dimensional tables

The tuning options ("typ=", last 2 of totally 3 digits) are:
  - case tuning (only for base link types 1 "/" and 2 "raw"
    and 5 "combo" and 6 "combo"):
    - "0" - no tuning
    - "1" - change beginning letter to lowercase
    - "2" - change beginning letter to uppercase
    - "3" - change all letters to lowercase
    - "4" - change all letters to uppercase
  - gap tuning (only for base link types 2 and 6):
    - "0" - no tuning
    - "1" - add a space (note that "fpn=" may NOT end with a space)

Note that link type 4 ie section link duplicates the automatic creation
of an index by wiki software and HTML, thus "__NOTOC__" will be
needed. For alphabetic sections (A,B,C,...), even with a prefix
(YA,YB,YC,...), a horizontal index generated by this module is usually
preferable over the default vertical index. Flaw of this trick is that we
need an alphabet, nobody will automatically evaluate all "==" headings for us.

The "fla=" parameter must be used differently for link types 1...3 as opposed
to type 0 since there is no point with "#~" as opposed to "from=~". Instead of
"~" we will add the visible words or other specified words after the cross
"#", the two words will usually be something like "Index" (top) and
"Notes" (bottom).

For "typ=" link types 2...6 the default for "fla=" is "fla=-". Other
values are permitted, and even useful for type 3, but barely useful for 2.

Structure of a single http link (type 0 only) is:
- "["
- "canonicalurl" of: - "fpn=" as fullpagename
                     - possible "from=" thing as parameter
                     - possible "namespace=" as parameter if "ans=" has
                       a value different from ZERO
- possible <<#mw-pages>> if "mwp=1" (only categories)
- space
- visible link string (see below)
- "]"

The structure of a single wiki link (1,2,3) and
section link (4) and combo link (5,6) is:
- "[["
- ":" if it is a category for type 2 (crucial!!!)
- "fpn=" as fullpagename for type 1,2,3 and 5,6
- slash "/" for types 1 and 5
- possible optional gap char (space) and (optionally
  case-tuned) raw letter or string for type 1 or 2 or 5 or 6
- possible #-part with a letter for type 3...6
- wall
- visible link string (see below)
- "]]"

Structure of the visible link string is:
- for letters and other signs the single character enclosed in "["..."]"
  (unless enclosing deselected by subparameter in "cos=")
- for words from "fla=" ("all" and "remainder" by default for type 0) the
  word enclosed in "("...")"

Warning about bloat: This module can challenge the limits of the wiki
software. Given an alphabet with 35 letters, including lowercase 70, a
2-dimensional table will have 4'900 elements. If one element needs 150
octet:s (this is realistic) then the complete table will be only 720 KiO
in size, this is more than 1/3 of the limit of 2 MiO. To minimize the risk
of problems, you should prevent accumulation of following trouble factors:
- 2-dimensional table
- long fullpagenames with much non-ASCII text -- little can be do, maybe
  move them, maybe use English terms for special pages and namespace prefixes
- long alphabet -- little can be do, maybe split into several smaller tables
- link type 0 (http links) -- use other type whenever possible
- non-default colour of the text requested by "cos=" -- try to avoid
- enclosing brackets (on by default, but can be disabled by "cos=")

Internal error codes (continuous mapping from error code
to parameter ONE-based index NOT required):
-----  ----  -----------
error  para  description
-----  ----  -----------
#E00    --   OK
#E01    --   internal error (constant strings EO vs ID have
             not been uncommented)
#E02    --   undesirable anonymous parameter
#E03     1   "fpn=" bad
#E04     2   "tit=" bad generic except illegal stuff
#E05     2   "tit=" bad due to illegal links or transclusions or apo:s
#E06     3   "dud=" bad
#E07 (reserved)
#E08     4   "typ=" bad (3 digits) generic except bad base
#E09     4   "typ=" bad (3 digits) bad base link type
#E10     5   "mwp=" bad
#E11     6   "ans=" bad
#E12 (reserved)
#E13     7   "alf=" bad generic except lowercase and dupe
#E14     7   "alf=" bad due to lowercase with category
#E15     7   "alf=" bad due to dupe
#E16     8   "fla=" bad
#E17     9   "ctb=" bad
#E18    10   "cos=" bad (6 ext hex digits) generic except equal colours
#E19    10   "cos=" bad (6 ext hex digits) both colours are non-ZERO and equal
#E20 (reserved)
#E21    11   "pre=" bad generic except illegal stuff
#E22    11   "pre=" bad due to illegal links or transclusions or apo:s
#E23 (reserved)
#E24 (reserved)
#E25    --   inconsistent explicitly specified parameters
             - "tit=-" and "dud=1"
             - "tit=-" and "ctb="
             - "dud=1" and "base link type" <> 0 1 5 6
             - "dud=0" and "base link type" is 5 or 6 (note that 5 or
                                                       6 is never guessed)
             - "fpn=-" and "base link type" <> 4
             - "ans="  and "base link type" <> 0
             - "fpn=" given (not "-") and "base link type" = 4
             - "dud=1" and "pre="
             - "mwp=1" and "ans="
#E26    --   inconsistency with fullpagename and defaults and guesses
             - "mwp=1" and non-cat
             - "ans="  and non-special
             - "dud=1" and "base link type" <> 0 1 5 6
             - "fpn=-" and "base link type" <> 4
             - "ans="  and "base link type" <> 0
             - cat     and "base link type" <> 0 2
             - special and "base link type" <> 0 1
-----  ----  -----------

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

* #T00 (no params, default table, peek fullpagename, default colour ZERO)
* "{{#invoke:mind12dim|ek}}"

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

* #T01 (illegal anonymous parameter, error #E02)
* "{{#invoke:mind12dim|ek|stultulo}}"

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

* #T02 (explicit colour test, default table, peek fullpagename, colour ZERO, same output as case #T00 above)
* "{{#invoke:mind12dim|ek|ctb=0}}"

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

* #T03 (explicit colour test, default table, peek fullpagename, colour 1)
* "{{#invoke:mind12dim|ek|ctb=1}}"

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

* #T04 (explicit colour test, default table, peek fullpagename, colour 2)
* "{{#invoke:mind12dim|ek|ctb=2}}"

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

* #T05 (explicit colour test, default table, peek fullpagename, colour 3)
* "{{#invoke:mind12dim|ek|ctb=3}}"

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

* #T06 (explicit colour test, default table, peek fullpagename, colour 4)
* "{{#invoke:mind12dim|ek|ctb=4}}"

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

* #T07 (explicit colour test, default table, peek fullpagename, colour 5)
* "{{#invoke:mind12dim|ek|ctb=5}}"

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

* #T08 (explicit colour test, default table, peek fullpagename, colour 6, invalid, error #E17)
* "{{#invoke:mind12dim|ek|ctb=6}}"

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

* #T10 (maximal parameter usage (8 of 11) with "mwp=1" whereas "ans=" is NOT possible, "dud=1" used while "pre=" is NOT possible)
* "{{#invoke:mind12dim|ek|fpn=Kategorio:Verbo|tit=mojosa indekso|dud=1|mwp=1|alf=EDCBA|fla=everything,leftover|ctb=5|cos=000000}}"

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

* #T11 (maximal parameter usage (8 of 11) with "ans=" whereas "mwp=1" is NOT possible, "dud=1" used while "pre=" is NOT possible)
* "{{#invoke:mind12dim|ek|fpn=Special:AllPages|tit=mojosa indekso|dud=1|ans=12|alf=EDCBA|fla=everything,leftover|ctb=5|cos=000000}}"

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

* #T12 (bad usage (9 of 11) due to both "mwp=1" and "ans=", error #E25)
* "{{#invoke:mind12dim|ek|fpn=Special:AllPages|tit=mojosa indekso|dud=1|mwp=1|ans=12|alf=EDCBA|fla=everything,leftover|ctb=5|cos=000000}}"

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

* #T13 (bad usage (9 of 11) due to both "dud=1" and "pre=", error #E25)
* "{{#invoke:mind12dim|ek|fpn=Kategorio:Verbo|tit=mojosa indekso|dud=1|mwp=1|alf=EDCBA|fla=everything,leftover|ctb=5|cos=000000|pre=DATOR}}"

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

* #T14 (maximal parameter usage (9 of 11) with "ans=" whereas "mwp=1" is NOT possible, "typ=000" is redundant, text colour test)
* "{{#invoke:mind12dim|ek|fpn=Special:AllPages|tit=mojosa indekso|dud=1|typ=000|ans=12|alf=GHIJKLMNOP|fla=everything,leftover|ctb=5|cos=420000}}"

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

* #T15 (maximal parameter usage (9 of 11) with "ans=" whereas "mwp=1" is NOT possible, "typ=100" is criminal, text colour test, error #E25)
* "{{#invoke:mind12dim|ek|fpn=Special:AllPages|tit=mojosa indekso|dud=1|typ=100|ans=12|alf=GHIJKLMNOP|fla=everything,leftover|ctb=5|cos=420000}}"

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

* #T16 (maximal parameter usage (9 of 11) with "ans=" whereas "mwp=1" is NOT possible, text colour test, invalid colours "33", error #E19)
* "{{#invoke:mind12dim|ek|fpn=Special:AllPages|tit=mojosa indekso|dud=1|typ=000|ans=12|alf=EDCBA|fla=everything,leftover|ctb=5|cos=330000}}"

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

* #T17 (no "dud=1", "fpn=" explicit "Special:AllPages", "tit=-" no table, "typ=" guessed to "100" (wiki links), "alf=" explicit)
* "{{#invoke:mind12dim|ek|fpn=Special:AllPages|tit=-|alf=KLMNOPQR}}"

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

* #T18 (no "dud=1", "fpn=" explicit "Special:AllPages", "tit=-" no table, "ans=102", "typ=" guessed to "000" (http links), "alf=" explicit)
* "{{#invoke:mind12dim|ek|fpn=Special:AllPages|ans=102|tit=-|alf=KLMNOPQR}}"

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

* #T19 (no "dud=1", "fpn=" explicit "Special:AllPages", "tit=-" no table and caller provides "plainlinks", "typ=" guessed to "000", "alf=" explicit)
* "<span class="plainlinks">{{#invoke:mind12dim|ek|fpn=Special:AllPages|ans=102|tit=-|alf=KLMNOPQR}}</span>"

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

* #T20 (no "dud=1", "typ=" guessed, text colour test here "EL", separator test here default)
* "{{#invoke:mind12dim|ek|fpn=Kategorio:Verbo|tit=mojosa indekso|mwp=1|alf=HGFEDCBA|fla=everything,leftover|ctb=5|cos=EL0000}}"

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

* #T21 (no "dud=1", "typ=" guessed, text colour test here "LE", separator test here "10")
* "{{#invoke:mind12dim|ek|fpn=Kategorio:Verbo|tit=mojosa indekso|mwp=1|alf=HGFEDCBA|fla=everything,leftover|ctb=5|cos=LE0010}}"

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

* #T22 (no "dud=1", "typ=" guessed, text colour test here "LE", separator test here "01", invalid, error #E18)
* "{{#invoke:mind12dim|ek|fpn=Kategorio:Verbo|tit=mojosa indekso|mwp=1|alf=HGFEDCBA|fla=everything,leftover|ctb=5|cos=LE0001}}"

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

* #T23 (no "dud=1", "typ=" guessed, text colour test here "LE", separator test here "20")
* "{{#invoke:mind12dim|ek|fpn=Kategorio:Verbo|tit=mojosa indekso|mwp=1|alf=HGFEDCBA|fla=everything,leftover|ctb=5|cos=LE0020}}"

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

* #T24 (no "dud=1", "mwp=1", "typ=" guessed, text colour test here "LD", separator test here "60")
* "{{#invoke:mind12dim|ek|fpn=Kategorio:Verbo|mwp=1|alf=HGFEDCBA|fla=everything,leftover|ctb=5|cos=LD0060}}"

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

* #T25 (no "dud=1", no "mwp=1", "typ=" guessed, text colour test here "LC", separator test here "61")
* "{{#invoke:mind12dim|ek|fpn=Kategorio:Verbo|mwp=1|alf=HGFEDCBA|fla=everything,leftover|ctb=5|cos=LC0061}}"

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

* #T30 (no "dud=1", "fpn=" explicit cat "Category:", "typ=" guessed, "alf=" explicit)
* "{{#invoke:mind12dim|ek|fpn=Category:Verbo|alf=HGFEDCBA}}"

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

* #T31 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" explicit)
* "{{#invoke:mind12dim|ek|fpn=Kategorio:Verbo|alf=HGFEDCBA}}"

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

* #T32 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" is criminal with lowercase, #E14)
* "{{#invoke:mind12dim|ek|fpn=Kategorio:Verbo|alf=HGFEdCBA}}"

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

* #T33 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" is criminal with dupe, #E15)
* "{{#invoke:mind12dim|ek|fpn=Kategorio:Verbo|alf=HGFEHCBA}}"

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

* #T34 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" explicit with groups, do NOT convert X-system to unicode here)
* "{{#invoke:mind12dim|ek|fpn=Kategorio:Verbo|alf=ABC(CX)DEFG(GX)HIJKL(LL)MNO(NENIO)P}}"

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

* #T35 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" explicit with groups, reaching limit of 9)
* "{{#invoke:mind12dim|ek|fpn=Kategorio:Verbo|alf=ABC(CT)DEFG(GH)HIJKL(LL)MNO(NENIOMULO)P}}"

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

* #T40 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" explicit with groups, limit violation (9->10), #E13)
* "{{#invoke:mind12dim|ek|fpn=Kategorio:Verbo|alf=ABC(CT)DEFG(GH)HIJKL(LL)MNO(NENIOMULOJ)P}}"

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

* #T41 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" explicit with groups, limit violation (2->1), #E13)
* "{{#invoke:mind12dim|ek|fpn=Kategorio:Verbo|alf=ABC(CT)DEFG(GH)HIJKL(LL)MNO(N)P}}"

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

* #T42 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" explicit with groups, limit violation (2->1), replace "UX" with unicode, #E13)
* "{{#invoke:mind12dim|ek|fpn=Kategorio:Verbo|alf=ABC(CT)DEFG(GH)HIJKL(LL)MNO(UX)P}}"

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

* #T43 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" explicit with groups, limit violation (2->1), replace "UXX" with some Amharic letter in unicode, #E13)
* "{{#invoke:mind12dim|ek|fpn=Kategorio:Verbo|alf=ABC(CT)DEFG(GH)HIJKL(LL)MNO(UXX)P}}"

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

* #T44 (no "dud=1", "fpn=" explicit cat "Kategorio:", "typ=" guessed, "alf=" explicit with groups, using non-ASCII char:s, replace "CX" and "UXX" with unicode)
* "{{#invoke:mind12dim|ek|fpn=Kategorio:Verbo|alf=ABC(CT)DEFG(ACX)HIJKL(LL)MNO(LUXX)P}}"

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

]===]

local ind12dim = {}

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

---- CONSTANTS ----

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

  -- HTML stuff for our insane table (unless "tit=-" is used)

  local constrborder = '2060A0'  -- 6 digits no cross "#" here
  local constrbakgnd = 'D0E0F0'  -- 6 digits no cross "#" here
  local constrtabu7  = '<table class="plainlinks" style="margin:0.5em auto 0.5em auto; border:2px solid #'
  local constrtabu8  = '; background-color:#'
  local constrtabu9  = '; padding:5px; text-align:center;">'

  -- very verbose error messages

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

  local constrdamn  = 'Error in "Module:mind12dim" code #E'

  local contabevilpar2a = {} -- 1...26 AKA #E01...#E26 (use "-" for reserved codes)
  contabevilpar2a = {'A', 'B', 'CaD', 'CbD', 'CbDH', 'CcD', '-', 'CdD', 'CdDE', 'CeD', 'CfD', '-', 'CgD', 'CgDF', 'CgDG', 'ChD', 'CiD', 'CjD', 'CjDI', '-', 'CkD', 'CkDH', '-', '-', 'K', 'L'}

  local contabevilpar3a = {} -- 1...11 AKA "a"..."k" -- lowercase
  contabevilpar3a = {'fpn','tit','dud','typ','mwp','ans','alf','fla','ctb','cos','pre'}

  local contabevilpar4a = {} -- 1...12 AKA "A"..."L" -- UPPERCASE (use "-" for reserved codes)
  contabevilpar4a[ 1] = 'Internal error'                            -- "A"
  contabevilpar4a[ 2] = 'Undesirable anonymous parameter'           -- "B"
  contabevilpar4a[ 3] = 'Parameter "'                               -- "C"
  contabevilpar4a[ 4] = '=" is bad'                                 -- "D"
  contabevilpar4a[ 5] = ' (bad base link type, must be 0...6)'      -- "E"
  contabevilpar4a[ 6] = ' (lowercase char)'                         -- "F"
  contabevilpar4a[ 7] = ' (dupe char)'                              -- "G"
  contabevilpar4a[ 8] = ' (illegal dbl apo:s links transclusions)'  -- "H"
  contabevilpar4a[ 9] = ' (both colours are non-ZERO and same)'     -- "I"
  contabevilpar4a[10] = '-'                                         -- "J"
  contabevilpar4a[11] = 'Inconsistent explicit parameters'          -- "K"
  contabevilpar4a[12] = 'Inconsistency with pagename'               -- "L"

  -- text colours (none,1-dim,2-dim) and separators for "strapart" (none,1-dim) controlled by "cos="

  local contabwarn = {'F00000','00F000','0000F0','F0F000','00F0F0','F000F0','808080'} -- 1...7 RGB CMY grey (ZERO valid and default but separate)
  local contabsepa = {'-','--','*','|',string.char(0xC2,0xB7),string.char(0xE2,0x80,0xA2)} -- 1...6 (ZERO valid and default but separate)

  -- constant strings EO vs ID

  local constrkatp5 = "Category:"             -- EO (obligatory)
  local constrkatp6 = "Kategorio:"            -- EO (assign to empty string if unused)

  -- local constrkatp5 = "Category:"             -- ID (obligatory)
  -- local constrkatp6 = "Kategori:"             -- ID (assign to empty string if unused)

  local constrspep5 = "Special:"                                    -- EO (obligatory)
  local constrspep6 = "Speciala" .. string.char(0xC4,0xB5) .. "o:"  -- EO (assign to empty string if unused) ($C4,$B5 = "jx")

  -- local constrspep5 = "Special:"              -- ID (obligatory)
  -- local constrspep6 = "Istimewa:"             -- ID (assign to empty string if unused)

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

---- ORDINARY LOCAL LOW LEVEL FUNCTIONS ----

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

-- Local function LFCOUNTCHR

-- Count occurrences of a given ASCII char in a string

-- Input  : - strqq -- string (empty is useless but can't cause any harm)
--          - numascii -- code of char to be counted

-- Output : - numrezalt -- amount

local function lfcountchr (strqq, numascii)
  local numrezalt = 0
  local numciar = 0
  local numukuran = 0
  local numindxe = 0 -- ZERO-based
  numukuran = string.len (strqq)
  while (true) do
    if (numindxe==numukuran) then
      break -- done -- ZERO iterations possible
    end--if
    numciar = string.byte (strqq,(numindxe+1),(numindxe+1))
    if (numciar==numascii) then
      numrezalt = numrezalt + 1
    end--if
    numindxe = numindxe + 1
  end--while
  return numrezalt
end--function lfcountchr

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

-- Local function LFDEC1DIGLM

-- Convert 1 decimal ASCII digit to UINT8 with inclusive upper limit

-- Use this for single-digit conversions with range and for pseudo-bool
-- (0,1) and for genuine bool (false,true) via "boocrap=(numcrap==1)"

local function lfdec1diglm (num1dygyt, num1lim)
  num1dygyt = num1dygyt - 48 -- may become invalid ie negative
  if ((num1dygyt<0) or (num1dygyt>num1lim)) then
    num1dygyt = 255
  end--if
  return num1dygyt
end--function lfdec1diglm

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

-- Local function LFDECINP

-- Convert string (1...13 octet:s) with decimal ASCII digits to a positive
-- (UINT32) integer number (0...4'000'000'000) ignoring possible inside apo:s

-- 65'536 good
-- 655'36 ugly but accepted -- 6''''55''''36 ugly but accepted
-- '65536 rejected -- 65536' rejected

-- Input  : - strin

-- Output : - numaout (4'294'967'295 on error)

local function lfdecinp (strin)
  local numaout = 0
  local numleen = 0
  local numinxx = 1 -- ONE-based -- counts up
  local numokkt = 0
  numleen = string.len (strin)
  if ((numleen<1) or (numleen>13)) then
    numaout = 4294967295 -- damn
  else
    while (true) do
      numokkt = string.byte (strin,numinxx,numinxx)
      if (numokkt==39) then
        if ((numinxx==1) or (numinxx==numleen)) then
          numaout = 4294967295
          break -- damn (may not begin or end with apo)
        end--if
      else
        if ((numokkt<48) or (numokkt>57) or (numaout>400000000)) then
          numaout = 4294967295
          break -- damn (bad char or out of range)
        end--if
        numaout = numaout * 10 + (numokkt - 48)
      end--if
      if (numinxx==numleen) then
        break -- done (hopefully success, but not yet sure)
      end--if
      numinxx = numinxx + 1
    end--while
    if (numaout>4000000000) then
      numaout = 4294967295 -- damn (out of range)
    end--if
  end--if
  return numaout
end--function lfdecinp

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

-- Local function LFEXTHEXTOINT

-- Convert 1 ASCII code of an extended quasi-hex digit (0...9 A...Z)
-- to a number 0...35 (255 invalid)

-- Only uppercase accepted

local function lfexthextoint (numdgiit)
  local numressult = 255
  if ((numdgiit>47) and (numdgiit<58)) then
    numressult = numdgiit-48
  end--if
  if ((numdgiit>64) and (numdgiit<91)) then
    numressult = numdgiit-55
  end--if
  return numressult
end--function lfexthextoint

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

-- Local function LFUTF8LENGTH

-- Measure length of a 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 LFCASELETO

-- Adjust case of a single letter, limited unicode support

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

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

-- Depends on sub "lfutf8length"

local function lfcaseleto (strucinut, booucas)
  local numlaengden = 0 -- length from "string.len"
  local numchaer = 0
  local numchaes = 0
  local booc3blok = false
  local booc4c5bl = false
  local booisuppr = false
  local booislowr = false
  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
    if (numlaengden==1) then
      booisuppr = ((numchaer>64) and (numchaer< 91))
      booislowr = ((numchaer>96) and (numchaer<123))
      if (booisuppr and (booucas==false)) then
        numchaer = numchaer+32 -- ASCII UPPER->lower
      end--if
      if (booislowr and booucas) then
        numchaer = numchaer-32 -- ASCII lower->UPPER
      end--if
      strucinut = string.char (numchaer)
      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
    if (booc3blok) then
      if ((numchaes>=128) and (numchaes<160)) then
        booisuppr = true -- C3,80...C3,9F
      end--if
      if ((numchaes>=160) and (numchaes<192)) then
        booislowr = true -- C3,A0...C3,BF
      end--if
    end--if
    if (booc4c5bl) then
      if ((numchaes%2)==0) then -- MOD is available but DIV is NOT available
        booisuppr = true
      else
        booislowr = true
      end--if
    end--if
    if (booc3blok and booisuppr and (booucas==false)) then
      numchaes = numchaes+32 -- UC UPPER->lower -- holds f AA AE EE NN OE UE
    end--if
    if (booc3blok and booislowr and booucas) then
      numchaes = numchaes-32 -- UC lower->UPPER -- holds f aa ae ee nn oe ue
    end--if
    if (booc4c5bl and booisuppr and (booucas==false)) then
      numchaes = numchaes+1 -- UC UPPER->lower -- holds for JX SX ...
    end--if
    if (booc4c5bl and booislowr and booucas) then
      numchaes = numchaes-1 -- UC lower->UPPER -- holds for jx sx ...
    end--if
    strucinut = string.char (numchaer) .. string.char (numchaes)
    break -- finally to join mark
  end--while -- fake loop -- join mark
  return strucinut -- same var for input and output !!!
end--function lfcaseleto

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

-- Local function LFXCASEULT

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

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

-- Depends on subs "lfcaseleto" and "lfutf8length"

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
  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 = lfcaseleto(strte7mp,booupcas)
    end--if
    strelygo = strelygo .. strte7mp
    numposi = numposi + numcut
  end--while
  return strelygo
end--function lfxcaseult

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

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

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

-- Local function lfranges

-- Check whether ASCII char is maybe illegal (2) or maybe lowercase (1)

local function lfranges (numascii)
  local numtrista = 2 -- preassume guilt
  if ((numascii>=48) and (numascii<=57)) then
    numtrista = 0 -- innocent -- "0"..."9"
  end--if
  if ((numascii>=65) and (numascii<=90)) then
    numtrista = 0 -- innocent -- "A"..."Z"
  end--if
  if ((numascii==39) or (numascii==45)) then
    numtrista = 0 -- innocent -- apo "'" or dash "-"
  end--if
  if ((numascii>=97) and (numascii<=122)) then
    numtrista = 1 -- lowercase -- "a"..."z"
  end--if
  return numtrista
end--function lfranges

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

-- Local function LFILLEGAL

-- Check whether string contains illegal
-- stuff such as <> '''' [[]] {{}} "[http"

-- Input  : - "strinpil" (empty cannot cause major harm and returns "false")
--          - boonotags (disallow <> ie HTML tags)
--          - boonobold (disallow "''" ie both italics and bold)
--          - boonowili (disallow "[[" and "]]" but not single ones)
--          - boonotskl (disallow "{{" and "}}" but not single ones)
--          - boonohttp (disallow "[http")

-- Output : - boocriminal ("true" if "strinpil" contains illegal stuff)

local function lfillegal (strinpil, boonotags, boonobold, boonowili, boonotskl, boonohttp)
  local numleunin = 0
  local numindexx = 1 -- ONE-based
  local numcrap = 0
  local numcraq = 0 -- previous
  local boocriminal = false
  if (boonohttp and (string.find(strinpil,"[http",1,true))) then
    boocriminal = true
  else
    numleunin = string.len (strinpil) -- length of input string to be tested
    while (true) do
      if (numindexx>numleunin) then
        break -- innocent now
      end--if
      numcrap = string.byte (strinpil,numindexx,numindexx)
      if (boonotags and ((numcrap==60) or (numcrap==62))) then
        boocriminal = true -- single "<" or ">" is criminal
        break
      end--if
      if (numcrap==numcraq) then
        if (boonobold and (numcrap==39)) then
          boocriminal = true -- "''" ie italics or bold
          break
        end--if
        if (boonowiki and ((numcrap==91) or (numcrap==93))) then
          boocriminal = true
          break
        end--if
        if (boonotskl and ((numcrap==123) or (numcrap==125))) then
          boocriminal = true
          break
        end--if
      end--if
      numcraq = numcrap -- previous
      numindexx = numindexx + 1
    end--while
  end--if (boonohttp and (string.find(strinpil,"[http",1,true))) else
  return boocriminal
end--function lfillegal

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

-- Local function LFDBLCMP

-- Check whether string begins with one of 2 supplied reference strings

-- Input  : - strinp
--          - strx4 (may be empty, partial result is false then)
--          - strx5 (may be empty, partial result is false then)

-- Output : - booequal ("true" if "strinp" begins with "strx4" or "strx5")

local function lfdblcmp (strinp, strx4, strx5)
  local numlenin = 0
  local numleeen = 0
  local booequal = false
  numlenin = string.len (strinp) -- length of input string to be tested
  numleeen = string.len (strx4)
  if ((numleeen~=0) and (numleeen<=numlenin)) then
    if (string.sub(strinp,1,numleeen)==strx4) then
      booequal = true -- hit against "strx4"
    end--if
  end--if
  numleeen = string.len (strx5)
  if ((numleeen~=0) and (numleeen<=numlenin)) then
    if (string.sub(strinp,1,numleeen)==strx5) then
      booequal = true -- hit against "strx5"
    end--if
  end--if
  return booequal
end--function lfdblcmp

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

-- Local function LFWARNA

-- Convert index 1...21 (1...7 8...14 15...21) to 6 HEX digits

-- Depends on const table "contabwarn"

local function lfwarna (numwarnaidx)
  local strhexhex = '000000'
  local strhexaut = ''
  local numin5ex = 1
  local numch5ar = 0
  local boolajt = false
  local boodark = false
  if ((numwarnaidx>=1) and (numwarnaidx<=21)) then
    boolajt = ((numwarnaidx>= 8) and (numwarnaidx<=14))
    boodark = ((numwarnaidx>=15) and (numwarnaidx<=21))
    if (boolajt) then
      numwarnaidx = numwarnaidx - 7
    end--if
    if (boodark) then
      numwarnaidx = numwarnaidx - 14
    end--if
  else
    numwarnaidx = 7 -- grey
  end--if
  strhexhex = contabwarn [numwarnaidx] -- 1...7
  while (true) do
    if (numin5ex==7) then -- ONE-based
      break -- done
    end--if
    numch5ar = string.byte (strhexhex,numin5ex,numin5ex)
    if ((numin5ex==1) or (numin5ex==3) or (numin5ex==5)) then
      if (numch5ar>57) then
        numch5ar = numch5ar - 7 -- now 48...63
      end--if
      if (boolajt) then
        numch5ar = numch5ar + 6
      end--if
      if (boodark) then
        numch5ar = numch5ar - 6
      end--if
      if (numch5ar<48) then
        numch5ar = 48 -- do NOT sink below ZERO
      end--if
      if (numch5ar>63) then
        numch5ar = 63 -- do NOT grow above F AKA 15
      end--if
      if (numch5ar>57) then
        numch5ar = numch5ar + 7 -- now 48...57 or 65...70
      end--if
    end--if
    strhexaut = strhexaut .. string.char (numch5ar)
    numin5ex = numin5ex + 1
  end--while
  return strhexaut
end--function lfwarna

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

-- Local function LFPER6MUT

-- Permute RGB in a colour string

-- Input  : - strhex6in (6 hex digits, no cross "#")
--          - numindepe (0...5)

-- Output : - strhex6out

local function lfper6mut (strhex6in, numindepe)
  local strhexmm   = ""
  local strhexnn   = ""
  local strhexoo   = ""
  local strhex6out = ""
  if (string.len(strhex6in)==6) then
    strhexmm = string.sub (strhex6in,1,2)
    strhexnn = string.sub (strhex6in,3,4)
    strhexoo = string.sub (strhex6in,5,6)
    if (numindepe==1) then
      strhex6out = strhexmm .. strhexoo .. strhexnn
    end--if
    if (numindepe==2) then
      strhex6out = strhexnn .. strhexmm .. strhexoo
    end--if
    if (numindepe==3) then
      strhex6out = strhexnn .. strhexoo .. strhexmm
    end--if
    if (numindepe==4) then
      strhex6out = strhexoo .. strhexmm .. strhexnn
    end--if
    if (numindepe==5) then
      strhex6out = strhexoo .. strhexnn .. strhexmm
    end--if
  end--if
  if (strhex6out=="") then
    strhex6out = strhex6in -- unchanged if (numindepe==0) or invalid value
  end--if
  return strhex6out
end--function lfper6mut

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

-- Local function LFLUKSALIGILO

-- Input  : - one string and 3 numbers (1 of them tristate)

-- Enhance string (visible part of the link)

local function lfluksaligilo (strvysy, numenklo, numwarnanya, numwarnabkg)
  local strstilo = ''
  strvysy = "<b>" .. strvysy .. "</b>" -- text is BOLD, enclosement is NOT
  if (numenklo==1) then
    strvysy = "<small>&#91;</small>" .. strvysy .. "<small>&#93;</small>" -- "[" and "]"
  end--if
  if (numenklo==2) then
    strvysy = "<small>(</small>" .. strvysy .. "<small>)</small>"
  end--if
  if (numwarnanya~=0) then
    strstilo = 'color:#' .. lfwarna(numwarnanya) .. ';'
  end--if
  if (numwarnabkg~=0) then
    strstilo = strstilo .. 'background-color:#' .. lfwarna(numwarnabkg) .. ';'
  end--if
  if (strstilo~="") then
    strvysy = '<span style="' .. strstilo .. '">' .. strvysy .. '</span>'
  end--if
  return strvysy
end--function lfluksaligilo

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

-- Local function LFBREWLINK

-- Brew a single link (maybe quasi-external http link, but actually
-- always internal not leaving the wiki)

-- Input  : - numtyplnk -- base type of link 0...6
--          - numtuncas -- case tuning 0...4 (1: lower | 2: upper | 3 | 4)
--                         (for ba types 1 2 5 6 only -- NOT for "^" and "~")
--          - numtungap -- gap tuning 0 or 1 (1: add space) (for ba types
--                         2 and 6 only -- NOT for "^" and "~")
--          - strfull -- fullpagename (category name must be already prefixed
--                       by a dot like ":Category:Piracy", here we add wall,
--                       visible string and brackets, ignored for type 4)
--          - strpostf -- postfix string (one letter, two letters, maybe
--                        other type of char or short string, special values
--                        "^" for "all" and "~" for "remainder" are permitted
--                        for types 0 and 1 only, may NOT be empty, may not be
--                        "''" (double apo), can be both "y" and "x" parts
--                        concatenated in this order for 2-dim table
--                        (caller has the responsibility)
--          - strvisi -- visible link string (may NOT be empty)
--          - numencl -- enclosement of the visible link string (0 none |
--                       1 "[" and "]" as "&#91;" and "&#93;" | 2 "(" and ")")
--                       forwarded to "lfxcaseult"
--          - numw8, numw9
--          - boomowop -- true if <<#mw-pages>> desired (only type 0
--                        and only category, caller has the responsibility)
--          - numruangnama -- namespace (required, use ZERO if no better
--                            value available, ZERO is the default of the
--                            wiki software, ZERO will NOT be passed to
--                            the wiki, caller has the responsibility)
--          - strpos2pa -- for base link types 5 and 6 the "y" row part

-- Output : - strlaenken (always begins with "[" and ends with "]")

-- BEWARE: It is the caller's responsibility to make sure that the 12
-- parameters are valid and consistent.

-- The "strfull" fullpagename may contain plain spaces, no encoding
-- to underscore "_" or "%20" is needed since for base type 0 it goes
-- through "mw.uri.canonicalUrl". Gap tuning is possible only for base
-- link types 2 and 6 where we use wiki links thus spaces cannot cause
-- any major harm either.

-- Depends on sub "lfxcaseult" (in turn depends
-- on "lfcaseleto" and "lfutf8length")

-- "numtyplnk" values: 0 "from=" | 1 wiki link + "/" | 2 wiki link + raw pofi
--                     3 wiki link + "#" | 4 "#" section link
--                     5 combo 2-part link "/"+"#" 1+3
--                     6 combo 2-part link raw+"#" 2+3

-- With 2-part links (types 5 and 6) we use "strpos2pa" (only "y") and
-- "strpostf" ("y" and "x") in this order, otherwise we use only "strpostf"
-- and ignore "strpos2pa".

-- Incoming "strpos2pa" and "strpostf" are NOT used anymore after "stradrq"
-- and "strkrys" have been assigned from them.  Only "stradrq" can be victim
-- of tuning. "stradrq" is needed even for bottom link "~" but NOT for
-- top link.

local function lfbrewlink (numtyplnk, numtuncas, numtungap, strfull, strpostf, strvisi, numencl, numw8, numw9, boomowop, numruangnama, strpos2pa)
  local strlaenken = ""
  local stradrq = "" -- part of address or http query
  local strkrys = "" -- part after the cross "#"
  local strquery = "" -- can have ZERO or 1 or 2 items
  local booalltop = false -- all AKA top AKA "^"
  local boorembot = false -- bottom AKA remainder AKA "~"
  if (numtyplnk<=1) then
    booalltop = (strpostf=="^") -- do not use "strpostf" "strpos2pa" "strkrys"
    boorembot = (strpostf=="~") -- do not use "strpostf" "strpos2pa" "strkrys"
  end--if
  strvisi = lfluksaligilo (strvisi, numencl, numw8, numw9)
  if ((booalltop==false) and (boorembot==false)) then
    if (numtyplnk<=2) then
      stradrq = strpostf -- "krys" NOT needed for 0 1 2
    end--if
    if (numtyplnk>=3) then
      strkrys = strpostf -- "adre" maybe needed (for 5 and 6 only)
    end--if
    if (numtyplnk>=5) then
      stradrq = strpos2pa -- "krys" needed and comes from "strpostf"
    end--if
    if (numtuncas==1) then
      stradrq = lfxcaseult (stradrq, false, false) -- force lowercase beg
    end--if
    if (numtuncas==2) then
      stradrq = lfxcaseult (stradrq, true, false) -- force uppercase beg
    end--if
    if (numtuncas==3) then
      stradrq = lfxcaseult (stradrq, false, true) -- force lowercase all
    end--if
    if (numtuncas==4) then
      stradrq = lfxcaseult (stradrq, true, true) -- force uppercase all
    end--if
    if (numtungap==1) then
      stradrq = "_" .. stradrq -- add space encoded as underscore "_" (!!!)
    end--if
  end--if
  if (boorembot) then
    stradrq = "~" -- CRUCIAL & needed several times below ("strkrys" NOT used)
  end--if
  if ((numtyplnk==0) and (booalltop==false)) then
    strquery = "from=" .. stradrq -- even for bottom AKA remainder but NOT top
  end--if
  if (numruangnama~=0) then
    if (strquery~="") then
      strquery = strquery .. "&" -- need "and" separator (NOT wall separator)
    end--if
    strquery = strquery .. "namespace=" .. tostring (numruangnama)
  end--if
  if (numtyplnk==0) then
    if (strquery=="") then
      strlaenken = tostring ( mw.uri.canonicalUrl ( strfull ) )
    else
      strlaenken = tostring ( mw.uri.canonicalUrl ( strfull , strquery ) )
    end--if
  end--if
  if ((numtyplnk~=0) and (numtyplnk~=4)) then
    strlaenken = strfull -- via "canonicalUrl" for 0 and not needed for 4
  end--if
  if (((numtyplnk==1) or (numtyplnk==5)) and (booalltop==false)) then
    strlaenken = strlaenken .. "/" -- link type 1 or 5 (but NOT for "^")
  end--if
  if ((numtyplnk~=0) and (booalltop==false)) then
    strlaenken = strlaenken .. stradrq -- type 1...6 -- tuning already done
  end--if
  if ((numtyplnk>=3) and (booalltop==false)) then
    strlaenken = strlaenken .. "#" .. strkrys -- link type 3 or 4 or 5 or 6
  end--if
  if (boomowop) then
    strlaenken = strlaenken .. "#mw-pages" -- only legal for type 0 some cases
  end--if
  if (numtyplnk==0) then
    strlaenken = strlaenken .. " " -- separation space (NOT wall "|" in http)
  else
    strlaenken = strlaenken .. "|" -- wiki link
  end--if
  strlaenken = "[" .. strlaenken .. strvisi .. "]"
  if (numtyplnk~=0) then
    strlaenken = "[" .. strlaenken .. "]"
  end--if
  return strlaenken
end--function lfbrewlink

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

---- MAIN EXPORTED FUNCTION ----

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

function ind12dim.ek (arxframent)

  -- various types 11 parameters (fpn,tit,dud,typ,mwp,ans,alf,fla,ctb,cos,pre)

  local strfpn    = ""     -- fullpagename AKA title
  local boohavfpn = false  -- true even if "-" was supplied
  local boofpns   = false  -- no fullpagename and link type 3 due to "fpn=-"

  local strtit    = ""     -- title of the table
  local bootits   = false  -- suppress table due to "tit=-"

  local boodud    = false  -- berdimensi dua

  local strtyp    = ""     -- base type & 2 tuning digits (guess "000" or ...)
  local boohavtyp = false  -- true even if "000" was supplied
  local numtypb   = 0      -- (0...6) ba type of link -- 0 "from=" 1 "/" 2 raw
  local numtycas  = 0      -- case tuning 0 or 1 2 3 4 (1 lower | 2 upper)
  local numtygap  = 0      -- gap tuning 0 or 1

  local boomwp    = false  -- add <<#mw-pages>>

  local numans    = 0      -- namespace for "Special:AllPages"
  local boohavans = false  -- true even if ZERO was supplied

  local stralf    = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" -- alph (later to "tababjad")

  local strfla    = ""
  local boohavfla = false   -- true even if "-" was supplied
  local strflb4   = ""      -- "top" link string ("all" "top" "^")
  local strflb5   = ""      -- "top" visible description
  local strflb6   = ""      -- "bottom" link string ("remainder" "bottom" "~")
  local strflb7   = ""      -- "bottom" visible description
  local booflas   = false   -- suppress 2 extra links due to "fla=-"

  local numctb    = 0       -- (0...5) permutation of colour table background
  local boohavctb = false   -- true even if ZERO was supplied

  local strcos    = ""      -- 6 d tuning col & sepa values (default "000000")
  local boohavcos = false   -- true even if "000000" was supplied
  local numscw8   = 0
  local numscw9   = 0
  local numenkli  = 0       -- forwarded to some calls, can be 0 or 1 only, []
  local numspix   = 0       -- (0...6) 1...6 indx "contabsepa" or ZERO -> NBSP
  local boosepenh = false   -- enhance separator with 2 NBSP:s if "true"

  local strpre    = ""      -- default is empty "", empty value is even valid
  local boohavpre = false   -- true if param used (explicit empty impossible)

  -- general unknown type

  local vartmp = 0     -- variable without type

  -- special type "args" AKA "arx"

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

  -- general "tab"

  local tababjad = {}  -- fill in useful default content later if needed

  -- general "str"

  local strret     = ""  -- output string
  local str1to4err = ""  -- for error generation 1...4 char:s
  local strapart   = ""  -- separator for 1-dim or no table from "contabsepa"
  local strtmp     = ""
  local strtpm     = ""
  local stryonl    = ""  -- for 2-dimensional -- only "y"
  local stryyxx    = ""  -- for 2-dimensional -- "y" and "x" concatenated
  local strrow     = ""  -- for 2-dimensional

  -- general "num"

  local numerrk   = 0  -- error code (0...20 -- 0 OK 1 inter 2 anon 3 ...)
  local numabjad  = 0  -- hard minimum 3 -- maximum ca 220

  local numlong   = 0  -- temp length of parameter
  local numoct    = 0  -- temp some char
  local numsrcind = 0  -- temp for "alf=" conversion
  local numunilen = 0  -- temp for "alf=" conversion
  local numranger = 0  -- temp for "alf=" conversion
  local numtmpx   = 0  -- temp
  local numtmpy   = 0  -- temp
  local numtmpz   = 0  -- temp

  local numclspl  = 0  -- colspan left
  local numclspr  = 0  -- colspan right

  -- general "boo" (no main fatal error flag here, see "numerrk")

  local booiskat  = false  -- true for categories
  local booisspe  = false  -- true for special pages
  local boocrap   = false  -- temp (this is a local flag only)
  local boomult   = false  -- temp

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

  -- constrkatp5,constrkatp6,constrspep5,constrspep6 must be assigned
  -- constrkatp5,constrspep5 must be non-empty

  if ((type(constrkatp5)~="string") or (type(constrspep5)~="string")) then
    numerrk = 1 -- #E01 -- internal
  else
    if ((constrkatp5=="") or (constrspep5=="")) then
      numerrk = 1 -- #E01 -- internal
    end--if
  end--if
  if ((type(constrkatp6)~="string") or (type(constrspep6)~="string")) then
    numerrk = 1 -- #E01 -- internal
  end--if

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

  if (numerrk==0) then
    arxsomons = arxframent.args -- "args" from our own "frame"
    if (arxsomons[1]) then
      numerrk = 2 -- #E02 -- anonymous params NOT appreciated
    else
      vartmp = arxsomons["caller"]
      if (vartmp=="true") then
        arxsomons = arxframent:getParent().args -- "args" frm caller's "frame"
        if (arxsomons[1]) then
          numerrk = 2 -- #E02 -- anonymous params NOT appreciated
        end--if
      end--if
    end--if
  end--if

  ---- SEIZE 11 OPTIONAL NAMED PARAMETERS ----

  while (true) do -- fake loop

    if (numerrk~=0) then
      break -- to join mark -- we already suck :-(
    end--if

    vartmp = arxsomons["fpn"] -- 5...100 octet:s zero or one colon ":" code 58
    if (type(vartmp)=="string") then
      numlong = string.len (vartmp)
      if (numlong~=0) then -- ignore empty string
        if (vartmp=="-") then
          boofpns = true -- suppress the pagename
          boohavfpn = true -- "strfpn" was preassigned to empty "" far above
        else
          if ((numlong<5) or (numlong>100)) then
            numerrk = 3 -- bad "fpn=" #E03 (length)
            break -- to join mark
          end--if
          strfpn = vartmp
          boohavfpn = true -- "strfpn" was preassigned to empty "" far above
          if (lfillegal(strfpn,true,true,true,true,true)) then
            numerrk = 3 -- bad "fpn=" #E03 (illegal char:s)
            break -- to join mark
          end--if
          numtmpx = lfcountchr(strfpn,58)
          if (numtmpx>1) then
            numerrk = 3 -- bad "fpn=" #E03 (amount of colon:s)
            break -- to join mark
          end--if
          numtmpx = string.byte (strfpn,1,1)
          numtmpy = string.byte (strfpn,numlong,numlong)
          if ((numtmpx==58) or (numtmpy==58)) then
            numerrk = 3 -- bad "fpn=" #E03 (colon at begin or end)
            break -- to join mark
          end--if
        end--if
      end--if (numlong~=0) then
    end--if

    vartmp = arxsomons["tit"] -- 5...120 octet:s but no [[]] {{}} or similar
    if (type(vartmp)=="string") then
      numlong = string.len (vartmp)
      if (numlong~=0) then -- ignore empty string
        if (vartmp=="-") then
          bootits = true -- suppress the table
        else
          if ((numlong<5) or (numlong>120)) then
            numerrk = 4 -- #E04 bad "tit" generic
            break -- to join mark
          end--if
          strtit = vartmp -- there is no "boohavtit"
          if (lfillegal(strtit,true,true,true,true,true)) then
            numerrk = 5 -- #E05 bad "tit" illegal content apo/brackets
            break -- to join mark
          end--if
        end--if (vartmp=="-") else
      end--if (numlong~=0) then
    end--if

    vartmp = arxsomons["dud"] -- bool must be "0" or "1"
    if (type(vartmp)=="string") then
      numlong = string.len (vartmp)
      if (numlong~=0) then -- ignore empty string
        if (numlong~=1) then
          numerrk = 6 -- bad "dud=" #E06
          break -- to join mark
        end--if
        numoct = lfdec1diglm(string.byte(vartmp,1,1),1) -- 255 if invalid
        if (numoct==255) then
          numerrk = 6 -- bad "dud=" #E06
          break -- to join mark
        end--if
        boodud = (numoct==1) -- was preassigned to "false" ie 1-dim far above
      end--if (numlong~=0) then
    end--if

    vartmp = arxsomons["typ"] -- need 3 digit:s base link type + 2 tuning digi
    if (type(vartmp)=="string") then
      numlong = string.len (vartmp)
      if (numlong~=0) then -- ignore empty string
        if (numlong~=3) then
          numerrk = 8 -- bad "typ=" #E08
          break -- to join mark
        end--if
        strtyp = vartmp -- further validation below
        boohavtyp = true -- "strtyp" was preassigned to empty "" far above
      end--if (numlong~=0) then
    end--if

    vartmp = arxsomons["mwp"] -- bool must be "0" or "1"
    if (type(vartmp)=="string") then
      numlong = string.len (vartmp)
      if (numlong~=0) then -- ignore empty string
        if (numlong~=1) then
          numerrk = 10 -- bad "mwp=" #E10
          break -- to join mark
        end--if
        numoct = lfdec1diglm(string.byte(vartmp,1,1),1) -- 255 if invalid
        if (numoct==255) then
          numerrk = 10 -- bad "mwp=" #E10
          break -- to join mark
        end--if
        boomwp = (numoct==1) -- was preassigned to "false" far above
      end--if (numlong~=0) then
    end--if

    vartmp = arxsomons["ans"] -- 1...5 octet:s DEC number 0...5'000
    if (type(vartmp)=="string") then
      numlong = string.len (vartmp)
      if (numlong~=0) then -- ignore empty string
        if ((numlong<1) or (numlong>5)) then
          numerrk = 11 -- bad "ans" #E11
          break -- to join mark
        end--if
        numans = lfdecinp (vartmp) -- only 4'294'967'295 on error
        if (numans>5000) then
          numerrk = 11 -- bad "ans" #E11
          break -- to join mark
        end--if
        boohavans = true
      end--if (numlong~=0) then
    end--if

    vartmp = arxsomons["alf"] -- 5...400 octet:s with further restrictions
    if (type(vartmp)=="string") then
      numlong = string.len (vartmp)
      if (numlong~=0) then -- ignore empty string
        if ((numlong<5) or (numlong>400)) then
          numerrk = 13 -- bad "alf" (lower,dupe have separate er c #E14,#E15)
          break -- to join mark #E13
        end--if
        stralf = vartmp -- later converted to "tababjad" with further checks
      end--if (numlong~=0) then
    end--if

    vartmp = arxsomons["fla"] -- 5...80 octet:s (exactly 1 or 3 comma "," 44)
    if (type(vartmp)=="string") then
      numlong = string.len (vartmp)
      if (numlong~=0) then -- ignore empty string
        if ((vartmp~="-") and ((numlong<5) or (numlong>80))) then
          numerrk = 16 -- bad "fla" #E16
          break -- to join mark
        end--if
        strfla = vartmp -- "-" or 5...80 octet:s, further validation below
        boohavfla = true -- "strfla" was preassigned to empty "" far above
      end--if (numlong~=0) then
    end--if

    vartmp = arxsomons["ctb"] -- need 1 digit "0"..."5" -- colour permutation
    if (type(vartmp)=="string") then
      numlong = string.len (vartmp)
      if (numlong~=0) then -- ignore empty string
        if (numlong~=1) then
          numerrk = 17 -- bad "ctb=" #E17
          break -- to join mark
        end--if
        numctb = lfdec1diglm(string.byte(vartmp,1,1),5) -- 255 if invalid
        if (numctb==255) then
          numerrk = 17 -- bad "ctb=" #E17
          break -- to join mark
        end--if
        boohavctb = true -- "numctb" was preassigned to 0 far above
      end--if (numlong~=0) then
    end--if

    vartmp = arxsomons["cos"] -- need 6 ext hex digits (up to "L" 21)
    if (type(vartmp)=="string") then
      numlong = string.len (vartmp)
      if (numlong~=0) then -- ignore empty string
        if (numlong~=6) then
          numerrk = 18 -- bad "cos=" #E18 (we have also separate #E19 below)
          break -- to join mark
        end--if
        strcos = vartmp -- was preassigned to empty "" far above
        boohavcos = true -- further validation below
      end--if (numlong~=0) then
    end--if

    vartmp = arxsomons["pre"] -- need 1...9 octet:s
    if (type(vartmp)=="string") then
      numlong = string.len (vartmp)
      if (numlong~=0) then -- ignore empty string
        if ((numlong<1) or (numlong>9)) then
          numerrk = 21 -- bad "pre=" #E21 (length)
          break -- to join mark
        end--if
        strpre = vartmp -- was preassigned to empty "" far above
        if (lfillegal(strpre,true,true,true,true,true)) then
          numerrk = 22 -- bad "pre=" #E22 (illegal char:s) extra code
          break -- to join mark
        end--if
        boohavpre = true
      end--if (numlong~=0) then
    end--if

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

  ---- SEIZE FULLPAGENAME IF NEEDED ----

  -- there is "boohavfpn" and "boofpns"

  -- do NOT seize for "fpn=-"

  if ((numerrk==0) and (boohavfpn==false)) then
    vartmp = mw.title.getCurrentTitle().prefixedText -- "{{FULLPAGENAME}}"
    if (type(vartmp)=="string") then
      strfpn = vartmp
    end--if
    if (strfpn=="") then -- "strfpn" was preassigned to empty "" far above
      strfpn = "Special:AllPages" -- last resort
    end--if
  end--if

  ---- FIGURE OUT WHETHER THE PAGE IS A CATEGORY OR SPECIAL PAGE ----

  -- set 2 bool:s based on "strfpn" (can't be empty anymore)

  -- "booiskat" has several effects:
  -- # affects type guessing
  -- # used for inconsistency evaluation
  -- # prohibits lowercase
  -- # adds extra colon for link type 2 -> "[[:Category:Piracy]]"

  if (numerrk==0) then
    if (lfdblcmp(strfpn,constrkatp5,constrkatp6)) then
      booiskat  = true
    end--if
    if (lfdblcmp(strfpn,constrspep5,constrspep6)) then
      booisspe  = true
    end--if
  end--if

  ---- GUESS "TIT" IF WE DO NOT HAVE ANY YET ----

  -- "tit=" depends from "fpn="
  -- there is no "boohavtit", check if (strtit=="") instead

  if ((numerrk==0) and (strtit=="")) then
    if (boofpns) then
      strtit = "Index" -- this was rocket science
    else
      strtit = strfpn -- this was rocket science
    end--if
  end--if

  ---- GUESS "TYP=" IF WE DO NOT HAVE ANY YET ----

  -- "typ=" depends from "fpn=" (via "boofpns" and "booiskat" and "booisspe")
  -- based on "boohavtyp"

  -- 0 "from=" | 1 wiki link + "/" | 2 wiki link + raw pofi
  -- 3 wiki link + "#" | 4 "#" section link | 5 and 6 "combo" never guessed

  -- guessing type 4 for "fpn=-" (and NO other type is tolerable), otherwise
  -- type 1 if the page is special and "ans=" is ZERO, otherwise type 0
  -- if the page is category or special, otherwise type 3

  if ((numerrk==0) and (boohavtyp==false)) then
    while (true) do -- fake loop

      if (boofpns) then
        strtyp = "400" -- "fpn=-" -> base type 4 and no tuning
        break
      end--if
      if (booisspe and (numans==0)) then
        strtyp = "100" -- special and "ans=" is ZERO
        break
      end--if
      if (booiskat or booisspe) then
        strtyp = "000" -- base type 0 and no tuning
        break
      end--if
      strtyp = "300" -- last: maybe some appendix needing the crossy "#"-type

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

  ---- PROCESS "TYP=" ----

  -- from above "strtyp" has 3 octet:s (empty or invalid length is
  -- NOT possible) and possible guess done, but validation incomplete,
  -- and we have "boohavtyp" (needed below)

  -- # base link type 0...6
  -- # case tuning 0...4 (0 none | 1 beg low | 2 beg upp | 3 all
  --   low | 4 all upp) (types 1 2 5 6 only)
  -- # gap tuning 0 or 1 pseudo-bool (types 2 and 6 only)

  if (numerrk==0) then

    while (true) do -- fake loop
      numtypb = lfdec1diglm(string.byte(strtyp,1,1),6) -- 255 if invalid
      if (numtypb==255) then
        numerrk = 9 -- bad base type in "typ=" #E09 -- separate error code
        break -- to join mark
      end--if
      numtycas = lfdec1diglm(string.byte(strtyp,2,2),4) -- 255 if invalid
      if (numtycas==255) then
        numerrk = 8 -- bad case tuning value in "typ=" #E08
        break -- to join mark
      end--if
      numtygap = lfdec1diglm(string.byte(strtyp,3,3),1) -- 255 if invalid
      if (numtygap==255) then
        numerrk = 8 -- bad gap tuning value in "typ=" #E08
        break -- to join mark
      end--if
      if ((numtycas~=0) and (numtypb~=1) and (numtypb~=2) and (numtypb~=5) and (numtypb~=6)) then
        numerrk = 8 -- illegal use of case tuning in "typ=" #E08
        break -- to join mark
      end--if
      if ((numtygap~=0) and (numtypb~=2) and (numtypb~=6)) then
        numerrk = 8 -- illegal use of gap tuning in "typ=" #E08
        break -- to join mark
      end--if
      break -- finally to join mark
    end--while -- fake loop -- join mark

  end--if (numerrk==0) then

  ---- GUESS "FLA" IF WE DO NOT HAVE ANY YET ----

  -- "fla=" depends from "typ="

  -- we need "numtypb" to be already assigned (separate rules
  -- apply for types 0 and 1)

  -- based on "boohavfla"

  if ((numerrk==0) and (boohavfla==false)) then
    if (numtypb<=1) then
      strfla = "^,all,~,remainder" -- types 0 1 ("^" is placeholder for empty)
    else
      strfla = "-" -- types 2...6 (will assign "booflas" to "true" soon)
    end--if
  end--if

  ---- PROCESS "FLA=" ----

  -- from above "strfla" is "-" or has 5...80 octet:s (empty or invalid
  -- length is NOT possible) but validation incomplete, "boohavfla"
  -- exists but not needed anymore

  -- assign strflb4, strflb5, strflb6, strflb7 and booflas

  -- we need "numtypb" to be already assigned (separate rules
  -- apply for types 0 and 1)

  -- here we can throw bad "fla=" #E16

  if (numerrk==0) then

    while (true) do -- outer fake loop
      if (strfla=="-") then
        booflas = true -- suppress those 2 extra links
        break -- to join mark
      end--if
      numtmpx = lfcountchr(strfla,44)
      if ((numtmpx~=1) and (numtmpx~=3)) then
        numerrk = 16 -- bad "fla" #E16
        break -- to join mark
      end--if
      numlong = string.len (strfla)
      if (numtmpx==1) then -- 1 comma 2 words (visible text)
        vartmp = string.find (strfla, ',', 1, true) -- plain text search
        numtmpx = 0
        if (vartmp~=nil) then -- "not found" is NOT valid but CANNOT occur
          numtmpx = vartmp -- ONE-based position of the comma ","
        end--if
        if ((numtmpx>1) and (numtmpx<numlong)) then -- split at the comma
          strflb5 = string.sub (strfla,1,(numtmpx-1)) -- "top"
          strflb7 = string.sub (strfla,(numtmpx+1),numlong) -- "bottom"
          if (numtypb<=1) then
            strflb4 = "^" -- special placeholder for empty
            strflb6 = "~"
          else
            strflb4 = strflb5 -- copy from visible text to link text
            strflb6 = strflb7
          end--if
        else
          numerrk = 16 -- bad "fla" #E16
        end--if
      else
        numtmpy = 0 -- amount of substrings seized
        numtmpz = 1 -- position to start searching
        while (true) do -- inner genuine loop
          vartmp = string.find (strfla, ',', numtmpz, true) -- plain text sea
          numtmpx = 0
          if (vartmp~=nil) then -- "not found" is NOT valid but CANNOT occur
            numtmpx = vartmp -- ONE-based position of the comma ","
          end--if
          if ((numtmpx>numtmpz) and (numtmpx<numlong)) then -- split
            strtmp = string.sub (strfla,numtmpz,(numtmpx-1)) -- take part bef
            if (numtmpy==0) then
              strflb4 = strtmp
            end--if
            if (numtmpy==1) then
              strflb5 = strtmp
            end--if
            if (numtmpy==2) then
              strflb6 = strtmp
            end--if
            numtmpy = numtmpy + 1 -- INC
            numtmpz = numtmpx + 1 -- BUMP -- continue search after the comma
            if (numtmpy==3) then
              strflb7 = string.sub (strfla,numtmpz,numlong) -- pick the last
              break -- exit inner genuine loop
            end--if
          else
            numerrk = 16 -- bad "fla" #E16
            break -- exit inner genuine loop
          end--if
        end--while
      end--if (numtmpx==1) else
      break -- finally to join mark
    end--while -- fake loop -- join mark

  end--if (numerrk==0) then

  ---- ASSIGN "COS=" TO DEFAULT IF WE DO NOT HAVE ANY YET ----

  -- based on "boohavcos"

  if ((numerrk==0) and (boohavcos==false)) then
    strcos = "000000"
  end--if

  ---- PROCESS "COS=" ----

  -- from above "strcos" has always 6 octet:s (empty or invalid length
  -- is NOT possible) but validation is incomplete, and we have
  -- "boohavcos" (needed below)

  -- "numspix" was preassigned to ZERO far above
  -- "boosepenh" was preassigned to "false" far above

  -- always assign (colours):
  -- numscw8, numscw9 -- font, backgound (may NOT be non-ZERO and equal)

  -- always assign (enclosing):
  -- # numenkli -- non-default 0 suppresses "[","]" enclosing (value 2 for
  --   enclosing exists but cannot occur is this variable)

  -- for 1-dim or no table assign also (separator):
  -- # "numspix" must be "0"..."6" -- default is 0 ie NBSP
  -- # "boosepenh" default is "false"
  -- later we brew "strapart" from them

  if (numerrk==0) then

    while (true) do -- fake loop
      numscw8 = lfexthextoint(string.byte(strcos,1,1)) -- 255 if invalid
      if (numscw8>21) then
        numerrk = 18 -- bad "cos=" #E18 generic -- text colour
        break -- to join mark
      end--if
      numscw9 = lfexthextoint(string.byte(strcos,2,2)) -- 255 if invalid
      if (numscw9>21) then
        numerrk = 18 -- bad "cos=" #E18 generic -- text colour
        break -- to join mark
      end--if
      if ((numscw8~=0) and (numscw8==numscw9)) then
        numerrk = 19 -- bad "cos=" #E19 both colours equal
        break -- to join mark
      end--if
      numoct = lfdec1diglm(string.byte(strcos,3,3),1) -- bool -- 255 if invali
      if (numoct==255) then
        numerrk = 18 -- bad "cos=" #E18 generic -- enclosing
        break -- to join mark
      end--if
      numenkli = 1 - numoct -- invert it
      break -- finally to join mark
    end--while -- fake loop -- join mark

  end--if (numerrk==0) then

  if ((numerrk==0) and (boodud==false)) then

    while (true) do -- fake loop
      numspix = lfdec1diglm(string.byte(strcos,5,5),6) -- 255 if invalid
      if (numspix==255) then
        numerrk = 18 -- bad "cos=" #E18 generic -- separator
        break -- to join mark
      end--if
      numoct = lfdec1diglm(string.byte(strcos,6,6),1) -- 255 if invalid
      if (numoct==255) then
        numerrk = 18 -- bad "cos=" #E18 generic -- separator
        break -- to join mark
      end--if
      boosepenh = (numoct==1)
      if ((numspix==0) and boosepenh) then
        numerrk = 18 -- bad "cos=" #E18 -- do NOT enclose NBSP with 2 NBSP:s
        break -- to join mark
      end--if
      break -- finally to join mark
    end--while -- fake loop -- join mark

  end--if ((numerrk==0) and (boodud==false)) then

  ---- CHECK FOR INCONSISTENT PARAMETERS ----

  -- criminal conditions:
  -- # "tit=-" and "dud=1"
  -- # "tit=-" and "ctb="
  -- # "dud=1" and "base link type" <> 0 1 5 6 and "typ=" explicitly specified
  -- # "dud=0" and "base link type" is 5 or 6 (not that 5 or 6 is nvr guessed)
  -- # "fpn=-" and "base link type" <> 4 and "typ=" explicitly specified
  -- # "ans="  and "base link type" <> 0 and "typ=" explicitly specified
  -- # "fpn=" given (not "-") and "base link type" = 4
  -- # "dud=1" and "pre="
  -- # "mwp=1" and "ans="
  -- error code #E25
  -- rely on bool:s:  - "boodud", "boomwp"
  --                  - "bootits" (OTOH "booflas" not used here), "boofpns"
  --                  - "boohavfpn", "boohavtyp", "boohavans", "boohavctb"
  -- rely on integer: - "numtypb"

  if (numerrk==0) then

    if (bootits and boodud) then
      numerrk = 25 -- "tit=-" and "dud=1"
    end--if
    if (bootits and boohavctb) then
      numerrk = 25 -- "tit=-" and "ctb="
    end--if
    if (boodud and boohavtyp and (numtypb~=0) and (numtypb~=1) and (numtypb~=5) and (numtypb~=6)) then
      numerrk = 25 -- "dud=1" and bad type
    end--if
    if ((boodud==false) and ((numtypb==5) or (numtypb==6))) then
      numerrk = 25 -- "dud=0" and bad type
    end--if
    if (boofpns and boohavtyp and (numtypb~=4)) then
      numerrk = 25 -- "fpn=-" and bad type
    end--if
    if (boohavans and boohavtyp and (numtypb~=0)) then
      numerrk = 25 -- "ans=" and "base link type" <> 0
    end--if
    if (boohavfpn and (boofpns==false) and (numtypb==4)) then
      numerrk = 25 -- "fpn=" given (not "-") and bad type
    end--if
    if (boodud and boohavpre) then
      numerrk = 25 -- "dud=1" and "pre="
    end--if
    if (boomwp and boohavans) then
      numerrk = 25 -- "mwp=1" and "ans="
    end--if

  end--if

  ---- CHECK FOR INCONSISTENCY WITH FULLPAGENAME ----

  -- criminal conditions:
  -- # "mwp=1" and non-cat
  -- # "ans="  and non-special
  -- # dud=1"  and "base link type" <> 0 1 5 6 and that type auto-guessed
  -- # "fpn=-" and "base link type" <> 4 and that type auto-guessed
  -- # "ans="  and "base link type" <> 0 and that type auto-guessed
  -- # cat     and "base link type" <> 0 2 and that type auto-guessed
  -- # special and "base link type" <> 0 1 and that type auto-guessed
  -- error code #E26
  -- rely on bool:s: - "booiskat", "booisspe", "boofpns"

  if (numerrk==0) then

    if (boomwp and (booiskat==false)) then
      numerrk = 26 -- "mwp=1" and non-cat
    end--if
    if (boohavans and (booisspe==false)) then
      numerrk = 26 -- "ans=" and non-special
    end--if
    if (boodud and (numtypb~=0) and (numtypb~=1) and (numtypb~=5) and (numtypb~=6)) then
      numerrk = 26 -- "dud=1" and bad type
    end--if
    if (boofpns and (numtypb~=4)) then
      numerrk = 26 -- "fpn=-" and bad type
    end--if
    if (boohavans and (numtypb~=0)) then
      numerrk = 25 -- "ans=" and "base link type" <> 0
    end--if
    if (booiskat and (numtypb~=0) and (numtypb~=2)) then
      numerrk = 26 -- cat and bad type
    end--if
    if (booisspe and (numtypb~=0) and (numtypb~=1)) then
      numerrk = 26 -- special and bad type
    end--if

  end--if

  ---- CONVERT THE ALPHABET ----

  -- convert one long UTF8 string to separate strings (1-octet ...
  -- 4-octet) for single char:s stored in a table, fill
  -- "tababjad" and assign var "numabjad"

  -- "stralf" is guaranteed to be nonempty (5...400) either
  -- from default or from "alf=" but further checks are needed

  -- "strpre" contains the prefix string to be attached to all letters,
  -- default is empty, allows to creates sublevel index, for example a
  -- dictionary has many words "abstract" to "zone" divided on 26 subpages
  -- "A"..."Z", and then we further divide the subpage "A" as "AA" to "AZ"
  -- and index the subsections via #-type section links

  -- here we need the fullpagename "strfpn" actually only "booiskat" in
  -- order to decide whether lowercase is tolerable or not

  -- we maybe disallow ASCII lowercase, unicode lowercase is
  -- prohibited too but never checked

  -- disallow ASCII range 0...127 except:
  -- # allowed range 48...57
  -- # allowed range 65...90
  -- # maybe allowed range 97...122 lowercase
  -- # allowed single values 39 apo and 45 dash

  -- order of char:s is irrelevant ("alf=EDCBA" is fine)

  -- disallow dupes ("alf=EDCBAD" is criminal)

  -- silly "multi-char letter":s are allowed (2...9 octet:s and 2...9 UTF8
  -- char:s between brackets "(" and ")")

  -- ensure that at least 3 alphabet elements (unicode char:s
  -- or "multi-char letter":s) are available at the end

  -- possible error codes #E13 (generic) #E14 (lowercase with cat) #E15 (dupe)

  if (numerrk==0) then

    numlong = string.len (stralf) -- 5...400
    numsrcind = 1 -- ONE-based source octet index (may grow faster than dest)
    numabjad = 0 -- ZERO-based dest char/elem index (may grow slower than src)

    while (true) do -- walk through "stralf"
      if (numsrcind>numlong) then
        break -- done (but must subsequently check for minimum of 3 char:s)
      end--if
      numoct = string.byte (stralf,numsrcind,numsrcind)
      boomult = (numoct==40) -- "("
      if (boomult) then
        numsrcind = numsrcind + 1 -- ONE-based
      end--if
      numtmpy = 0 -- amount of octet:s
      numtmpz = 0 -- amount of UTF8 char:s
      strtmp = ''
      while (true) do -- copy the stuff
        if (boomult) then
          if (numsrcind>numlong) then
            numerrk = 13 -- bad "alf" (#E13 -- lower,dupe sep er co #E14,#E15)
            break -- give up -- exits INNER LOOP ONLY
          end--if
          numoct = string.byte (stralf,numsrcind,numsrcind)
          if (numoct==41) then -- ")"
            numsrcind = numsrcind + 1 -- ONE-based -- dbl low lim (2,2) to chk
            break -- exit INNER LOOP ONLY -- do NOT exit OUTER
          end--if
        end--if
        numunilen = lfutf8length (numoct) -- ZERO is invalid
        if ((numunilen==0) or ((numsrcind+numunilen-1)>numlong)) then
          numerrk = 13 -- bad "alf" (#E13 -- lower,dupe separ er co #E14,#E15)
          break -- give up -- exits INNER LOOP ONLY
        end--if
        if ((numtmpy+numunilen)>9) then -- no need to check "numtmpz" here
          numerrk = 13 -- bad "alf" (#E13 -- lower,dupe separ er co #E14,#E15)
          break -- give up -- exits INNER LOOP ONLY
        end--if
        if (numunilen==1) then -- check disallowed ASCII ranges
          numranger = lfranges (numoct) -- 0 good -- 1 lower -- 2 malicious
          boocrap = (numranger==2) -- preliminary verdict
          if ((numranger==1) and booiskat) then
            boocrap = true -- guilty -- "a"..."z" lowercase prohibited in cat
          end--if
          if (boocrap) then
            numerrk = 13 -- bad "alf" (#E13 -- low,dupe sep er co #E14,#E15)
            if (numranger==1) then
              numerrk = 14 -- lowercase with category #E14
            end--if
            break -- give up -- exits INNER LOOP ONLY
          end--if
        end--if (numunilen==1) then
        strtmp = strtmp .. string.sub (stralf,numsrcind,(numsrcind+numunilen-1)) -- 1...4
        numsrcind = numsrcind + numunilen -- ONE-based
        numtmpy = numtmpy + numunilen -- amount of octet:s
        numtmpz = numtmpz + 1 -- amount of UTF8 char:s
        if (boomult==false) then
          break -- only 1 UTF8 char -- exits INNER LOOP -- do NOT exit OUTER
        end--if
      end--while
      if (numerrk~=0) then
        break -- crucial: exit OUTER LOOP too
      end--if
      if (boomult and ((numtmpy<2) or (numtmpz<2))) then
        numerrk = 13 -- bad "alf" (#E13 -- lower,dupe separ er code #E14,#E15)
        break -- give up -- double lower limit (2,2)
      end--if
      boocrap = false -- preassume innocence about dupe
      numtmpx = 1 -- ONE-based search index for dupe investigation
      while (true) do -- walk through "tababjad" inner loop
        if (numtmpx>numabjad) then
          break -- done INNER LOOP -- note that ZERO iterations are possible
        end--if
        if (strtmp==tababjad[numtmpx]) then
          boocrap = true -- !!! FATAL CONDITION -- dupe detected !!!
          break -- give up INNER LOOP
        end--if
        numtmpx = numtmpx + 1
      end--while
      if (boocrap) then
        numerrk = 15 -- dupe #E15
        break -- outer loop -- give up
      end--if
      numabjad = numabjad + 1 -- var ZERO-based but [] is ONE-based (F**K!!!)
      tababjad[numabjad] = strpre .. strtmp -- 0...9 octet:s + 1...4 octet:s
    end--while
    if ((numerrk==0) and (numabjad<3)) then
      numerrk = 13 -- bad "alf" (#E13 -- lower,dupe separate er co #E14,#E15)
    end--if

  end--if

  ---- GENERATE ANGRY MESSAGE IF THERE IS AN ERROR ----

  -- single element in "contabevilpar2a" holds a list with 1...4
  -- message fragments, index in the table is 1...26 #E01...#E26 error code

  -- single element in "contabevilpar3a" holds a message fragment, index
  -- range is 1...11 AKA "a"..."k" -- lowercase (parameter names "fpn"..."pre")

  -- single element in "contabevilpar4a" holds a message fragment, index
  -- range is 1...12 AKA "A"..."L" -- UPPERASE (further silly texts)

  if (numerrk~=0) then

    strret = constrkros .. constrelabg .. constrdamn -- begin red
    if (numerrk<10) then
      strret = strret .. "0" -- always 2 digits please
    end--if
    strret = strret .. tostring(numerrk) .. " -- "

    boocrap = false -- preassume innocence about generating error message
    strtpm = "" -- prebrew detail info here and use it only if complete

    str1to4err = ""
    vartmp = contabevilpar2a[numerrk] -- 1...26 -- pick list 1...4 EXT-HEX dig
    if (type(vartmp)=="string") then
      str1to4err = vartmp
    end--if
    numlong = string.len (str1to4err)

    if ((numlong==0) or (numlong>4)) then -- must be 1...4
      boocrap = true -- failed to brew the error message
    else
      numtmpx = 1 -- ONE-based index -- limit "numlong" assigned above
      while (true) do -- walk through 1...4 char:s in "str1to4err"
        if (numtmpx>numlong) then
          break -- done
        end--if
        numoct = string.byte (str1to4err,numtmpx,numtmpx) -- a...z or A...Z
        strtmp = ""
        if ((numoct>=97) and (numoct<=122)) then -- "a"..."z"
          vartmp = contabevilpar3a[numoct-96] -- pick fragment -- param name
          if (type(vartmp)=="string") then
            strtmp = vartmp
          end--if
        end--if
        if ((numoct>=65) and (numoct<=90)) then -- "A"..."Z"
          vartmp = contabevilpar4a[numoct-64] -- pick fragment -- silly text
          if (type(vartmp)=="string") then
            strtmp = vartmp
          end--if
        end--if
        if (strtmp=="") then
          boocrap = true -- failed to brew the error message
          break
        else
          strtpm = strtpm .. strtmp -- add fragment
        end--if
        numtmpx = numtmpx + 1 -- ONE-based index
      end--while
    end--if ((numlong==0) or (numlong>4)) else

    if (boocrap) then
      strtpm = "??" -- failed to brew a better error message
    end--if

    strret = strret .. strtpm .. constrelaen .. constrkros -- det + end of red

  end--if

  ---- BEGIN TO BREW THE TABLE IF APPROPRIATE ----

  -- colours are "constrborder" and "constrbakgnd" and we
  -- permute them by "numctb" (0...5) in "lfper6mut"

  -- "constrtabu7" "constrtabu8" "constrtabu9" build the "<table>" element

  -- the title is made big and bold through "<big><b>"

  -- the title is obligatory, if you hate it then suppress the complete
  -- table (bootits==true) through syntax "tit=-"

  -- note that a 1-dimensional table has the complete list in one cell
  -- but a 2-dimensional table has 17'000'000 (maybe only "numabjad") cells
  -- in a row and thus we need "colspan" for the title line

  -- below the title line we brew a narrow separation line

  if ((numerrk==0) and (bootits==false)) then
    if (boodud) then
      strtmp = '<td colspan="' .. tostring (numabjad) .. '">'
    else
      strtmp = '<td>' -- no silly span in 1-dimensinal table
    end--if
    strret = constrtabu7 .. lfper6mut (constrborder,numctb) .. constrtabu8
    strret = strret .. lfper6mut (constrbakgnd,numctb) .. constrtabu9
    strret = strret .. '<tr>' .. strtmp .. '<big><b>' .. strtit .. '</b></big></td></tr>'
    strret = strret .. '<tr style="font-size:20%;">' .. strtmp .. '&nbsp;</td></tr>' -- empty line
  end--if

  ---- APPLY THE SILLY DOT RULE IF NEEDED ----

  -- category pages can have type 0 or 2 only, and only the latter
  -- needs this silly hack

  if (booiskat and (numtypb==2)) then
    strfpn = ":" .. strfpn -- "[[:Category:Piracy]]"
  end--if

  ---- PREPARE THE SEPARATOR FOR THE 1-DIMENSIONAL LIST FROM "COS=" ----

  -- separator string will be in "strapart"
  -- (preassigned to empty "" far above, we must fix it here)

  -- this is useful for 1-dimensional table and no table only

  -- "numspix" is 1...6 index in "contabsepa" or ZERO for NBSP
  -- 1...6 -> dash, dbldash, star, wall, small central dot, huge central dot

  -- if "boosepenh" is "true" then enclose separator in 2 NBSP:s ie "&nbsp;"
  -- (NOT valid for separator type in "numspix" ZERO)

  if (numerrk==0) then
    strapart = "&nbsp;"
    if (numspix~=0) then
      vartmp = contabsepa[numspix] -- 1...6
      if (type(vartmp)=="string") then
        strapart = vartmp
      end--if
    end--if
    if (boosepenh) then
      strapart = "&nbsp;" .. strapart .. "&nbsp;" -- NOT for "numspix" = 0
    end--if
  end--if

  ---- CARRY OUT THE HARD WORK TO BREW THE 1-DIMENSIONAL LIST ----

  -- here we can have a table or not (check "bootits")

  -- a single "<tr><td>" and "</td></tr>" is needed if we have a table

  -- relevant parameters forwarded to "lfbrewlink":
  -- # numtypb (0...2)
  -- # numtycas (0...2)
  -- # numtygap (0 or 1)
  -- # strfpn (fullpagename)
  -- # strflb4 & strflb5 ("top"), strflb6 & strflb7 ("bottom")
  -- # numscw8, numscw9
  -- # boomwp
  -- # numans
  -- some of the 12 params (only 2 -- namely enclosing and "y"-part) are
  -- maybe hardcoded here rather than forwarded

  -- other relevant parameters:
  -- # "bootits"
  -- # "booflas" (suppress those 2 extra links if "true")
  -- # "strapart"

  -- the core content is "tababjad" and "numabjad" (they MUST be valid)
  -- the alphabet is in "tababjad" and amount of letters is
  -- in "numabjad" (MUST be >=3)

  if ((numerrk==0) and (boodud==false)) then

    if (bootits==false) then
      strret = strret .. "<tr><td>" -- have a table
    end--if

    if (booflas==false) then -- have "top" (here) and "bottom" (below)
      strret = strret .. lfbrewlink (numtypb, numtycas, numtygap, strfpn, strflb4, strflb5, 2, numscw8, numscw9, boomwp, numans, "") .. strapart
    end--if

    numtmpx = 1 -- ONE-based index
    while (true) do -- walk through "tababjad"
      strtmp = tababjad[numtmpx] -- this CANNOT fail
      strret = strret .. lfbrewlink (numtypb, numtycas, numtygap, strfpn, strtmp, strtmp, numenkli, numscw8, numscw9, boomwp, numans, "")
      if (numtmpx==numabjad) then
        break -- done -- ZERO iterations NOT possible
      end--if
      strret = strret .. strapart
      numtmpx = numtmpx + 1 -- complete list is in 1 table cell
    end--while

    if (booflas==false) then -- have "top" (above) and "bottom" (here)
      strret = strret .. strapart .. lfbrewlink (numtypb, numtycas, numtygap, strfpn, strflb6, strflb7, 2, numscw8, numscw9, boomwp, numans, "")
    end--if

    if (bootits==false) then
      strret = strret .. "</td></tr>" -- have a table
    end--if

  end--if

  ---- CARRY OUT THE VERY HARD WORK TO BREW THE 2-DIMENSIONAL LIST ----

  -- here we always do have a table due to "dud=1" -> (boodud==true)

  -- every link gets a separate table cell with "<td>" and "</td>"

  -- "top" and "bottom" land in a separate table row with "colspan" below the
  -- alphabet matrix, note that the table layout may be suboptimal with long
  -- visible strings for "top" and "bottom" and few elements in the alphabet

  -- relevant parameters forwarded to "lfbrewlink":
  -- # numtypb (0...2)
  -- # numtycas (0...2)
  -- # numtygap (0 or 1)
  -- # strfpn (fullpagename)
  -- # strflb4 & strflb5 ("top"), strflb6 & strflb7 ("bottom")
  -- # numscw8, numscw9
  -- # boomwp
  -- # numans
  -- some of the 12 params (only 2 -- namely enclosing and "y"-part) are
  -- maybe hardcoded here rather than forwarded

  -- other relevant parameters (no "bootits" no "strapart" here):
  -- # "booflas" (suppress those 2 extra links if "true")

  -- the core content is "tababjad" and "numabjad" (they MUST be valid)
  -- the alphabet is in "tababjad" and amount of letters is
  -- in "numabjad" (MUST be >=3)

  -- An absurd problem can occur here that we have to guard against. Apo ("'")
  -- is and must be permitted in the alphabet, but a double apo ("''") is
  -- prohibited in pagenames as it would cause italic style. Thus if the
  -- 2-dimensional processing happens to generate a double apo ("''") (note
  -- that "pre=" is prohibited together with "dud=1") then we give F**K in
  -- a link and display plain "N/A" instead.

  if ((numerrk==0) and (boodud==true)) then

    numtmpy = 1 -- ONE-based index -- move down
    while (true) do -- walk through "tababjad"
      strrow = "" -- bunch "td" elements here
      numtmpx = 1 -- ONE-based index -- move right
      while (true) do -- walk through "tababjad"
        stryonl = tababjad[numtmpy] -- this CANNOT fail
        stryyxx = stryonl .. tababjad[numtmpx] -- this CANNOT fail
        if (stryyxx=="''") then
          strtpm = "N/A" -- guard against italics
        else
          strtpm = lfbrewlink (numtypb, numtycas, numtygap, strfpn, stryyxx, stryyxx, numenkli, numscw8, numscw9, boomwp, numans, stryonl)
        end--if
        strrow = strrow .. '<td>' .. strtpm .. '</td>'
        if (numtmpx==numabjad) then
          break -- done -- ZERO iterations NOT possible -- inner loop
        end--if
        numtmpx = numtmpx + 1 -- move right
      end--while
      strret = strret .. '<tr>' .. strrow .. '</tr>'
      if (numtmpy==numabjad) then
        break -- done -- ZERO iterations NOT possible -- outer loop
      end--if
      numtmpy = numtmpy + 1 --  move down
    end--while

    if (booflas==false) then -- have those 2 extra links "top" and "bottom"
      numclspl = numabjad -- at least 3 (hard rule) but more is preferred
      numclspr = 0
      while (true) do -- this is integer division in LUA :-D
        if (numclspl<=(numclspr+1)) then -- 4 -> 2+2 | 3 -> 2+1 (NOT 1+2)
          break
        end--if
        numclspl = numclspl - 1 -- left
        numclspr = numclspr + 1 -- right
      end--while
      if ((numclspl>numclspr) and ((string.len(strflb5))<(string.len(strflb7)))) then
        numclspl = numclspl - 1 -- left -- change L=R+1 to L=R-1
        numclspr = numclspr + 1 -- right
      end--if
      strret = strret .. '<tr><td colspan="' .. tostring (numclspl) .. '">'
      strret = strret .. lfbrewlink (numtypb, numtycas, numtygap, strfpn, strflb4, strflb5, 2, numscw8, numscw9, boomwp, numans, "")
      strret = strret .. '</td><td colspan="' .. tostring (numclspr) .. '">'
      strret = strret .. lfbrewlink (numtypb, numtycas, numtygap, strfpn, strflb6, strflb7, 2, numscw8, numscw9, boomwp, numans, "")
      strret = strret .. '</td></tr>'
    end--if

  end--if

  ---- CLOSE THE TABLE IF APPROPRIATE ----

  if ((numerrk==0) and (bootits==false)) then
    strret = strret .. '</table>'
  end--if

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

  return strret

end--function

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

return ind12dim