-- merged file : lualibs-extended-merged.lua
-- parent file : lualibs-extended.lua
-- merge date  : Mon May  5 07:37:22 2014

do -- begin closure to overcome local limits and interference

if not modules then modules={} end modules ['util-str']={
  version=1.001,
  comment="companion to luat-lib.mkiv",
  author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
  copyright="PRAGMA ADE / ConTeXt Development Team",
  license="see context related readme files"
}
utilities=utilities or {}
utilities.strings=utilities.strings or {}
local strings=utilities.strings
local format,gsub,rep,sub=string.format,string.gsub,string.rep,string.sub
local load,dump=load,string.dump
local tonumber,type,tostring=tonumber,type,tostring
local unpack,concat=table.unpack,table.concat
local P,V,C,S,R,Ct,Cs,Cp,Carg,Cc=lpeg.P,lpeg.V,lpeg.C,lpeg.S,lpeg.R,lpeg.Ct,lpeg.Cs,lpeg.Cp,lpeg.Carg,lpeg.Cc
local patterns,lpegmatch=lpeg.patterns,lpeg.match
local utfchar,utfbyte=utf.char,utf.byte
local loadstripped=nil
if _LUAVERSION<5.2 then
  loadstripped=function(str,shortcuts)
    return load(str)
  end
else
  loadstripped=function(str,shortcuts)
    if shortcuts then
      return load(dump(load(str),true),nil,nil,shortcuts)
    else
      return load(dump(load(str),true))
    end
  end
end
if not number then number={} end 
local stripper=patterns.stripzeros
local function points(n)
  n=tonumber(n)
  return (not n or n==0) and "0pt" or lpegmatch(stripper,format("%.5fpt",n/65536))
end
local function basepoints(n)
  n=tonumber(n)
  return (not n or n==0) and "0bp" or lpegmatch(stripper,format("%.5fbp",n*(7200/7227)/65536))
end
number.points=points
number.basepoints=basepoints
local rubish=patterns.spaceortab^0*patterns.newline
local anyrubish=patterns.spaceortab+patterns.newline
local anything=patterns.anything
local stripped=(patterns.spaceortab^1/"")*patterns.newline
local leading=rubish^0/""
local trailing=(anyrubish^1*patterns.endofstring)/""
local redundant=rubish^3/"\n"
local pattern=Cs(leading*(trailing+redundant+stripped+anything)^0)
function strings.collapsecrlf(str)
  return lpegmatch(pattern,str)
end
local repeaters={} 
function strings.newrepeater(str,offset)
  offset=offset or 0
  local s=repeaters[str]
  if not s then
    s={}
    repeaters[str]=s
  end
  local t=s[offset]
  if t then
    return t
  end
  t={}
  setmetatable(t,{ __index=function(t,k)
    if not k then
      return ""
    end
    local n=k+offset
    local s=n>0 and rep(str,n) or ""
    t[k]=s
    return s
  end })
  s[offset]=t
  return t
end
local extra,tab,start=0,0,4,0
local nspaces=strings.newrepeater(" ")
string.nspaces=nspaces
local pattern=Carg(1)/function(t)
    extra,tab,start=0,t or 7,1
  end*Cs((
   Cp()*patterns.tab/function(position)
     local current=(position-start+1)+extra
     local spaces=tab-(current-1)%tab
     if spaces>0 then
       extra=extra+spaces-1
       return nspaces[spaces] 
     else
       return ""
     end
   end+patterns.newline*Cp()/function(position)
     extra,start=0,position
   end+patterns.anything
 )^1)
function strings.tabtospace(str,tab)
  return lpegmatch(pattern,str,1,tab or 7)
end
local newline=patterns.newline
local endofstring=patterns.endofstring
local whitespace=patterns.whitespace
local spacer=patterns.spacer
local space=spacer^0
local nospace=space/""
local endofline=nospace*newline
local stripend=(whitespace^1*endofstring)/""
local normalline=(nospace*((1-space*(newline+endofstring))^1)*nospace)
local stripempty=endofline^1/""
local normalempty=endofline^1
local singleempty=endofline*(endofline^0/"")
local doubleempty=endofline*endofline^-1*(endofline^0/"")
local stripstart=stripempty^0
local p_prune_normal=Cs (stripstart*(stripend+normalline+normalempty )^0 )
local p_prune_collapse=Cs (stripstart*(stripend+normalline+doubleempty )^0 )
local p_prune_noempty=Cs (stripstart*(stripend+normalline+singleempty )^0 )
local p_retain_normal=Cs ((normalline+normalempty )^0 )
local p_retain_collapse=Cs ((normalline+doubleempty )^0 )
local p_retain_noempty=Cs ((normalline+singleempty )^0 )
local striplinepatterns={
  ["prune"]=p_prune_normal,
  ["prune and collapse"]=p_prune_collapse,
  ["prune and no empty"]=p_prune_noempty,
  ["retain"]=p_retain_normal,
  ["retain and collapse"]=p_retain_collapse,
  ["retain and no empty"]=p_retain_noempty,
}
strings.striplinepatterns=striplinepatterns
function strings.striplines(str,how)
  return str and lpegmatch(how and striplinepatterns[how] or p_prune_collapse,str) or str
end
strings.striplong=strings.striplines
function strings.nice(str)
  str=gsub(str,"[:%-+_]+"," ") 
  return str
end
local n=0
local sequenced=table.sequenced
function string.autodouble(s,sep)
  if s==nil then
    return '""'
  end
  local t=type(s)
  if t=="number" then
    return tostring(s) 
  end
  if t=="table" then
    return ('"'..sequenced(s,sep or ",")..'"')
  end
  return ('"'..tostring(s)..'"')
end
function string.autosingle(s,sep)
  if s==nil then
    return "''"
  end
  local t=type(s)
  if t=="number" then
    return tostring(s) 
  end
  if t=="table" then
    return ("'"..sequenced(s,sep or ",").."'")
  end
  return ("'"..tostring(s).."'")
end
local tracedchars={}
string.tracedchars=tracedchars
strings.tracers=tracedchars
function string.tracedchar(b)
  if type(b)=="number" then
    return tracedchars[b] or (utfchar(b).." (U+"..format('%05X',b)..")")
  else
    local c=utfbyte(b)
    return tracedchars[c] or (b.." (U+"..format('%05X',c)..")")
  end
end
function number.signed(i)
  if i>0 then
    return "+",i
  else
    return "-",-i
  end
end
local zero=P("0")^1/""
local plus=P("+")/""
local minus=P("-")
local separator=S(".")
local digit=R("09")
local trailing=zero^1*#S("eE")
local exponent=(S("eE")*(plus+Cs((minus*zero^0*P(-1))/"")+minus)*zero^0*(P(-1)*Cc("0")+P(1)^1))
local pattern_a=Cs(minus^0*digit^1*(separator/""*trailing+separator*(trailing+digit)^0)*exponent)
local pattern_b=Cs((exponent+P(1))^0)
function number.sparseexponent(f,n)
  if not n then
    n=f
    f="%e"
  end
  local tn=type(n)
  if tn=="string" then 
    local m=tonumber(n)
    if m then
      return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,m))
    end
  elseif tn=="number" then
    return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,n))
  end
  return tostring(n)
end
local template=[[
%s
%s
return function(%s) return %s end
]]
local preamble,environment="",{}
if _LUAVERSION<5.2 then
  preamble=[[
local lpeg=lpeg
local type=type
local tostring=tostring
local tonumber=tonumber
local format=string.format
local concat=table.concat
local signed=number.signed
local points=number.points
local basepoints= number.basepoints
local utfchar=utf.char
local utfbyte=utf.byte
local lpegmatch=lpeg.match
local nspaces=string.nspaces
local tracedchar=string.tracedchar
local autosingle=string.autosingle
local autodouble=string.autodouble
local sequenced=table.sequenced
local formattednumber=number.formatted
local sparseexponent=number.sparseexponent
    ]]
else
  environment={
    global=global or _G,
    lpeg=lpeg,
    type=type,
    tostring=tostring,
    tonumber=tonumber,
    format=string.format,
    concat=table.concat,
    signed=number.signed,
    points=number.points,
    basepoints=number.basepoints,
    utfchar=utf.char,
    utfbyte=utf.byte,
    lpegmatch=lpeg.match,
    nspaces=string.nspaces,
    tracedchar=string.tracedchar,
    autosingle=string.autosingle,
    autodouble=string.autodouble,
    sequenced=table.sequenced,
    formattednumber=number.formatted,
    sparseexponent=number.sparseexponent,
  }
end
local arguments={ "a1" } 
setmetatable(arguments,{ __index=function(t,k)
    local v=t[k-1]..",a"..k
    t[k]=v
    return v
  end
})
local prefix_any=C((S("+- .")+R("09"))^0)
local prefix_tab=P("{")*C((1-P("}"))^0)*P("}")+C((1-R("az","AZ","09","%%"))^0)
local format_s=function(f)
  n=n+1
  if f and f~="" then
    return format("format('%%%ss',a%s)",f,n)
  else 
    return format("(a%s or '')",n) 
  end
end
local format_S=function(f) 
  n=n+1
  if f and f~="" then
    return format("format('%%%ss',tostring(a%s))",f,n)
  else
    return format("tostring(a%s)",n)
  end
end
local format_q=function()
  n=n+1
  return format("(a%s and format('%%q',a%s) or '')",n,n) 
end
local format_Q=function() 
  n=n+1
  return format("format('%%q',tostring(a%s))",n)
end
local format_i=function(f)
  n=n+1
  if f and f~="" then
    return format("format('%%%si',a%s)",f,n)
  else
    return format("format('%%i',a%s)",n) 
  end
end
local format_d=format_i
local format_I=function(f)
  n=n+1
  return format("format('%%s%%%si',signed(a%s))",f,n)
end
local format_f=function(f)
  n=n+1
  return format("format('%%%sf',a%s)",f,n)
end
local format_F=function(f)
  n=n+1
  return format("((a%s == 0 and '0') or (a%s == 1 and '1') or format('%%%sf',a%s))",n,n,f,n)
end
local format_g=function(f)
  n=n+1
  return format("format('%%%sg',a%s)",f,n)
end
local format_G=function(f)
  n=n+1
  return format("format('%%%sG',a%s)",f,n)
end
local format_e=function(f)
  n=n+1
  return format("format('%%%se',a%s)",f,n)
end
local format_E=function(f)
  n=n+1
  return format("format('%%%sE',a%s)",f,n)
end
local format_j=function(f)
  n=n+1
  return format("sparseexponent('%%%se',a%s)",f,n)
end
local format_J=function(f)
  n=n+1
  return format("sparseexponent('%%%sE',a%s)",f,n)
end
local format_x=function(f)
  n=n+1
  return format("format('%%%sx',a%s)",f,n)
end
local format_X=function(f)
  n=n+1
  return format("format('%%%sX',a%s)",f,n)
end
local format_o=function(f)
  n=n+1
  return format("format('%%%so',a%s)",f,n)
end
local format_c=function()
  n=n+1
  return format("utfchar(a%s)",n)
