if not modules then modules = { } end modules ['math-ext'] = {
    version   = 1.001,
    comment   = "companion to math-ini.mkiv",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

-- if needed we can use the info here to set up xetex definition files
-- the "8000 hackery influences direct characters (utf) as indirect \char's

local texsprint, format, utfchar, utfbyte = tex.sprint, string.format, utf.char, utf.byte
local setmathcode, setdelcode = tex.setmathcode, tex.setdelcode

local allocate = utilities.storage.allocate

local trace_defining = false  trackers.register("math.defining", function(v) trace_defining = v end)

local report_math = logs.reporter("mathematics","initializing")

mathematics       = mathematics or { }
local mathematics = mathematics

mathematics.extrabase   = 0xFE000 -- here we push some virtuals
mathematics.privatebase = 0xFF000 -- here we push the ex

local families = allocate {
    tf = 0, it = 1, sl = 2, bf = 3, bi = 4, bs = 5, -- no longer relevant
}

local classes = allocate {
    ord       =  0,  -- mathordcomm     mathord
    op        =  1,  -- mathopcomm      mathop
    bin       =  2,  -- mathbincomm     mathbin
    rel       =  3,  -- mathrelcomm     mathrel
    open      =  4,  -- mathopencomm    mathopen
    close     =  5,  -- mathclosecomm   mathclose
    punct     =  6,  -- mathpunctcomm   mathpunct
    alpha     =  7,  -- mathalphacomm   firstofoneargument
    accent    =  8,  -- class 0
    radical   =  9,
    xaccent   = 10,  -- class 3
    topaccent = 11,  -- class 0
    botaccent = 12,  -- class 0
    under     = 13,
    over      = 14,
    delimiter = 15,
    inner     =  0,  -- mathinnercomm   mathinner
    nothing   =  0,  -- mathnothingcomm firstofoneargument
    choice    =  0,  -- mathchoicecomm  @@mathchoicecomm
    box       =  0,  -- mathboxcomm     @@mathboxcomm
    limop     =  1,  -- mathlimopcomm   @@mathlimopcomm
    nolop     =  1,  -- mathnolopcomm   @@mathnolopcomm
}

mathematics.families = families
mathematics.classes  = classes

classes.alphabetic  = classes.alpha
classes.unknown     = classes.nothing
classes.default     = classes.nothing
classes.punctuation = classes.punct
classes.normal      = classes.nothing
classes.opening     = classes.open
classes.closing     = classes.close
classes.binary      = classes.bin
classes.relation    = classes.rel
classes.fence       = classes.unknown
classes.diacritic   = classes.accent
classes.large       = classes.op
classes.variable    = classes.alphabetic
classes.number      = classes.alphabetic

-- there will be proper functions soon (and we will move this code in-line)
-- no need for " in class and family (saves space)

local function delcode(target,family,slot)
    return format('\\Udelcode%s="%X "%X ',target,family,slot)
end
local function mathchar(class,family,slot)
    return format('\\Umathchar "%X "%X "%X ',class,family,slot)
end
local function mathaccent(class,family,slot)
    return format('\\Umathaccent "%X "%X "%X ',0,family,slot) -- no class
end
local function delimiter(class,family,slot)
    return format('\\Udelimiter "%X "%X "%X ',class,family,slot)
end
local function radical(family,slot)
    return format('\\Uradical "%X "%X ',family,slot)
end
local function mathchardef(name,class,family,slot)
    return format('\\Umathchardef\\%s "%X "%X "%X ',name,class,family,slot)
end
local function mathcode(target,class,family,slot)
    return format('\\Umathcode%s="%X "%X "%X ',target,class,family,slot)
end
local function mathtopaccent(class,family,slot)
    return format('\\Umathaccent "%X "%X "%X ',0,family,slot) -- no class
end
if tex.luatexversion > 65 then -- this will disappear at 0.70
    local function mathbotaccent(class,family,slot)
        return format('\\Umathaccent bottom "%X "%X "%X ',0,family,slot) -- no class
    end
else
    local function mathbotaccent(class,family,slot)
        return format('\\Umathbotaccent "%X "%X "%X ',0,family,slot) -- no class
    end
end
local function mathtopdelimiter(class,family,slot)
    return format('\\Udelimiterover "%X "%X ',family,slot) -- no class
end
local function mathbotdelimiter(class,family,slot)
    return format('\\Udelimiterunder "%X "%X ',family,slot) -- no class
end

local escapes = characters.filters.utf.private.escapes

local setmathcharacter, setmathsynonym, setmathsymbol -- once updated we will inline them

if setmathcode then

    setmathcharacter = function(class,family,slot,unicode,firsttime)
        if not firsttime and class <= 7 then
            setmathcode(slot,{class,family,unicode or slot})
        end
    end

    setmathsynonym = function(class,family,slot,unicode,firsttime)
        if not firsttime and class <= 7 then
            setmathcode(slot,{class,family,unicode})
        end
        if class == classes.open or class == classes.close then
            setdelcode(slot,{family,unicode,0,0})
        end
    end

    setmathsymbol = function(name,class,family,slot) -- hex is nicer for tracing
        if class == classes.accent then
            texsprint(format([[\unexpanded\gdef\%s{\Umathaccent 0 "%X "%X }]],name,family,slot))
        elseif class == classes.topaccent then
            texsprint(format([[\unexpanded\gdef\%s{\Umathaccent 0 "%X "%X }]],name,family,slot))
        elseif class == classes.botaccent then
            texsprint(format([[\unexpanded\gdef\%s{\Umathbotaccent 0 "%X "%X }]],name,family,slot))
        elseif class == classes.over then
            texsprint(format([[\unexpanded\gdef\%s{\Udelimiterover "%X "%X }]],name,family,slot))
        elseif class == classes.under then
            texsprint(format([[\unexpanded\gdef\%s{\Udelimiterunder "%X "%X }]],name,family,slot))
        elseif class == classes.open or class == classes.close then
            setdelcode(slot,{family,slot,0,0})
            texsprint(format([[\unexpanded\gdef\%s{\Udelimiter "%X "%X "%X }]],name,class,family,slot))
        elseif class == classes.delimiter then
            setdelcode(slot,{family,slot,0,0})
            texsprint(format([[\unexpanded\gdef\%s{\Udelimiter 0 "%X "%X }]],name,family,slot))
        elseif class == classes.radical then
            texsprint(format([[\unexpanded\gdef\%s{\Uradical "%X "%X }]],name,family,slot))
        else
            -- beware, open/close and other specials should not end up here
            texsprint(format([[\unexpanded\gdef\%s{\Umathchar "%X "%X "%X }]],name,class,family,slot))
        end
    end


else

    setmathcharacter = function(class,family,slot,unicode,firsttime)
        if not firsttime and class <= 7 then
            texsprint(mathcode(slot,class,family,unicode or slot))
        end
    end

    setmathsynonym = function(class,family,slot,unicode,firsttime)
        if not firsttime and class <= 7 then
            texsprint(mathcode(slot,class,family,unicode))
        end
        if class == classes.open or class == classes.close then
            texsprint(delcode(slot,family,unicode))
        end
    end

    setmathsymbol = function(name,class,family,slot)
        if class == classes.accent then
            texsprint(format("\\unexpanded\\xdef\\%s{%s}",name,mathaccent(class,family,slot)))
        elseif class == classes.topaccent then
            texsprint(format("\\unexpanded\\xdef\\%s{%s}",name,mathtopaccent(class,family,slot)))
        elseif class == classes.botaccent then
            texsprint(format("\\unexpanded\\xdef\\%s{%s}",name,mathbotaccent(class,family,slot)))
        elseif class == classes.over then
            texsprint(format("\\unexpanded\\xdef\\%s{%s}",name,mathtopdelimiter(class,family,slot)))
        elseif class == classes.under then
            texsprint(format("\\unexpanded\\xdef\\%s{%s}",name,mathbotdelimiter(class,family,slot)))
        elseif class == classes.open or class == classes.close then
            texsprint(delcode(slot,family,slot))
            texsprint(format("\\unexpanded\\xdef\\%s{%s}",name,delimiter(class,family,slot)))
        elseif class == classes.delimiter then
            texsprint(delcode(slot,family,slot))
            texsprint(format("\\unexpanded\\xdef\\%s{%s}",name,delimiter(0,family,slot)))
        elseif class == classes.radical then
            texsprint(format("\\unexpanded\\xdef\\%s{%s}",name,radical(family,slot)))
        else
            -- beware, open/close and other specials should not end up here
            texsprint(format("\\unexpanded\\xdef\\%s{%s}",name,mathchar(class,family,slot)))
        end
    end

end

local function report(class,family,unicode,name)
    local nametype = type(name)
    if nametype == "string" then
        report_math("%s:%s %s U+%05X (%s) => %s",classname,class,family,unicode,utfchar(unicode),name)
    elseif nametype == "number" then
        report_math("%s:%s %s U+%05X (%s) => U+%05X",classname,class,family,unicode,utfchar(unicode),name)
    else
        report_math("%s:%s %s U+%05X (%s)", classname,class,family,unicode,utfchar(unicode))
    end
end

-- there will be a combined \(math)chardef

function mathematics.define(family)
    family = family or 0
    family = families[family] or family
    local data = characters.data
    for unicode, character in next, data do
        local symbol = character.mathsymbol
        if symbol then
            local other = data[symbol]
            local class = other.mathclass
            if class then
                class = classes[class] or class -- no real checks needed
                if trace_defining then
                    report(class,family,unicode,symbol)
                end
                setmathsynonym(class,family,unicode,symbol)
            end
            local spec = other.mathspec
            if spec then
                for i, m in next, spec do
                    local class = m.class
                    if class then
                        class = classes[class] or class -- no real checks needed
                        setmathsynonym(class,family,unicode,symbol,i)
                    end
                end
            end
        end
        local mathclass = character.mathclass
        local mathspec = character.mathspec
        if mathspec then
            for i, m in next, mathspec do
                local name = m.name
                local class = m.class
                if not class then
                    class = mathclass
                elseif not mathclass then
                    mathclass = class
                end
                if class then
                    class = classes[class] or class -- no real checks needed
                    if name then
                        if trace_defining then
                            report(class,family,unicode,name)
                        end
                        setmathsymbol(name,class,family,unicode)
                    else
                        name = class == classes.variable or class == classes.number and character.adobename
                        if name then
                            if trace_defining then
                                report(class,family,unicode,name)
                            end
                        end
                    end
                    setmathcharacter(class,family,unicode,unicode,i)
                end
            end
        end
        if mathclass then
            local name = character.mathname
            local class = classes[mathclass] or mathclass -- no real checks needed
            if name == false then
                if trace_defining then
                    report(class,family,unicode,name)
                end
                setmathcharacter(class,family,unicode)
            else
                name = name or character.contextname
                if name then
                    if trace_defining then
                        report(class,family,unicode,name)
                    end
                    setmathsymbol(name,class,family,unicode)
                else
                    if trace_defining then
                        report(class,family,unicode,character.adobename)
                    end
                end
                setmathcharacter(class,family,unicode,unicode)
            end
        end
    end
end

-- needed for mathml analysis

function mathematics.utfmathclass(chr, default)
    local cd = characters.data[utfbyte(chr)]
    return (cd and cd.mathclass) or default or "unknown"
end
function mathematics.utfmathstretch(chr, default) -- "h", "v", "b", ""
    local cd = characters.data[utfbyte(chr)]
    return (cd and cd.mathstretch) or default or ""
end
function mathematics.utfmathcommand(chr, default)
    local cd = characters.data[utfbyte(chr)]
    local cmd = cd and cd.mathname
    return cmd or default or ""
end
function mathematics.utfmathfiller(chr, default)
    local cd = characters.data[utfbyte(chr)]
    local cmd = cd and (cd.mathfiller or cd.mathname)
    return cmd or default or ""
end

-- xml

mathematics.xml = { entities = { } }

function mathematics.xml.registerentities()
    local entities = xml.entities
    for name, unicode in next, mathematics.xml.entities do
        if not entities[name] then
            entities[name] = utfchar(unicode)
        end
    end
end

-- helpers

function mathematics.big(tfmdata,unicode,n)
    local t = tfmdata.characters
    local c = t[unicode]
    if c then
        local vv = c.vert_variants or c.next and t[c.next].vert_variants
        if vv then
            local vvn = vv[n]
            return vvn and vvn.glyph or vv[#vv].glyph or unicode
        else
            local next = c.next
            while next do
                if n <= 1 then
                    return next
                else
                    n = n - 1
                    local tn = t[next].next
                    if tn then
                        next = tn
                    else
                        return next
                    end
                end
            end
        end
    end
    return unicode
end