end
local format_C=function()
  n=n+1
  return format("tracedchar(a%s)",n)
end
local format_r=function(f)
  n=n+1
  return format("format('%%%s.0f',a%s)",f,n)
end
local format_h=function(f)
  n=n+1
  if f=="-" then
    f=sub(f,2)
    return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
  else
    return format("format('0x%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
  end
end
local format_H=function(f)
  n=n+1
  if f=="-" then
    f=sub(f,2)
    return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
  else
    return format("format('0x%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
  end
end
local format_u=function(f)
  n=n+1
  if f=="-" then
    f=sub(f,2)
    return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
  else
    return format("format('u+%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
  end
end
local format_U=function(f)
  n=n+1
  if f=="-" then
    f=sub(f,2)
    return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
  else
    return format("format('U+%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n)
  end
end
local format_p=function()
  n=n+1
  return format("points(a%s)",n)
end
local format_b=function()
  n=n+1
  return format("basepoints(a%s)",n)
end
local format_t=function(f)
  n=n+1
  if f and f~="" then
    return format("concat(a%s,%q)",n,f)
  else
    return format("concat(a%s)",n)
  end
end
local format_T=function(f)
  n=n+1
  if f and f~="" then
    return format("sequenced(a%s,%q)",n,f)
  else
    return format("sequenced(a%s)",n)
  end
end
local format_l=function()
  n=n+1
  return format("(a%s and 'true' or 'false')",n)
end
local format_L=function()
  n=n+1
  return format("(a%s and 'TRUE' or 'FALSE')",n)
end
local format_N=function() 
  n=n+1
  return format("tostring(tonumber(a%s) or a%s)",n,n)
end
local format_a=function(f)
  n=n+1
  if f and f~="" then
    return format("autosingle(a%s,%q)",n,f)
  else
    return format("autosingle(a%s)",n)
  end
end
local format_A=function(f)
  n=n+1
  if f and f~="" then
    return format("autodouble(a%s,%q)",n,f)
  else
    return format("autodouble(a%s)",n)
  end
end
local format_w=function(f) 
  n=n+1
  f=tonumber(f)
  if f then 
    return format("nspaces[%s+a%s]",f,n) 
  else
    return format("nspaces[a%s]",n) 
  end
end
local format_W=function(f) 
  return format("nspaces[%s]",tonumber(f) or 0)
end
local digit=patterns.digit
local period=patterns.period
local three=digit*digit*digit
local splitter=Cs (
  (((1-(three^1*period))^1+C(three))*(Carg(1)*three)^1+C((1-period)^1))*(P(1)/""*Carg(2))*C(2)
)
patterns.formattednumber=splitter
function number.formatted(n,sep1,sep2)
  local s=type(s)=="string" and n or format("%0.2f",n)
  if sep1==true then
    return lpegmatch(splitter,s,1,".",",")
  elseif sep1=="." then
    return lpegmatch(splitter,s,1,sep1,sep2 or ",")
  elseif sep1=="," then
    return lpegmatch(splitter,s,1,sep1,sep2 or ".")
  else
    return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".")
  end
end
local format_m=function(f)
  n=n+1
  if not f or f=="" then
    f=","
  end
  return format([[formattednumber(a%s,%q,".")]],n,f)
end
local format_M=function(f)
  n=n+1
  if not f or f=="" then
    f="."
  end
  return format([[formattednumber(a%s,%q,",")]],n,f)
end
local format_z=function(f)
  n=n+(tonumber(f) or 1)
  return "''" 
end
local format_rest=function(s)
  return format("%q",s) 
end
local format_extension=function(extensions,f,name)
  local extension=extensions[name] or "tostring(%s)"
  local f=tonumber(f) or 1
  if f==0 then
    return extension
  elseif f==1 then
    n=n+1
    local a="a"..n
    return format(extension,a,a) 
  elseif f<0 then
    local a="a"..(n+f+1)
    return format(extension,a,a)
  else
    local t={}
    for i=1,f do
      n=n+1
      t[#t+1]="a"..n
    end
    return format(extension,unpack(t))
  end
end
local builder=Cs { "start",
  start=(
    (
      P("%")/""*(
        V("!") 
+V("s")+V("q")+V("i")+V("d")+V("f")+V("F")+V("g")+V("G")+V("e")+V("E")+V("x")+V("X")+V("o")
+V("c")+V("C")+V("S") 
+V("Q") 
+V("N")
+V("r")+V("h")+V("H")+V("u")+V("U")+V("p")+V("b")+V("t")+V("T")+V("l")+V("L")+V("I")+V("w") 
+V("W") 
+V("a") 
+V("A") 
+V("j")+V("J") 
+V("m")+V("M") 
+V("z")
+V("*") 
      )+V("*")
    )*(P(-1)+Carg(1))
  )^0,
  ["s"]=(prefix_any*P("s"))/format_s,
  ["q"]=(prefix_any*P("q"))/format_q,
  ["i"]=(prefix_any*P("i"))/format_i,
  ["d"]=(prefix_any*P("d"))/format_d,
  ["f"]=(prefix_any*P("f"))/format_f,
  ["F"]=(prefix_any*P("F"))/format_F,
  ["g"]=(prefix_any*P("g"))/format_g,
  ["G"]=(prefix_any*P("G"))/format_G,
  ["e"]=(prefix_any*P("e"))/format_e,
  ["E"]=(prefix_any*P("E"))/format_E,
  ["x"]=(prefix_any*P("x"))/format_x,
  ["X"]=(prefix_any*P("X"))/format_X,
  ["o"]=(prefix_any*P("o"))/format_o,
  ["S"]=(prefix_any*P("S"))/format_S,
  ["Q"]=(prefix_any*P("Q"))/format_S,
  ["N"]=(prefix_any*P("N"))/format_N,
  ["c"]=(prefix_any*P("c"))/format_c,
  ["C"]=(prefix_any*P("C"))/format_C,
  ["r"]=(prefix_any*P("r"))/format_r,
  ["h"]=(prefix_any*P("h"))/format_h,
  ["H"]=(prefix_any*P("H"))/format_H,
  ["u"]=(prefix_any*P("u"))/format_u,
  ["U"]=(prefix_any*P("U"))/format_U,
  ["p"]=(prefix_any*P("p"))/format_p,
  ["b"]=(prefix_any*P("b"))/format_b,
  ["t"]=(prefix_tab*P("t"))/format_t,
  ["T"]=(prefix_tab*P("T"))/format_T,
  ["l"]=(prefix_any*P("l"))/format_l,
  ["L"]=(prefix_any*P("L"))/format_L,
  ["I"]=(prefix_any*P("I"))/format_I,
  ["w"]=(prefix_any*P("w"))/format_w,
  ["W"]=(prefix_any*P("W"))/format_W,
  ["j"]=(prefix_any*P("j"))/format_j,
  ["J"]=(prefix_any*P("J"))/format_J,
  ["m"]=(prefix_tab*P("m"))/format_m,
  ["M"]=(prefix_tab*P("M"))/format_M,
  ["z"]=(prefix_any*P("z"))/format_z,
  ["a"]=(prefix_any*P("a"))/format_a,
  ["A"]=(prefix_any*P("A"))/format_A,
  ["*"]=Cs(((1-P("%"))^1+P("%%")/"%%")^1)/format_rest,
  ["!"]=Carg(2)*prefix_any*P("!")*C((1-P("!"))^1)*P("!")/format_extension,
}
local direct=Cs (
  P("%")*(S("+- .")+R("09"))^0*S("sqidfgGeExXo")*P(-1)/[[local format = string.format return function(str) return format("%0",str) end]]
)
local function make(t,str)
  local f
  local p
  local p=lpegmatch(direct,str)
  if p then
    f=loadstripped(p)()
  else
    n=0
    p=lpegmatch(builder,str,1,t._connector_,t._extensions_) 
    if n>0 then
      p=format(template,preamble,t._preamble_,arguments[n],p)
      f=loadstripped(p,t._environment_)() 
    else
      f=function() return str end
    end
  end
  t[str]=f
  return f
end
local function use(t,fmt,...)
  return t[fmt](...)
end
strings.formatters={}
if _LUAVERSION<5.2 then
  function strings.formatters.new(noconcat)
    local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_=preamble,_environment_={} }
    setmetatable(t,{ __index=make,__call=use })
    return t
  end
else
  function strings.formatters.new(noconcat)
    local e={} 
    for k,v in next,environment do
      e[k]=v
    end
    local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_="",_environment_=e }
    setmetatable(t,{ __index=make,__call=use })
    return t
  end
end
local formatters=strings.formatters.new() 
string.formatters=formatters 
string.formatter=function(str,...) return formatters[str](...) end 
local function add(t,name,template,preamble)
  if type(t)=="table" and t._type_=="formatter" then
    t._extensions_[name]=template or "%s"
    if type(preamble)=="string" then
      t._preamble_=preamble.."\n"..t._preamble_ 
    elseif type(preamble)=="table" then
      for k,v in next,preamble do
        t._environment_[k]=v
      end
    end
  end
end
strings.formatters.add=add
patterns.xmlescape=Cs((P("<")/"&lt;"+P(">")/"&gt;"+P("&")/"&amp;"+P('"')/"&quot;"+P(1))^0)
patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+P(1))^0)
patterns.luaescape=Cs(((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0) 
patterns.luaquoted=Cs(Cc('"')*((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0*Cc('"'))
if _LUAVERSION<5.2 then
  add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],"local xmlescape = lpeg.patterns.xmlescape")
  add(formatters,"tex",[[lpegmatch(texescape,%s)]],"local texescape = lpeg.patterns.texescape")
  add(formatters,"lua",[[lpegmatch(luaescape,%s)]],"local luaescape = lpeg.patterns.luaescape")
else
  add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],{ xmlescape=lpeg.patterns.xmlescape })
  add(formatters,"tex",[[lpegmatch(texescape,%s)]],{ texescape=lpeg.patterns.texescape })
  add(formatters,"lua",[[lpegmatch(luaescape,%s)]],{ luaescape=lpeg.patterns.luaescape })
end

end -- closure

do -- begin closure to overcome local limits and interference

if not modules then modules={} end modules ['util-tab']={
  version=1.001,
  comment="companion to luat-lib.mkiv",
  author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
  copyright="PRAGMA ADE / ConTeXt Development Team",
  license="see context related readme files"
}
utilities=utilities or {}
utilities.tables=utilities.tables or {}
local tables=utilities.tables
local format,gmatch,gsub,sub=string.format,string.gmatch,string.gsub,string.sub
local concat,insert,remove=table.concat,table.insert,table.remove
local setmetatable,getmetatable,tonumber,tostring=setmetatable,getmetatable,tonumber,tostring
local type,next,rawset,tonumber,tostring,load,select=type,next,rawset,tonumber,tostring,load,select
local lpegmatch,P,Cs,Cc=lpeg.match,lpeg.P,lpeg.Cs,lpeg.Cc
local sortedkeys,sortedpairs=table.sortedkeys,table.sortedpairs
local formatters=string.formatters
local utftoeight=utf.toeight
local splitter=lpeg.tsplitat(".")
function utilities.tables.definetable(target,nofirst,nolast) 
  local composed,t=nil,{}
  local snippets=lpegmatch(splitter,target)
  for i=1,#snippets-(nolast and 1 or 0) do
    local name=snippets[i]
    if composed then
      composed=composed.."."..name
        t[#t+1]=formatters["if not %s then %s = { } end"](composed,composed)
    else
      composed=name
      if not nofirst then
        t[#t+1]=formatters["%s = %s or { }"](composed,composed)
      end
    end
  end
  if composed then
    if nolast then
      composed=composed.."."..snippets[#snippets]
    end
    return concat(t,"\n"),composed 
  else
    return "",target
  end
end
function tables.definedtable(...)
  local t=_G
  for i=1,select("#",...) do
    local li=select(i,...)
    local tl=t[li]
    if not tl then
      tl={}
      t[li]=tl
    end
    t=tl
  end
  return t
end
function tables.accesstable(target,root)
  local t=root or _G
  for name in gmatch(target,"([^%.]+)") do
    t=t[name]
    if not t then
      return
    end
  end
  return t
end
function tables.migratetable(target,v,root)
  local t=root or _G
  local names=lpegmatch(splitter,target)
  for i=1,#names-1 do
    local name=names[i]
    t[name]=t[name] or {}
    t=t[name]
    if not t then
      return
    end
  end
  t[names[#names]]=v
end
function tables.removevalue(t,value) 
  if value then
    for i=1,#t do
      if t[i]==value then
        remove(t,i)
      end
    end
  end
end
function tables.insertbeforevalue(t,value,extra)
  for i=1,#t do
    if t[i]==extra then
      remove(t,i)
    end
  end
  for i=1,#t do
    if t[i]==value then
      insert(t,i,extra)
      return
    end
  end
  insert(t,1,extra)
end
function tables.insertaftervalue(t,value,extra)
  for i=1,#t do
    if t[i]==extra then
      remove(t,i)
    end
  end
  for i=1,#t do
    if t[i]==value then
      insert(t,i+1,extra)
      return
    end
  end
  insert(t,#t+1,extra)
end
local escape=Cs(Cc('"')*((P('"')/'""'+P(1))^0)*Cc('"'))
function table.tocsv(t,specification)
  if t and #t>0 then
    local result={}
    local r={}
    specification=specification or {}
    local fields=specification.fields
    if type(fields)~="string" then
      fields=sortedkeys(t[1])
    end
    local separator=specification.separator or ","
    if specification.preamble==true then
      for f=1,#fields do
        r[f]=lpegmatch(escape,tostring(fields[f]))
      end
      result[1]=concat(r,separator)
    end
    for i=1,#t do
      local ti=t[i]
      for f=1,#fields do
        local field=ti[fields[f]]
        if type(field)=="string" then
          r[f]=lpegmatch(escape,field)
        else
          r[f]=tostring(field)
        end
      end
      result[#result+1]=concat(r,separator)
    end
    return concat(result,"\n")
  else
    return ""
  end
end
local nspaces=utilities.strings.newrepeater(" ")
local function toxml(t,d,result,step)
  for k,v in sortedpairs(t) do
    local s=nspaces[d] 
    local tk=type(k)
    local tv=type(v)
    if tv=="table" then
      if tk=="number" then
        result[#result+1]=formatters["%s<entry n='%s'>"](s,k)
        toxml(v,d+step,result,step)
        result[#result+1]=formatters["%s</entry>"](s,k)
      else
        result[#result+1]=formatters["%s<%s>"](s,k)
        toxml(v,d+step,result,step)
        result[#result+1]=formatters["%s</%s>"](s,k)
      end
    elseif tv=="string" then
      if tk=="number" then
        result[#result+1]=formatters["%s<entry n='%s'>%!xml!</entry>"](s,k,v,k)
      else
        result[#result+1]=formatters["%s<%s>%!xml!</%s>"](s,k,v,k)
      end
    elseif tk=="number" then
      result[#result+1]=formatters["%s<entry n='%s'>%S</entry>"](s,k,v,k)
    else
      result[#result+1]=formatters["%s<%s>%S</%s>"](s,k,v,k)
    end
  end
end
function table.toxml(t,specification)
  specification=specification or {}
  local name=specification.name
  local noroot=name==false
  local result=(specification.nobanner or noroot) and {} or { "<?xml version='1.0' standalone='yes' ?>" }
  local indent=specification.indent or 0
  local spaces=specification.spaces or 1
  if noroot then
    toxml(t,indent,result,spaces)
  else
    toxml({ [name or "data"]=t },indent,result,spaces)
  end
  return concat(result,"\n")
end
function tables.encapsulate(core,capsule,protect)
  if type(capsule)~="table" then
    protect=true
    capsule={}
  end
  for key,value in next,core do
    if capsule[key] then
      print(formatters["\ninvalid %s %a in %a"]("inheritance",key,core))
      os.exit()
    else
      capsule[key]=value
    end
  end
  if protect then
    for key,value in next,core do
      core[key]=nil
    end
    setmetatable(core,{
      __index=capsule,
      __newindex=function(t,key,value)
        if capsule[key] then
          print(formatters["\ninvalid %s %a' in %a"]("overload",key,core))
          os.exit()
        else
          rawset(t,key,value)
        end
      end
    } )
  end
end
local f_hashed_string=formatters["[%q]=%q,"]
local f_hashed_number=formatters["[%q]=%s,"]
local f_hashed_boolean=formatters["[%q]=%l,"]
local f_hashed_table=formatters["[%q]="]
local f_indexed_string=formatters["[%s]=%q,"]
local f_indexed_number=formatters["[%s]=%s,"]
local f_indexed_boolean=formatters["[%s]=%l,"]
local f_indexed_table=formatters["[%s]="]
local f_ordered_string=formatters["%q,"]
local f_ordered_number=formatters["%s,"]
local f_ordered_boolean=formatters["%l,"]
function table.fastserialize(t,prefix)
  local r={ type(prefix)=="string" and prefix or "return" }
  local m=1
  local function fastserialize(t,outer) 
    local n=#t
    m=m+1
    r[m]="{"
    if n>0 then
      for i=0,n do
        local v=t[i]
        local tv=type(v)
        if tv=="string" then
          m=m+1 r[m]=f_ordered_string(v)
        elseif tv=="number" then
          m=m+1 r[m]=f_ordered_number(v)
        elseif tv=="table" then
          fastserialize(v)
        elseif tv=="boolean" then
          m=m+1 r[m]=f_ordered_boolean(v)
        end
      end
    end
    for k,v in next,t do
      local tk=type(k)
      if tk=="number" then
        if k>n or k<0 then
          local tv=type(v)
          if tv=="string" then
            m=m+1 r[m]=f_indexed_string(k,v)
          elseif tv=="number" then
            m=m+1 r[m]=f_indexed_number(k,v)
          elseif tv=="table" then
            m=m+1 r[m]=f_indexed_table(k)
            fastserialize(v)
          elseif tv=="boolean" then
            m=m+1 r[m]=f_indexed_boolean(k,v)
          end
        end
      else
        local tv=type(v)
        if tv=="string" then
          m=m+1 r[m]=f_hashed_string(k,v)
        elseif tv=="number" then
          m=m+1 r[m]=f_hashed_number(k,v)
        elseif tv=="table" then
          m=m+1 r[m]=f_hashed_table(k)
          fastserialize(v)
        elseif tv=="boolean" then
          m=m+1 r[m]=f_hashed_boolean(k,v)
        end
      end
    end
    m=m+1
    if outer then
      r[m]="}"
    else
      r[m]="},"
    end
    return r
  end
  return concat(fastserialize(t,true))
end
function table.deserialize(str)
  if not str or str=="" then
    return
  end
  local code=load(str)
  if not code then
    return
  end
  code=code()
  if not code then
    return
  end
  return code
end
function table.load(filename,loader)
  if filename then
    local t=(loader or io.loaddata)(filename)
    if t and t~="" then
      local t=utftoeight(t)
      t=load(t)
      if type(t)=="function" then
        t=t()
        if type(t)=="table" then
          return t
        end
      end
    end
  end
end
function table.save(filename,t,n,...)
  io.savedata(filename,table.serialize(t,n==nil and true or n,...)) 
end
local f_key_value=formatters["%s=%q"]
local f_add_table=formatters[" {%t},\n"]
local f_return_table=formatters["return {\n%t}"]
local function slowdrop(t) 
  local r={}
  local l={}
  for i=1,#t do
    local ti=t[i]
    local j=0
    for k,v in next,ti do
      j=j+1
      l[j]=f_key_value(k,v)
    end
    r[i]=f_add_table(l)
  end
  return f_return_table(r)
end
local function fastdrop(t)
  local r={ "return {\n" }
  local m=1
  for i=1,#t do
    local ti=t[i]
    m=m+1 r[m]=" {"
    for k,v in next,ti do
      m=m+1 r[m]=f_key_value(k,v)
    end
    m=m+1 r[m]="},\n"
  end
  m=m+1
  r[m]="}"
  return concat(r)
end
function table.drop(t,slow) 
  if #t==0 then
    return "return { }"
  elseif slow==true then
    return slowdrop(t) 
  else
    return fastdrop(t) 
  end
end
function table.autokey(t,k)
  local v={}
  t[k]=v
  return v
end
local selfmapper={ __index=function(t,k) t[k]=k return k end }
function table.twowaymapper(t)
  if not t then
    t={}
  else
    for i=0,#t do
      local ti=t[i]    
      if ti then
        local i=tostring(i)
        t[i]=ti   
        t[ti]=i    
      end
    end
    t[""]=t[0] or ""
  end
  setmetatable(t,selfmapper)
  return t
end
local f_start_key_idx=formatters["%w{"]
local f_start_key_num=formatters["%w[%s]={"]
local f_start_key_str=formatters["%w[%q]={"]
local f_start_key_boo=formatters["%w[%l]={"]
local f_start_key_nop=formatters["%w{"]
local f_stop=formatters["%w},"]
local f_key_num_value_num=formatters["%w[%s]=%s,"]
local f_key_str_value_num=formatters["%w[%q]=%s,"]
local f_key_boo_value_num=formatters["%w[%l]=%s,"]
local f_key_num_value_str=formatters["%w[%s]=%q,"]
local f_key_str_value_str=formatters["%w[%q]=%q,"]
local f_key_boo_value_str=formatters["%w[%l]=%q,"]
local f_key_num_value_boo=formatters["%w[%s]=%l,"]
local f_key_str_value_boo=formatters["%w[%q]=%l,"]
local f_key_boo_value_boo=formatters["%w[%l]=%l,"]
local f_key_num_value_not=formatters["%w[%s]={},"]
local f_key_str_value_not=formatters["%w[%q]={},"]
local f_key_boo_value_not=formatters["%w[%l]={},"]
local f_key_num_value_seq=formatters["%w[%s]={ %, t },"]
local f_key_str_value_seq=formatters["%w[%q]={ %, t },"]
local f_key_boo_value_seq=formatters["%w[%l]={ %, t },"]
local f_val_num=formatters["%w%s,"]
local f_val_str=formatters["%w%q,"]
local f_val_boo=formatters["%w%l,"]
local f_val_not=formatters["%w{},"]
local f_val_seq=formatters["%w{ %, t },"]
local f_table_return=formatters["return {"]
local f_table_name=formatters["%s={"]
local f_table_direct=formatters["{"]
local f_table_entry=formatters["[%q]={"]
local f_table_finish=formatters["}"]
local spaces=utilities.strings.newrepeater(" ")
local serialize=table.serialize
function table.serialize(root,name,specification)
  if type(specification)=="table" then
    return serialize(root,name,specification) 
  end
  local t 
  local n=1
  local function simple_table(t)
    if #t>0 then
      local n=0
      for _,v in next,t do
        n=n+1
        if type(v)=="table" then
          return nil
        end
      end
      if n==#t then
        local tt={}
        local nt=0
        for i=1,#t do
          local v=t[i]
          local tv=type(v)
          nt=nt+1
          if tv=="number" then
            tt[nt]=v
          elseif tv=="string" then
            tt[nt]=format("%q",v) 
          elseif tv=="boolean" then
            tt[nt]=v and "true" or "false"
          else
            return nil
          end
        end
        return tt
      end
    end
    return nil
  end
  local function do_serialize(root,name,depth,level,indexed)
    if level>0 then
      n=n+1
      if indexed then
        t[n]=f_start_key_idx(depth)
      else
        local tn=type(name)
        if tn=="number" then
          t[n]=f_start_key_num(depth,name)
        elseif tn=="string" then
          t[n]=f_start_key_str(depth,name)
        elseif tn=="boolean" then
          t[n]=f_start_key_boo(depth,name)
        else
          t[n]=f_start_key_nop(depth)
        end
      end
      depth=depth+1
    end
    if root and next(root) then
      local first=nil
      local last=0
      last=#root
      for k=1,last do
        if root[k]==nil then
          last=k-1
          break
        end
      end
      if last>0 then
        first=1
      end
      local sk=sortedkeys(root) 
      for i=1,#sk do
        local k=sk[i]
        local v=root[k]
        local tv=type(v)
        local tk=type(k)
        if first and tk=="number" and k>=first and k<=last then
          if tv=="number" then
            n=n+1 t[n]=f_val_num(depth,v)
          elseif tv=="string" then
            n=n+1 t[n]=f_val_str(depth,v)
          elseif tv=="table" then
            if not next(v) then
              n=n+1 t[n]=f_val_not(depth)
            else
              local st=simple_table(v)
              if st then
                n=n+1 t[n]=f_val_seq(depth,st)
              else
                do_serialize(v,k,depth,level+1,true)
              end
            end
          elseif tv=="boolean" then
            n=n+1 t[n]=f_val_boo(depth,v)
          end
        elseif tv=="number" then
          if tk=="number" then
            n=n+1 t[n]=f_key_num_value_num(depth,k,v)
          elseif tk=="string" then
            n=n+1 t[n]=f_key_str_value_num(depth,k,v)
          elseif tk=="boolean" then
            n=n+1 t[n]=f_key_boo_value_num(depth,k,v)
          end
        elseif tv=="string" then
          if tk=="number" then
            n=n+1 t[n]=f_key_num_value_str(depth,k,v)
          elseif tk=="string" then
            n=n+1 t[n]=f_key_str_value_str(depth,k,v)
          elseif tk=="boolean" then
            n=n+1 t[n]=f_key_boo_value_str(depth,k,v)
          end
        elseif tv=="table" then
          if not next(v) then
            if tk=="number" then
              n=n+1 t[n]=f_key_num_value_not(depth,k,v)
            elseif tk=="string" then
              n=n+1 t[n]=f_key_str_value_not(depth,k,v)
            elseif tk=="boolean" then
              n=n+1 t[n]=f_key_boo_value_not(depth,k,v)
            end
          else
            local st=simple_table(v)
            if not st then
              do_serialize(v,k,depth,level+1)
            elseif tk=="number" then
              n=n+1 t[n]=f_key_num_value_seq(depth,k,st)
            elseif tk=="string" then
              n=n+1 t[n]=f_key_str_value_seq(depth,k,st)
            elseif tk=="boolean" then
              n=n+1 t[n]=f_key_boo_value_seq(depth,k,st)
            end
          end
        elseif tv=="boolean" then
          if tk=="number" then
            n=n+1 t[n]=f_key_num_value_boo(depth,k,v)
          elseif tk=="string" then
            n=n+1 t[n]=f_key_str_value_boo(depth,k,v)
          elseif tk=="boolean" then
            n=n+1 t[n]=f_key_boo_value_boo(depth,k,v)
          end
        end
      end
    end
    if level>0 then
      n=n+1 t[n]=f_stop(depth-1)
    end
  end
  local tname=type(name)
  if tname=="string" then
    if name=="return" then
      t={ f_table_return() }
    else
      t={ f_table_name(name) }
    end
  elseif tname=="number" then
    t={ f_table_entry(name) }
  elseif tname=="boolean" then
    if name then
      t={ f_table_return() }
    else
      t={ f_table_direct() }
    end
  else
    t={ f_table_name("t") }
  end
  if root then
    if getmetatable(root) then 
      local dummy=root._w_h_a_t_e_v_e_r_
      root._w_h_a_t_e_v_e_r_=nil
    end
    if next(root) then
      do_serialize(root,name,1,0)
    end
  end
  n=n+1
  t[n]=f_table_finish()
  return concat(t,"\n")
end

end -- closure

do -- begin closure to overcome local limits and interference

if not modules then modules={} end modules ['util-sto']={
  version=1.001,
  comment="companion to luat-lib.mkiv",
  author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
  copyright="PRAGMA ADE / ConTeXt Development Team",
  license="see context related readme files"
}
local setmetatable,getmetatable,type=setmetatable,getmetatable,type
utilities=utilities or {}
utilities.storage=utilities.storage or {}
local storage=utilities.storage
function storage.mark(t)
  if not t then
    print("\nfatal error: storage cannot be marked\n")
    os.exit()
    return
  end
  local m=getmetatable(t)
  if not m then
    m={}
    setmetatable(t,m)
  end
  m.__storage__=true
  return t
end
function storage.allocate(t)
  t=t or {}
  local m=getmetatable(t)
  if not m then
    m={}
    setmetatable(t,m)
  end
  m.__storage__=true
  return t
end
function storage.marked(t)
  local m=getmetatable(t)
  return m and m.__storage__
end
function storage.checked(t)
  if not t then
    report("\nfatal error: storage has not been allocated\n")
    os.exit()
    return
  end
  return t
end
function storage.setinitializer(data,initialize)
  local m=getmetatable(data) or {}
  m.__index=function(data,k)
    m.__index=nil 
    initialize()
    return data[k]
  end
  setmetatable(data,m)
end
local keyisvalue={ __index=function(t,k)
  t[k]=k
  return k
end }
function storage.sparse(t)
  t=t or {}
  setmetatable(t,keyisvalue)
  return t
end
local function f_empty ()              return "" end 
local function f_self (t,k) t[k]=k        return k end
local function f_table (t,k) local v={} t[k]=v return v end
local function f_number(t,k) t[k]=0        return 0 end 
local function f_ignore()                   end 
local f_index={
  ["empty"]=f_empty,
  ["self"]=f_self,
  ["table"]=f_table,
  ["number"]=f_number,
}
local t_index={
  ["empty"]={ __index=f_empty },
  ["self"]={ __index=f_self  },
  ["table"]={ __index=f_table },
  ["number"]={ __index=f_number },
}
function table.setmetatableindex(t,f)
  if type(t)~="table" then
    f,t=t,{}
  end
  local m=getmetatable(t)
  if m then
    m.__index=f_index[f] or f
  else
    setmetatable(t,t_index[f] or { __index=f })
  end
  return t
end
local f_index={
  ["ignore"]=f_ignore,
}
local t_index={
  ["ignore"]={ __newindex=f_ignore },
}
function table.setmetatablenewindex(t,f)
  if type(t)~="table" then
    f,t=t,{}
  end
  local m=getmetatable(t)
  if m then
    m.__newindex=f_index[f] or f
  else
    setmetatable(t,t_index[f] or { __newindex=f })
  end
  return t
end
function table.setmetatablecall(t,f)
  if type(t)~="table" then
    f,t=t,{}
  end
  local m=getmetatable(t)
  if m then
    m.__call=f
  else
    setmetatable(t,{ __call=f })
  end
  return t
end
function table.setmetatablekey(t,key,value)
  local m=getmetatable(t)
  if not m then
    m={}
    setmetatable(t,m)
  end
  m[key]=value
  return t
end
function table.getmetatablekey(t,key,value)
  local m=getmetatable(t)
  return m and m[key]
end

end -- closure

do -- begin closure to overcome local limits and interference

if not modules then modules={} end modules ['util-prs']={
  version=1.001,
  comment="companion to luat-lib.mkiv",
  author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
  copyright="PRAGMA ADE / ConTeXt Development Team",
  license="see context related readme files"
}
local lpeg,table,string=lpeg,table,string
local P,R,V,S,C,Ct,Cs,Carg,Cc,Cg,Cf,Cp=lpeg.P,lpeg.R,lpeg.V,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cs,lpeg.Carg,lpeg.Cc,lpeg.Cg,lpeg.Cf,lpeg.Cp
local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns
local concat,gmatch,find=table.concat,string.gmatch,string.find
local tostring,type,next,rawset=tostring,type,next,rawset
local mod,div=math.mod,math.div
utilities=utilities or {}
local parsers=utilities.parsers or {}
utilities.parsers=parsers
local patterns=parsers.patterns or {}
parsers.patterns=patterns
local setmetatableindex=table.setmetatableindex
local sortedhash=table.sortedhash
local digit=R("09")
local space=P(' ')
local equal=P("=")
local comma=P(",")
local lbrace=P("{")
local rbrace=P("}")
local lparent=P("(")
local rparent=P(")")
local period=S(".")
local punctuation=S(".,:;")
local spacer=lpegpatterns.spacer
local whitespace=lpegpatterns.whitespace
local newline=lpegpatterns.newline
local anything=lpegpatterns.anything
local endofstring=lpegpatterns.endofstring
local nobrace=1-(lbrace+rbrace )
local noparent=1-(lparent+rparent)
local escape,left,right=P("\\"),P('{'),P('}')
lpegpatterns.balanced=P {
  [1]=((escape*(left+right))+(1-(left+right))+V(2))^0,
  [2]=left*V(1)*right
}
local nestedbraces=P { lbrace*(nobrace+V(1))^0*rbrace }
local nestedparents=P { lparent*(noparent+V(1))^0*rparent }
local spaces=space^0
local argument=Cs((lbrace/"")*((nobrace+nestedbraces)^0)*(rbrace/""))
local content=(1-endofstring)^0
lpegpatterns.nestedbraces=nestedbraces 
lpegpatterns.nestedparents=nestedparents 
lpegpatterns.nested=nestedbraces 
lpegpatterns.argument=argument   
lpegpatterns.content=content    
local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C((nestedbraces+(1-comma))^0)
local key=C((1-equal-comma)^1)
local pattern_a=(space+comma)^0*(key*equal*value+key*C(""))
local pattern_c=(space+comma)^0*(key*equal*value)
local key=C((1-space-equal-comma)^1)
local pattern_b=spaces*comma^0*spaces*(key*((spaces*equal*spaces*value)+C("")))
local hash={}
local function set(key,value)
  hash[key]=value
end
local pattern_a_s=(pattern_a/set)^1
local pattern_b_s=(pattern_b/set)^1
local pattern_c_s=(pattern_c/set)^1
patterns.settings_to_hash_a=pattern_a_s
patterns.settings_to_hash_b=pattern_b_s
patterns.settings_to_hash_c=pattern_c_s
function parsers.make_settings_to_hash_pattern(set,how)
  if type(str)=="table" then
    return set
  elseif how=="strict" then
    return (pattern_c/set)^1
  elseif how=="tolerant" then
    return (pattern_b/set)^1
  else
    return (pattern_a/set)^1
  end
end
function parsers.settings_to_hash(str,existing)
  if type(str)=="table" then
    if existing then
      for k,v in next,str do
        existing[k]=v
      end
      return exiting
    else
      return str
    end
  elseif str and str~="" then
    hash=existing or {}
    lpegmatch(pattern_a_s,str)
    return hash
  else
    return {}
  end
end
function parsers.settings_to_hash_tolerant(str,existing)
  if type(str)=="table" then
    if existing then
      for k,v in next,str do
        existing[k]=v
      end
      return exiting
    else
      return str
    end
  elseif str and str~="" then
    hash=existing or {}
    lpegmatch(pattern_b_s,str)
    return hash
  else
    return {}
  end
end
function parsers.settings_to_hash_strict(str,existing)
  if type(str)=="table" then
    if existing then
      for k,v in next,str do
        existing[k]=v
      end
      return exiting
    else
      return str
    end
  elseif str and str~="" then
    hash=existing or {}
    lpegmatch(pattern_c_s,str)
    return next(hash) and hash
  else
    return nil
  end
end
local separator=comma*space^0
local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C((nestedbraces+(1-comma))^0)
local pattern=spaces*Ct(value*(separator*value)^0)
patterns.settings_to_array=pattern
function parsers.settings_to_array(str,strict)
  if type(str)=="table" then
    return str
  elseif not str or str=="" then
    return {}
  elseif strict then
    if find(str,"{",1,true) then
      return lpegmatch(pattern,str)
    else
      return { str }
    end
  elseif find(str,",",1,true) then
    return lpegmatch(pattern,str)
  else
    return { str }
  end
end
local separator=space^0*comma*space^0
local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C((nestedbraces+(1-(space^0*(comma+P(-1)))))^0)
local withvalue=Carg(1)*value/function(f,s) return f(s) end
local pattern_a=spaces*Ct(value*(separator*value)^0)
local pattern_b=spaces*withvalue*(separator*withvalue)^0
function parsers.stripped_settings_to_array(str)
  if not str or str=="" then
    return {}
  else
    return lpegmatch(pattern_a,str)
  end
end
function parsers.process_stripped_settings(str,action)
  if not str or str=="" then
    return {}
  else
    return lpegmatch(pattern_b,str,1,action)
  end
end
local function set(t,v)
  t[#t+1]=v
end
local value=P(Carg(1)*value)/set
local pattern=value*(separator*value)^0*Carg(1)
function parsers.add_settings_to_array(t,str)
  return lpegmatch(pattern,str,nil,t)
end
function parsers.hash_to_string(h,separator,yes,no,strict,omit)
  if h then
    local t,tn,s={},0,table.sortedkeys(h)
    omit=omit and table.tohash(omit)
    for i=1,#s do
      local key=s[i]
      if not omit or not omit[key] then
        local value=h[key]
        if type(value)=="boolean" then
          if yes and no then
            if value then
              tn=tn+1
              t[tn]=key..'='..yes
            elseif not strict then
              tn=tn+1
              t[tn]=key..'='..no
            end
          elseif value or not strict then
            tn=tn+1
            t[tn]=key..'='..tostring(value)
          end
        else
          tn=tn+1
          t[tn]=key..'='..value
        end
      end
    end
    return concat(t,separator or ",")
  else
    return ""
  end
end
function parsers.array_to_string(a,separator)
  if a then
    return concat(a,separator or ",")
  else
    return ""
  end
end
function parsers.settings_to_set(str,t) 
  t=t or {}
  for s in gmatch(str,"[^, ]+") do 
    t[s]=true
  end
  return t
end
function parsers.simple_hash_to_string(h,separator)
  local t,tn={},0
  for k,v in sortedhash(h) do
    if v then
      tn=tn+1
      t[tn]=k
    end
  end
  return concat(t,separator or ",")
end
local str=C((1-whitespace-equal)^1)
local setting=Cf(Carg(1)*(whitespace^0*Cg(str*whitespace^0*(equal*whitespace^0*str+Cc(""))))^1,rawset)
local splitter=setting^1
function utilities.parsers.options_to_hash(str,target)
  return str and lpegmatch(splitter,str,1,target or {}) or {}
end
local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C(digit^1*lparent*(noparent+nestedparents)^1*rparent)+C((nestedbraces+(1-comma))^1)
local pattern_a=spaces*Ct(value*(separator*value)^0)
local function repeater(n,str)
  if not n then
    return str
  else
    local s=lpegmatch(pattern_a,str)
    if n==1 then
      return unpack(s)
    else
      local t,tn={},0
      for i=1,n do
        for j=1,#s do
          tn=tn+1
          t[tn]=s[j]
        end
      end
      return unpack(t)
    end
  end
end
local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+(C(digit^1)/tonumber*lparent*Cs((noparent+nestedparents)^1)*rparent)/repeater+C((nestedbraces+(1-comma))^1)
local pattern_b=spaces*Ct(value*(separator*value)^0)
function parsers.settings_to_array_with_repeat(str,expand) 
  if expand then
    return lpegmatch(pattern_b,str) or {}
  else
    return lpegmatch(pattern_a,str) or {}
  end
end
local value=lbrace*C((nobrace+nestedbraces)^0)*rbrace
local pattern=Ct((space+value)^0)
function parsers.arguments_to_table(str)
  return lpegmatch(pattern,str)
end
function parsers.getparameters(self,class,parentclass,settings)
  local sc=self[class]
  if not sc then
    sc={}
    self[class]=sc
    if parentclass then
      local sp=self[parentclass]
      if not sp then
        sp={}
        self[parentclass]=sp
      end
      setmetatableindex(sc,sp)
    end
  end
  parsers.settings_to_hash(settings,sc)
end
function parsers.listitem(str)
  return gmatch(str,"[^, ]+")
end
local pattern=Cs { "start",
  start=V("one")+V("two")+V("three"),
  rest=(Cc(",")*V("thousand"))^0*(P(".")+endofstring)*anything^0,
  thousand=digit*digit*digit,
  one=digit*V("rest"),
  two=digit*digit*V("rest"),
  three=V("thousand")*V("rest"),
}
lpegpatterns.splitthousands=pattern 
function parsers.splitthousands(str)
  return lpegmatch(pattern,str) or str
end
local optionalwhitespace=whitespace^0
lpegpatterns.words=Ct((Cs((1-punctuation-whitespace)^1)+anything)^1)
lpegpatterns.sentences=Ct((optionalwhitespace*Cs((1-period)^0*period))^1)
lpegpatterns.paragraphs=Ct((optionalwhitespace*Cs((whitespace^1*endofstring/""+1-(spacer^0*newline*newline))^1))^1)
local dquote=P('"')
local equal=P('=')
local escape=P('\\')
local separator=S(' ,')
local key=C((1-equal)^1)
local value=dquote*C((1-dquote-escape*dquote)^0)*dquote
local pattern=Cf(Ct("")*(Cg(key*equal*value)*separator^0)^1,rawset)^0*P(-1)
function parsers.keq_to_hash(str)
  if str and str~="" then
    return lpegmatch(pattern,str)
  else
    return {}
  end
end
local defaultspecification={ separator=",",quote='"' }
function parsers.csvsplitter(specification)
  specification=specification and table.setmetatableindex(specification,defaultspecification) or defaultspecification
  local separator=specification.separator
  local quotechar=specification.quote
  local separator=S(separator~="" and separator or ",")
  local whatever=C((1-separator-newline)^0)
  if quotechar and quotechar~="" then
    local quotedata=nil
    for chr in gmatch(quotechar,".") do
      local quotechar=P(chr)
      local quoteword=quotechar*C((1-quotechar)^0)*quotechar
      if quotedata then
        quotedata=quotedata+quoteword
      else
        quotedata=quoteword
      end
    end
    whatever=quotedata+whatever
  end
  local parser=Ct((Ct(whatever*(separator*whatever)^0)*S("\n\r")^1)^0 )
  return function(data)
    return lpegmatch(parser,data)
  end
end
function parsers.rfc4180splitter(specification)
  specification=specification and table.setmetatableindex(specification,defaultspecification) or defaultspecification
  local separator=specification.separator 
  local quotechar=P(specification.quote) 
  local dquotechar=quotechar*quotechar  
/specification.quote
  local separator=S(separator~="" and separator or ",")
  local escaped=quotechar*Cs((dquotechar+(1-quotechar))^0)*quotechar
  local non_escaped=C((1-quotechar-newline-separator)^1)
  local field=escaped+non_escaped+Cc("")
  local record=Ct(field*(separator*field)^1)
  local headerline=record*Cp()
  local wholeblob=Ct((newline^-1*record)^0)
  return function(data,getheader)
    if getheader then
      local header,position=lpegmatch(headerline,data)
      local data=lpegmatch(wholeblob,data,position)
      return data,header
    else
      return lpegmatch(wholeblob,data)
    end
  end
end
local function ranger(first,last,n,action)
  if not first then
  elseif last==true then
    for i=first,n or first do
      action(i)
    end
  elseif last then
    for i=first,last do
      action(i)
    end
  else
    action(first)
  end
end
local cardinal=lpegpatterns.cardinal/tonumber
local spacers=lpegpatterns.spacer^0
local endofstring=lpegpatterns.endofstring
local stepper=spacers*(C(cardinal)*(spacers*S(":-")*spacers*(C(cardinal)+Cc(true) )+Cc(false) )*Carg(1)*Carg(2)/ranger*S(", ")^0 )^1
local stepper=spacers*(C(cardinal)*(spacers*S(":-")*spacers*(C(cardinal)+(P("*")+endofstring)*Cc(true) )+Cc(false) )*Carg(1)*Carg(2)/ranger*S(", ")^0 )^1*endofstring 
function parsers.stepper(str,n,action)
  if type(n)=="function" then
    lpegmatch(stepper,str,1,false,n or print)
  else
    lpegmatch(stepper,str,1,n,action or print)
  end
end
local pattern_math=Cs((P("%")/"\\percent "+P("^")*Cc("{")*lpegpatterns.integer*Cc("}")+P(1))^0)
local pattern_text=Cs((P("%")/"\\percent "+(P("^")/"\\high")*Cc("{")*lpegpatterns.integer*Cc("}")+P(1))^0)
patterns.unittotex=pattern
function parsers.unittotex(str,textmode)
  return lpegmatch(textmode and pattern_text or pattern_math,str)
end
local pattern=Cs((P("^")/"<sup>"*lpegpatterns.integer*Cc("</sup>")+P(1))^0)
function parsers.unittoxml(str)
  return lpegmatch(pattern,str)
end
local cache={}
local spaces=lpeg.patterns.space^0
local dummy=function() end
table.setmetatableindex(cache,function(t,k)
  local separator=P(k)
  local value=(1-separator)^0
  local pattern=spaces*C(value)*separator^0*Cp()
  t[k]=pattern
  return pattern
end)
local commalistiterator=cache[","]
function utilities.parsers.iterator(str,separator)
  local n=#str
  if n==0 then
    return dummy
  else
    local pattern=separator and cache[separator] or commalistiterator
    local p=1
    return function()
      if p<=n then
        local s,e=lpegmatch(pattern,str,p)
        if e then
          p=e
          return s
        end
      end
    end
  end
end
local function initialize(t,name)
  local source=t[name]
  if source then
    local result={}
    for k,v in next,t[name] do
      result[k]=v
    end
    return result
  else
    return {}
  end
end
local function fetch(t,name)
  return t[name] or {}
end
local function process(result,more)
  for k,v in next,more do
    result[k]=v
  end
  return result
end
local name=C((1-S(", "))^1)
local parser=(Carg(1)*name/initialize)*(S(", ")^1*(Carg(1)*name/fetch))^0
local merge=Cf(parser,process)
function utilities.parsers.mergehashes(hash,list)
  return lpegmatch(merge,list,1,hash)
end
function utilities.parsers.runtime(time)
  if not time then
    time=os.runtime()
  end
  local days=div(time,24*60*60)
  time=mod(time,24*60*60)
  local hours=div(time,60*60)
  time=mod(time,60*60)
  local minutes=div(time,60)
  local seconds=mod(time,60)
  return days,hours,minutes,seconds
end

end -- closure

do -- begin closure to overcome local limits and interference

if not modules then modules={} end modules ['util-dim']={
  version=1.001,
  comment="support for dimensions",
  author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
  copyright="PRAGMA ADE / ConTeXt Development Team",
  license="see context related readme files"
}
local format,match,gsub,type,setmetatable=string.format,string.match,string.gsub,type,setmetatable
local P,S,R,Cc,C,lpegmatch=lpeg.P,lpeg.S,lpeg.R,lpeg.Cc,lpeg.C,lpeg.match
local allocate=utilities.storage.allocate
local setmetatableindex=table.setmetatableindex
local formatters=string.formatters
local texget=tex and tex.get or function() return 65536*10*100 end
number=number or {}
local number=number
number.tonumberf=function(n) return match(format("%.20f",n),"(.-0?)0*$") end 
number.tonumberg=function(n) return    format("%.20g",n)       end
local dimenfactors=allocate {
  ["pt"]=1/65536,
  ["in"]=(100/7227)/65536,
  ["cm"]=(254/7227)/65536,
  ["mm"]=(2540/7227)/65536,
  ["sp"]=1,
  ["bp"]=(7200/7227)/65536,
  ["pc"]=(1/12)/65536,
  ["dd"]=(1157/1238)/65536,
  ["cc"]=(1157/14856)/65536,
  ["nd"]=(20320/21681)/65536,
  ["nc"]=(5080/65043)/65536
}
local function numbertodimen(n,unit,fmt)
  if type(n)=='string' then
    return n
  else
    unit=unit or 'pt'
    if not fmt then
      fmt="%s%s"
    elseif fmt==true then
      fmt="%0.5f%s"
    end
    return format(fmt,n*dimenfactors[unit],unit)
  end
end
number.maxdimen=1073741823
number.todimen=numbertodimen
number.dimenfactors=dimenfactors
function number.topoints   (n,fmt) return numbertodimen(n,"pt",fmt) end
function number.toinches   (n,fmt) return numbertodimen(n,"in",fmt) end
function number.tocentimeters (n,fmt) return numbertodimen(n,"cm",fmt) end
function number.tomillimeters (n,fmt) return numbertodimen(n,"mm",fmt) end
function number.toscaledpoints(n,fmt) return numbertodimen(n,"sp",fmt) end
function number.toscaledpoints(n)   return      n.."sp"   end
function number.tobasepoints (n,fmt) return numbertodimen(n,"bp",fmt) end
function number.topicas    (n,fmt) return numbertodimen(n "pc",fmt) end
function number.todidots   (n,fmt) return numbertodimen(n,"dd",fmt) end
function number.tociceros   (n,fmt) return numbertodimen(n,"cc",fmt) end
function number.tonewdidots  (n,fmt) return numbertodimen(n,"nd",fmt) end
function number.tonewciceros (n,fmt) return numbertodimen(n,"nc",fmt) end
local amount=(S("+-")^0*R("09")^0*P(".")^0*R("09")^0)+Cc("0")
local unit=R("az")^1+P("%")
local dimenpair=amount/tonumber*(unit^1/dimenfactors+Cc(1)) 
lpeg.patterns.dimenpair=dimenpair
local splitter=amount/tonumber*C(unit^1)
function number.splitdimen(str)
  return lpegmatch(splitter,str)
end
setmetatableindex(dimenfactors,function(t,s)
  return false
end)
local stringtodimen 
local amount=S("+-")^0*R("09")^0*S(".,")^0*R("09")^0
local unit=P("pt")+P("cm")+P("mm")+P("sp")+P("bp")+P("in")+P("pc")+P("dd")+P("cc")+P("nd")+P("nc")
local validdimen=amount*unit
lpeg.patterns.validdimen=validdimen
local dimensions={}
function dimensions.__add(a,b)
  local ta,tb=type(a),type(b)
  if ta=="string" then a=stringtodimen(a) elseif ta=="table" then a=a[1] end
  if tb=="string" then b=stringtodimen(b) elseif tb=="table" then b=b[1] end
  return setmetatable({ a+b },dimensions)
end
function dimensions.__sub(a,b)
  local ta,tb=type(a),type(b)
  if ta=="string" then a=stringtodimen(a) elseif ta=="table" then a=a[1] end
  if tb=="string" then b=stringtodimen(b) elseif tb=="table" then b=b[1] end
  return setmetatable({ a-b },dimensions)
end
function dimensions.__mul(a,b)
  local ta,tb=type(a),type(b)
  if ta=="string" then a=stringtodimen(a) elseif ta=="table" then a=a[1] end
  if tb=="string" then b=stringtodimen(b) elseif tb=="table" then b=b[1] end
  return setmetatable({ a*b },dimensions)
end
function dimensions.__div(a,b)
  local ta,tb=type(a),type(b)
  if ta=="string" then a=stringtodimen(a) elseif ta=="table" then a=a[1] end
  if tb=="string" then b=stringtodimen(b) elseif tb=="table" then b=b[1] end
  return setmetatable({ a/b },dimensions)
end
function dimensions.__unm(a)
  local ta=type(a)
  if ta=="string" then a=stringtodimen(a) elseif ta=="table" then a=a[1] end
  return setmetatable({-a },dimensions)
end
function dimensions.__lt(a,b)
  return a[1]<b[1]
end
function dimensions.__eq(a,b)
  return a[1]==b[1]
end
function dimensions.__tostring(a)
  return a[1]/65536 .."pt" 
end
function dimensions.__index(tab,key)
  local d=dimenfactors[key]
  if not d then
    error("illegal property of dimen: "..key)
    d=1
  end
  return 1/d
end
  dimenfactors["ex"]=4*1/65536 
  dimenfactors["em"]=10*1/65536
local known={} setmetatable(known,{ __mode="v" })
function dimen(a)
  if a then
    local ta=type(a)
    if ta=="string" then
      local k=known[a]
      if k then
        a=k
      else
        local value,unit=lpegmatch(dimenpair,a)
        if value and unit then
          k=value/unit 
        else
          k=0
        end
        known[a]=k
        a=k
      end
    elseif ta=="table" then
      a=a[1]
    end
    return setmetatable({ a },dimensions)
  else
    return setmetatable({ 0 },dimensions)
  end
end
function string.todimen(str) 
  if type(str)=="number" then
    return str
  else
    local k=known[str]
    if not k then
      local value,unit=lpegmatch(dimenpair,str)
      if value and unit then
        k=value/unit 
      else
        k=0
      end
      known[str]=k
    end
    return k
  end
end
stringtodimen=string.todimen 
function number.toscaled(d)
  return format("%0.5f",d/2^16)
end
function number.percent(n,d) 
  d=d or texget("hsize")
  if type(d)=="string" then
    d=stringtodimen(d)
  end
  return (n/100)*d
end
number["%"]=number.percent

end -- closure

do -- begin closure to overcome local limits and interference

if not modules then modules={} end modules ['util-jsn']={
  version=1.001,
  comment="companion to m-json.mkiv",
  author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
  copyright="PRAGMA ADE / ConTeXt Development Team",
  license="see context related readme files"
}
local P,V,R,S,C,Cc,Cs,Ct,Cf,Cg=lpeg.P,lpeg.V,lpeg.R,lpeg.S,lpeg.C,lpeg.Cc,lpeg.Cs,lpeg.Ct,lpeg.Cf,lpeg.Cg
local lpegmatch=lpeg.match
local format=string.format
local utfchar=utf.char
local concat=table.concat
local tonumber,tostring,rawset,type=tonumber,tostring,rawset,type
local json=utilities.json or {}
utilities.json=json
local lbrace=P("{")
local rbrace=P("}")
local lparent=P("[")
local rparent=P("]")
local comma=P(",")
local colon=P(":")
local dquote=P('"')
local whitespace=lpeg.patterns.whitespace
local optionalws=whitespace^0
local escapes={
  ["b"]="\010",
  ["f"]="\014",
  ["n"]="\n",
  ["r"]="\r",
  ["t"]="\t",
}
local escape_un=C(P("\\u")/"0x"*S("09","AF","af"))/function(s) return utfchar(tonumber(s)) end
local escape_bs=P([[\]])/""*(P(1)/escapes) 
local jstring=dquote*Cs((escape_un+escape_bs+(1-dquote))^0)*dquote
local jtrue=P("true")*Cc(true)
local jfalse=P("false")*Cc(false)
local jnull=P("null")*Cc(nil)
local jnumber=(1-whitespace-rparent-rbrace-comma)^1/tonumber
local key=jstring
local jsonconverter={ "value",
  object=lbrace*Cf(Ct("")*V("pair")*(comma*V("pair"))^0,rawset)*rbrace,
  pair=Cg(optionalws*key*optionalws*colon*V("value")),
  array=Ct(lparent*V("value")*(comma*V("value"))^0*rparent),
  value=optionalws*(jstring+V("object")+V("array")+jtrue+jfalse+jnull+jnumber+#rparent)*optionalws,
}
function json.tolua(str)
  return lpegmatch(jsonconverter,str)
end
local function tojson(value,t) 
  local kind=type(value)
  if kind=="table" then
    local done=false
    local size=#value
    if size==0 then
      for k,v in next,value do
        if done then
          t[#t+1]=","
        else
          t[#t+1]="{"
          done=true
        end
        t[#t+1]=format("%q:",k)
        tojson(v,t)
      end
      if done then
        t[#t+1]="}"
      else
        t[#t+1]="{}"
      end
    elseif size==1 then
      t[#t+1]="["
      tojson(value[1],t)
      t[#t+1]="]"
    else
      for i=1,size do
        if done then
          t[#t+1]=","
        else
          t[#t+1]="["
          done=true
        end
        tojson(value[i],t)
      end
      t[#t+1]="]"
    end
  elseif kind=="string" then
    t[#t+1]=format("%q",value)
  elseif kind=="number" then
    t[#t+1]=value
  elseif kind=="boolean" then
    t[#t+1]=tostring(value)
  end
  return t
end
function json.tostring(value)
  local kind=type(value)
  if kind=="table" then
    return concat(tojson(value,{}),"")
  elseif kind=="string" or kind=="number" then
    return value
  else
    return tostring(value)
  end
end

end -- closure

do -- begin closure to overcome local limits and interference

if not modules then modules={} end modules ['trac-inf']={
  version=1.001,
  comment="companion to trac-inf.mkiv",
  author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
  copyright="PRAGMA ADE / ConTeXt Development Team",
  license="see context related readme files"
}
local type,tonumber,select=type,tonumber,select
local format,lower,find=string.format,string.lower,string.find
local concat=table.concat
local clock=os.gettimeofday or os.clock 
local setmetatableindex=table.setmetatableindex
local serialize=table.serialize
local formatters=string.formatters
statistics=statistics or {}
local statistics=statistics
statistics.enable=true
statistics.threshold=0.01
local statusinfo,n,registered,timers={},0,{},{}
setmetatableindex(timers,function(t,k)
  local v={ timing=0,loadtime=0 }
  t[k]=v
  return v
end)
local function hastiming(instance)
  return instance and timers[instance]
end
local function resettiming(instance)
  timers[instance or "notimer"]={ timing=0,loadtime=0 }
end
local function starttiming(instance)
  local timer=timers[instance or "notimer"]
  local it=timer.timing or 0
  if it==0 then
    timer.starttime=clock()
    if not timer.loadtime then
      timer.loadtime=0
    end
  end
  timer.timing=it+1
end
local function stoptiming(instance)
  local timer=timers[instance or "notimer"]
  local it=timer.timing
  if it>1 then
    timer.timing=it-1
  else
    local starttime=timer.starttime
    if starttime then
      local stoptime=clock()
      local loadtime=stoptime-starttime
      timer.stoptime=stoptime
      timer.loadtime=timer.loadtime+loadtime
      timer.timing=0
      return loadtime
    end
  end
  return 0
end
local function elapsed(instance)
  if type(instance)=="number" then
    return instance or 0
  else
    local timer=timers[instance or "notimer"]
    return timer and timer.loadtime or 0
  end
end
local function elapsedtime(instance)
  return format("%0.3f",elapsed(instance))
end
local function elapsedindeed(instance)
  return elapsed(instance)>statistics.threshold
end
local function elapsedseconds(instance,rest) 
  if elapsedindeed(instance) then
    return format("%0.3f seconds %s",elapsed(instance),rest or "")
  end
end
statistics.hastiming=hastiming
statistics.resettiming=resettiming
statistics.starttiming=starttiming
statistics.stoptiming=stoptiming
statistics.elapsed=elapsed
statistics.elapsedtime=elapsedtime
statistics.elapsedindeed=elapsedindeed
statistics.elapsedseconds=elapsedseconds
function statistics.register(tag,fnc)
  if statistics.enable and type(fnc)=="function" then
    local rt=registered[tag] or (#statusinfo+1)
    statusinfo[rt]={ tag,fnc }
    registered[tag]=rt
    if #tag>n then n=#tag end
  end
end
local report=logs.reporter("mkiv lua stats")
function statistics.show()
  if statistics.enable then
    local register=statistics.register
    register("used platform",function()
      return format("%s, type: %s, binary subtree: %s",
        os.platform or "unknown",os.type or "unknown",environment.texos or "unknown")
    end)
    register("luatex banner",function()
      return lower(status.banner)
    end)
    register("control sequences",function()
      return format("%s of %s + %s",status.cs_count,status.hash_size,status.hash_extra)
    end)
    register("callbacks",function()
      local total,indirect=status.callbacks or 0,status.indirect_callbacks or 0
      return format("%s direct, %s indirect, %s total",total-indirect,indirect,total)
    end)
    if jit then
      local jitstatus={ jit.status() }
      if jitstatus[1] then
        register("luajit options",concat(jitstatus," ",2))
      end
    end
    register("lua properties",function()
      local list=status.list()
      local hashchar=tonumber(list.luatex_hashchars)
      local mask=lua.mask or "ascii"
      return format("engine: %s, used memory: %s, hash type: %s, hash chars: min(%s,40), symbol mask: %s (%s)",
        jit and "luajit" or "lua",
        statistics.memused(),
        list.luatex_hashtype or "default",
        hashchar and 2^hashchar or "unknown",
        mask,
        mask=="utf" and "τεχ" or "tex")
    end)
    register("runtime",statistics.runtime)
    logs.newline() 
    for i=1,#statusinfo do
      local s=statusinfo[i]
      local r=s[2]()
      if r then
        report("%s: %s",s[1],r)
      end
    end
    statistics.enable=false
  end
end
function statistics.memused() 
  local round=math.round or math.floor
  return format("%s MB (ctx: %s MB)",round(collectgarbage("count")/1000),round(status.luastate_bytes/1000000))
end
starttiming(statistics)
function statistics.formatruntime(runtime) 
  return format("%s seconds",runtime)  
end
function statistics.runtime()
  stoptiming(statistics)
  return statistics.formatruntime(elapsedtime(statistics))
end
local report=logs.reporter("system")
function statistics.timed(action)
  starttiming("run")
  action()
  stoptiming("run")
  report("total runtime: %s seconds",elapsedtime("run"))
end
function statistics.tracefunction(base,tag,...)
  for i=1,select("#",...) do
    local name=select(i,...)
    local stat={}
    local func=base[name]
    setmetatableindex(stat,function(t,k) t[k]=0 return 0 end)
    base[name]=function(n,k,v) stat[k]=stat[k]+1 return func(n,k,v) end
    statistics.register(formatters["%s.%s"](tag,name),function() return serialize(stat,"calls") end)
  end
end
commands=commands or {}
function commands.resettimer(name)
  resettiming(name or "whatever")
  starttiming(name or "whatever")
end
function commands.elapsedtime(name)
  stoptiming(name or "whatever")
  context(elapsedtime(name or "whatever"))
end

end -- closure

do -- begin closure to overcome local limits and interference

if not modules then modules={} end modules ['util-lua']={
  version=1.001,
  comment="companion to luat-lib.mkiv",
  author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
  comment="the strip code is written by Peter Cawley",
  copyright="PRAGMA ADE / ConTeXt Development Team",
  license="see context related readme files"
}
local rep,sub,byte,dump,format=string.rep,string.sub,string.byte,string.dump,string.format
local load,loadfile,type=load,loadfile,type
utilities=utilities or {}
utilities.lua=utilities.lua or {}
local luautilities=utilities.lua
local report_lua=logs.reporter("system","lua")
local tracestripping=false
local forcestupidcompile=true 
luautilities.stripcode=true 
luautilities.alwaysstripcode=false 
luautilities.nofstrippedchunks=0
luautilities.nofstrippedbytes=0
local strippedchunks={} 
luautilities.strippedchunks=strippedchunks
luautilities.suffixes={
  tma="tma",
  tmc=jit and "tmb" or "tmc",
  lua="lua",
  luc=jit and "lub" or "luc",
  lui="lui",
  luv="luv",
  luj="luj",
  tua="tua",
  tuc="tuc",
}
local function register(name)
  if tracestripping then
    report_lua("stripped bytecode from %a",name or "unknown")
  end
  strippedchunks[#strippedchunks+1]=name
  luautilities.nofstrippedchunks=luautilities.nofstrippedchunks+1
end
local function stupidcompile(luafile,lucfile,strip)
  local code=io.loaddata(luafile)
  if code and code~="" then
    code=load(code)
    if code then
      code=dump(code,strip and luautilities.stripcode or luautilities.alwaysstripcode)
      if code and code~="" then
        register(name)
        io.savedata(lucfile,code)
        return true,0
      end
    else
      report_lua("fatal error %a in file %a",1,luafile)
    end
  else
    report_lua("fatal error %a in file %a",2,luafile)
  end
  return false,0
end
function luautilities.loadedluacode(fullname,forcestrip,name)
  name=name or fullname
  local code,message
  if environment.loadpreprocessedfile then
    code,message=environment.loadpreprocessedfile(fullname)
  else
    code,message=loadfile(fullname)
  end
  if code then
    code()
  else
    report_lua("loading of file %a failed:\n\t%s",fullname,message or "no message")
  end
  if forcestrip and luautilities.stripcode then
    if type(forcestrip)=="function" then
      forcestrip=forcestrip(fullname)
    end
    if forcestrip or luautilities.alwaysstripcode then
      register(name)
      return load(dump(code,true)),0
    else
      return code,0
    end
  elseif luautilities.alwaysstripcode then
    register(name)
    return load(dump(code,true)),0
  else
    return code,0
  end
end
function luautilities.strippedloadstring(code,forcestrip,name) 
  local code,message=load(code)
  if not code then
    report_lua("loading of file %a failed:\n\t%s",name,message or "no message")
  end
  if forcestrip and luautilities.stripcode or luautilities.alwaysstripcode then
    register(name)
    return load(dump(code,true)),0 
  else
    return code,0
  end
end
function luautilities.compile(luafile,lucfile,cleanup,strip,fallback) 
  report_lua("compiling %a into %a",luafile,lucfile)
  os.remove(lucfile)
  local done=stupidcompile(luafile,lucfile,strip~=false)
  if done then
    report_lua("dumping %a into %a stripped",luafile,lucfile)
    if cleanup==true and lfs.isfile(lucfile) and lfs.isfile(luafile) then
      report_lua("removing %a",luafile)
      os.remove(luafile)
    end
  end
  return done
end
function luautilities.loadstripped(...)
  local l=load(...)
  if l then
    return load(dump(l,true))
  end
end

end -- closure

do -- begin closure to overcome local limits and interference

if not modules then modules={} end modules ['util-deb']={
  version=1.001,
  comment="companion to luat-lib.mkiv",
  author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
  copyright="PRAGMA ADE / ConTeXt Development Team",
  license="see context related readme files"
}
local debug=require "debug"
local getinfo=debug.getinfo
local type,next,tostring=type,next,tostring
local format,find=string.format,string.find
local is_boolean=string.is_boolean
utilities=utilities or {}
local debugger=utilities.debugger or {}
utilities.debugger=debugger
local counters={}
local names={}
local report=logs.reporter("debugger")
local function hook()
  local f=getinfo(2) 
  if f then
    local n="unknown"
    if f.what=="C" then
      n=f.name or '<anonymous>'
      if not names[n] then
        names[n]=format("%42s",n)
      end
    else
      n=f.name or f.namewhat or f.what
      if not n or n=="" then
        n="?"
      end
      if not names[n] then
        names[n]=format("%42s : % 5i : %s",n,f.linedefined or 0,f.short_src or "unknown source")
      end
    end
    counters[n]=(counters[n] or 0)+1
  end
end
function debugger.showstats(printer,threshold) 
  printer=printer or report
  threshold=threshold or 0
  local total,grandtotal,functions=0,0,0
  local dataset={}
  for name,count in next,counters do
    dataset[#dataset+1]={ name,count }
  end
  table.sort(dataset,function(a,b) return a[2]==b[2] and b[1]>a[1] or a[2]>b[2] end)
  for i=1,#dataset do
    local d=dataset[i]
    local name=d[1]
    local count=d[2]
    if count>threshold and not find(name,"for generator") then 
      printer(format("%8i  %s\n",count,names[name]))
      total=total+count
    end
    grandtotal=grandtotal+count
    functions=functions+1
  end
  printer("\n")
  printer(format("functions  : % 10i\n",functions))
  printer(format("total      : % 10i\n",total))
  printer(format("grand total: % 10i\n",grandtotal))
  printer(format("threshold  : % 10i\n",threshold))
end
function debugger.savestats(filename,threshold)
  local f=io.open(filename,'w')
  if f then
    debugger.showstats(function(str) f:write(str) end,threshold)
    f:close()
  end
end
function debugger.enable()
  debug.sethook(hook,"c")
end
function debugger.disable()
  debug.sethook()
end
local function showtraceback(rep) 
  local level=2 
  local reporter=rep or report
  while true do
    local info=getinfo(level,"Sl")
    if not info then
      break
    elseif info.what=="C" then
      reporter("%2i : %s",level-1,"C function")
    else
      reporter("%2i : %s : %s",level-1,info.short_src,info.currentline)
    end
    level=level+1
  end
end
debugger.showtraceback=showtraceback

end -- closure

do -- begin closure to overcome local limits and interference

if not modules then modules={} end modules ['util-tpl']={
  version=1.001,
  comment="companion to luat-lib.mkiv",
  author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
  copyright="PRAGMA ADE / ConTeXt Development Team",
  license="see context related readme files"
}
utilities.templates=utilities.templates or {}
local templates=utilities.templates
local trace_template=false trackers.register("templates.trace",function(v) trace_template=v end)
local report_template=logs.reporter("template")
local tostring=tostring
local format,sub,byte=string.format,string.sub,string.byte
local P,C,R,Cs,Cc,Carg,lpegmatch,lpegpatterns=lpeg.P,lpeg.C,lpeg.R,lpeg.Cs,lpeg.Cc,lpeg.Carg,lpeg.match,lpeg.patterns
local replacer
local function replacekey(k,t,how,recursive)
  local v=t[k]
  if not v then
    if trace_template then
      report_template("unknown key %a",k)
    end
    return ""
  else
    v=tostring(v)
    if trace_template then
      report_template("setting key %a to value %a",k,v)
    end
    if recursive then
      return lpegmatch(replacer,v,1,t,how,recursive)
    else
      return v
    end
  end
end
local sqlescape=lpeg.replacer {
  { "'","''"  },
  { "\\","\\\\" },
  { "\r\n","\\n" },
  { "\r","\\n" },
}
local sqlquoted=lpeg.Cs(lpeg.Cc("'")*sqlescape*lpeg.Cc("'"))
lpegpatterns.sqlescape=sqlescape
lpegpatterns.sqlquoted=sqlquoted
local luaescape=lpegpatterns.luaescape
local escapers={
  lua=function(s)
    return lpegmatch(luaescape,s)
  end,
  sql=function(s)
    return lpegmatch(sqlescape,s)
  end,
}
local quotedescapers={
  lua=function(s)
    return format("%q",s)
  end,
  sql=function(s)
    return lpegmatch(sqlquoted,s)
  end,
}
local luaescaper=escapers.lua
local quotedluaescaper=quotedescapers.lua
local function replacekeyunquoted(s,t,how,recurse) 
  local escaper=how and escapers[how] or luaescaper
  return escaper(replacekey(s,t,how,recurse))
end
local function replacekeyquoted(s,t,how,recurse) 
  local escaper=how and quotedescapers[how] or quotedluaescaper
  return escaper(replacekey(s,t,how,recurse))
end
local single=P("%") 
local double=P("%%") 
local lquoted=P("%[") 
local rquoted=P("]%") 
local lquotedq=P("%(") 
local rquotedq=P(")%") 
local escape=double/'%%'
local nosingle=single/''
local nodouble=double/''
local nolquoted=lquoted/''
local norquoted=rquoted/''
local nolquotedq=lquotedq/''
local norquotedq=rquotedq/''
local key=nosingle*((C((1-nosingle )^1)*Carg(1)*Carg(2)*Carg(3))/replacekey    )*nosingle
local quoted=nolquotedq*((C((1-norquotedq)^1)*Carg(1)*Carg(2)*Carg(3))/replacekeyquoted )*norquotedq
local unquoted=nolquoted*((C((1-norquoted )^1)*Carg(1)*Carg(2)*Carg(3))/replacekeyunquoted)*norquoted
local any=P(1)
   replacer=Cs((unquoted+quoted+escape+key+any)^0)
local function replace(str,mapping,how,recurse)
  if mapping and str then
    return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str
  else
    return str
  end
end
templates.replace=replace
function templates.replacer(str,how,recurse) 
  return function(mapping)
    return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str
  end
end
function templates.load(filename,mapping,how,recurse)
  local data=io.loaddata(filename) or ""
  if mapping and next(mapping) then
    return replace(data,mapping,how,recurse)
  else
    return data
  end
end
function templates.resolve(t,mapping,how,recurse)
  if not mapping then
    mapping=t
  end
  for k,v in next,t do
    t[k]=replace(v,mapping,how,recurse)
  end
  return t
end

end -- closure

do -- begin closure to overcome local limits and interference

if not modules then modules={} end modules ['util-sta']={
  version=1.001,
  comment="companion to util-ini.mkiv",
  author="Hans Hagen, PRAGMA-ADE, Hasselt NL",
  copyright="PRAGMA ADE / ConTeXt Development Team",
  license="see context related readme files"
}
local insert,remove,fastcopy,concat=table.insert,table.remove,table.fastcopy,table.concat
local format=string.format
local select,tostring=select,tostring
local trace_stacker=false trackers.register("stacker.resolve",function(v) trace_stacker=v end)
local stacker=stacker or {}
utilities.stacker=stacker
local function start(s,t,first,last)
  if s.mode=="switch" then
    local n=tostring(t[last])
    if trace_stacker then
      s.report("start: %s",n)
    end
    return n
  else
    local r={}
    for i=first,last do
      r[#r+1]=tostring(t[i])
    end
    local n=concat(r," ")
    if trace_stacker then
      s.report("start: %s",n)
    end
    return n
  end
end
local function stop(s,t,first,last)
  if s.mode=="switch" then
    local n=tostring(false)
    if trace_stacker then
      s.report("stop: %s",n)
    end
    return n
  else
    local r={}
    for i=last,first,-1 do
      r[#r+1]=tostring(false)
    end
    local n=concat(r," ")
    if trace_stacker then
      s.report("stop: %s",n)
    end
    return n
  end
end
local function change(s,t1,first1,last1,t2,first2,last2)
  if s.mode=="switch" then
    local n=tostring(t2[last2])
    if trace_stacker then
      s.report("change: %s",n)
    end
    return n
  else
    local r={}
    for i=last1,first1,-1 do
      r[#r+1]=tostring(false)
    end
    local n=concat(r," ")
    for i=first2,last2 do
      r[#r+1]=tostring(t2[i])
    end
    if trace_stacker then
      s.report("change: %s",n)
    end
    return n
  end
end
function stacker.new(name)
  local s
  local stack={}
  local list={}
  local ids={}
  local hash={}
  local hashing=true
  local function push(...)
    for i=1,select("#",...) do
      insert(stack,(select(i,...))) 
    end
    if hashing then
      local c=concat(stack,"|")
      local n=hash[c]
      if not n then
        n=#list+1
        hash[c]=n
        list[n]=fastcopy(stack)
      end
      insert(ids,n)
      return n
    else
      local n=#list+1
      list[n]=fastcopy(stack)
      insert(ids,n)
      return n
    end
  end
  local function pop()
    remove(stack)
    remove(ids)
    return ids[#ids] or s.unset or -1
  end
  local function clean()
    if #stack==0 then
      if trace_stacker then
        s.report("%s list entries, %s stack entries",#list,#stack)
      end
    end
  end
  local tops={}
  local top,switch
  local function resolve_begin(mode)
    if mode then
      switch=mode=="switch"
    else
      switch=s.mode=="switch"
    end
    top={ switch=switch }
    insert(tops,top)
  end
  local function resolve_step(ti)
    local result=nil
    local noftop=#top
    if ti>0 then
      local current=list[ti]
      if current then
        local noflist=#current
        local nofsame=0
        if noflist>noftop then
          for i=1,noflist do
            if current[i]==top[i] then
              nofsame=i
            else
              break
            end
          end
        else
          for i=1,noflist do
            if current[i]==top[i] then
              nofsame=i
            else
              break
            end
          end
        end
        local plus=nofsame+1
        if plus<=noftop then
          if plus<=noflist then
            if switch then
              result=s.change(s,top,plus,noftop,current,nofsame,noflist)
            else
              result=s.change(s,top,plus,noftop,current,plus,noflist)
            end
          else
            if switch then
              result=s.change(s,top,plus,noftop,current,nofsame,noflist)
            else
              result=s.stop(s,top,plus,noftop)
            end
          end
        elseif plus<=noflist then
          if switch then
            result=s.start(s,current,nofsame,noflist)
          else
            result=s.start(s,current,plus,noflist)
          end
        end
        top=current
      else
        if 1<=noftop then
          result=s.stop(s,top,1,noftop)
        end
        top={}
      end
      return result
    else
      if 1<=noftop then
        result=s.stop(s,top,1,noftop)
      end
      top={}
      return result
    end
  end
  local function resolve_end()
    local noftop=#top
    if noftop>0 then
      local result=s.stop(s,top,1,#top)
      remove(tops)
      top=tops[#tops]
      switch=top and top.switch
      return result
    end
  end
  local function resolve(t)
    resolve_begin()
    for i=1,#t do
      resolve_step(t[i])
    end
    resolve_end()
  end
  local report=logs.reporter("stacker",name or nil)
  s={
    name=name or "unknown",
    unset=-1,
    report=report,
    start=start,
    stop=stop,
    change=change,
    push=push,
    pop=pop,
    clean=clean,
    resolve=resolve,
    resolve_begin=resolve_begin,
    resolve_step=resolve_step,
    resolve_end=resolve_end,
  }
  return s 
end

end -- closure
