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

local next, type, tonumber, rawget = next, type, tonumber, rawget
local ceil, odd, round, abs = math.ceil, math.odd, math.round, math.abs
local lower = string.lower
local copy = table.copy

local trace_state   = false  trackers.register("columnsets.trace",   function(v) trace_state  = v end)
local trace_details = false  trackers.register("columnsets.details", function(v) trace_details = v end)
local trace_cells   = false  trackers.register("columnsets.cells",   function(v) trace_cells  = v end)

local report            = logs.reporter("column sets")

local setmetatableindex = table.setmetatableindex

local properties        = nodes.properties.data

local nuts              = nodes.nuts
local tonode            = nuts.tonode
local tonut             = nuts.tonut

local setlink           = nuts.setlink
local setbox            = nuts.setbox
local getwhd            = nuts.getwhd
local setwhd            = nuts.setwhd

local getlist           = nuts.getlist
local takebox           = nuts.takebox
local splitbox          = nuts.splitbox
local vpack             = nuts.vpack

local getbox            = nuts.getbox

local texgetcount       = tex.getcount
local texgetdimen       = tex.getdimen
local texsetcount       = tex.setcount
local texsetdimen       = tex.setdimen

local theprop           = nuts.theprop

local nodepool          = nuts.pool
local new_vlist         = nodepool.vlist
local new_trace_rule    = nodepool.rule
local new_empty_rule    = nodepool.emptyrule

local context           = context
local implement         = interfaces.implement

local variables         = interfaces.variables
local v_here            = variables.here
local v_fixed           = variables.fixed
local v_top             = variables.top
local v_bottom          = variables.bottom
local v_repeat          = variables["repeat"]
local v_yes             = variables.yes
local v_page            = variables.page
local v_first           = variables.first
local v_last            = variables.last
----- v_wide            = variables.wide

pagebuilders            = pagebuilders or { } -- todo: pages.builders
local columnsets        = pagebuilders.columnsets or { }

pagebuilders.columnsets = columnsets

local data = { [""] = { } }

-- todo: use state

local function setstate(t,start)
    if start or not t.firstcolumn then
        t.firstcolumn = odd(texgetcount("realpageno")) and 1 or 2
    end
    if t.firstcolumn > 1 then
        t.firstcolumn = 1
        t.lastcolumn  = t.nofleft
        t.state       = "left"
    else
        t.firstcolumn = t.nofleft + 1
        t.lastcolumn  = t.firstcolumn + t.nofright - 1
        t.state       = "right"
    end
    t.currentcolumn = t.firstcolumn
    t.currentrow    = 1
end

function columnsets.define(t)
    local name            = t.name
    local nofleft         = t.nofleft or 1
    local nofright        = t.nofright or 1
    local nofcolumns      = nofleft + nofright
    local dataset         = data[name] or { }
    data[name]            = dataset
    dataset.nofleft       = nofleft
    dataset.nofright      = nofright
    dataset.nofcolumns    = nofcolumns
    dataset.nofrows       = t.nofrows    or 1
    dataset.distance      = t.distance   or texgetdimen("bodyfontsize")
    dataset.maxwidth      = t.maxwidth   or texgetdimen("makeupwidth")
    dataset.lineheight    = t.lineheight or texgetdimen("globalbodyfontstrutheight")
    dataset.linedepth     = t.linedepth  or texgetdimen("globalbodyfontstrutdepth")
    --
    dataset.cells         = { }
    dataset.currentcolumn = 1
    dataset.currentrow    = 1
    --
    dataset.lines         = dataset.lines or setmetatableindex("table")
    dataset.start         = dataset.start or setmetatableindex("table")
    --
    dataset.page          = 1
    --
    local distances = dataset.distances or setmetatableindex(function(t,k)
        return dataset.distance
    end)
    dataset.distances = distances
    --
    local widths = dataset.widths or setmetatableindex(function(t,k)
        return dataset.width
    end)
    dataset.widths = widths
    --
    local width = t.width
    if not width or width == 0 then
        local dl = 0
        local dr = 0
        for i=1,nofleft-1 do
            dl = dl + distances[i]
        end
        for i=1,nofright-1 do
            dr = dr + distances[nofleft+i]
        end
        local nl = nofleft
        local nr = nofright
        local wl = dataset.maxwidth
        local wr = wl
        for i=1,nofleft do
            local w = rawget(widths,i)
            if w then
                nl = nl - 1
                wl = wl - w
            end
        end
        for i=1,nofright do
            local w = rawget(widths,nofleft+i)
            if w then
                nr = nr - 1
                wr = wr - w
            end
        end
        dl = (wl - dl) / nl
        dr = (wr - dr) / nr
        if dl > dr then
            report("using %s page column width %p in columnset %a","right",dr,name)
            width = dr
        elseif dl < dr then
            report("using %s page column width %p in columnset %a","left",dl,name)
            width = dl
        else
            width = dl
        end
    end
 -- report("width %p, nleft %i, nright %i",width,nofleft,nofright)
    width         = round(width)
    dataset.width = width
    local spans   = { }
    dataset.spans = spans
    for i=1,nofleft do
        local s = { }
        local d = 0
        for j=1,nofleft-i+1 do
            d = d + width
            s[j] = round(d)
            d = d + distances[j]
        end
        spans[i]  = s
    end
    for i=1,nofright do
        local s = { }
        local d = 0
        for j=1,nofright-i+1 do
            d = d + width
            s[j] = round(d)
            d = d + distances[j]
        end
        spans[nofleft+i]  = s
    end
    --
    local spreads   = copy(spans)
    dataset.spreads = spreads
    local gap       = 2 * texgetdimen("backspace")
    for l=1,nofleft do
        local s = spreads[l]
        local n = #s
        local o = s[n] + gap
        for r=1,nofright do
            n = n + 1
            s[n] = s[r] + o
        end
    end
    --
    texsetdimen("d_page_grd_column_width",dataset.width)
    --
    setstate(dataset,true)
    --
    return dataset
end

local function check(dataset)
    local cells  = dataset.cells
    local page   = dataset.page
    local offset = odd(page) and dataset.nofleft or 0
    local start  = dataset.start
    local list   = rawget(start,page)
    if list then
        for c, n in next, list do
            local column = cells[offset + c]
            if column then
                for r=1,n do
                    column[r] = true
                end
            end
        end
        start[page] = nil
    end
    local lines = dataset.lines
    local list  = rawget(lines,page)
    local rows  = dataset.nofrows
    if list then
        for c, n in next, list do
            local column = cells[offset + c]
            if column then
                if n > 0 then
                    for r=n+1,rows do
                        column[r] = true
                    end
                elseif n < 0 then
                    for r=rows,rows+n+1,-1 do
                        column[r] = true
                    end
                end
            end
        end
        lines[page] = nil
    end
end

local function erase(dataset,all)
    local cells   = dataset.cells
    local nofrows = dataset.nofrows
    local first   = 1
    local last    = dataset.nofcolumns
    --
    if not all then
        first = dataset.firstcolumn or first
        last  = dataset.lastcolumn  or last
    end
    for c=first,last do
        local column = { }
        for r=1,nofrows do
            if column[r] then
                report("slot (%i,%i) is not empty",c,r)
            end
            column[r] = false -- not used
        end
        cells[c] = column
    end
end

function columnsets.reset(t)
    local dataset = columnsets.define(t)
    erase(dataset,true)
    check(dataset)
end

function columnsets.prepareflush(name)
    local dataset     = data[name]
    local cells       = dataset.cells
    local firstcolumn = dataset.firstcolumn
    local lastcolumn  = dataset.lastcolumn
    local nofrows     = dataset.nofrows
    local lineheight  = dataset.lineheight
    local linedepth   = dataset.linedepth
    local widths      = dataset.widths
    local height      = (lineheight+linedepth)*nofrows -- - linedepth
    --
    local columns     = { }
    dataset.columns   = columns
    --
    for c=firstcolumn,lastcolumn do
        local column = cells[c]
        for r=1,nofrows do
            local cell = column[r]
            if (cell == false) or (cell == true) then
                if trace_cells then
                    column[r] = new_trace_rule(65536*2,lineheight,linedepth)
                else
                    column[r] = new_empty_rule(0,lineheight,linedepth)
                end
            end
        end
        for r=1,nofrows-1 do
            setlink(column[r],column[r+1])
        end
        columns[c] = new_vlist(column[1],widths[c],height,0) -- linedepth
    end
    --
    texsetcount("c_page_grd_first_column",firstcolumn)
    texsetcount("c_page_grd_last_column",lastcolumn)
end

function columnsets.flushcolumn(name,column)
    local dataset = data[name]
    local columns = dataset.columns
    local packed  = columns[column]
    setbox("b_page_grd_column",packed)
    columns[column] = nil
end

function columnsets.finishflush(name)
    local dataset     = data[name]
    local cells       = dataset.cells
    local firstcolumn = dataset.firstcolumn
    local lastcolumn  = dataset.lastcolumn
    local nofrows     = dataset.nofrows
    for c=firstcolumn,lastcolumn do
        local column = { }
        for r=1,nofrows do
            column[r] = false -- not used
        end
        cells[c] = column
    end
    dataset.page = dataset.page + 1
    check(dataset)
    setstate(dataset)
end

function columnsets.block(t)
    local dataset    = data[t.name]
    local cells      = dataset.cells
    local nofcolumns = dataset.nofcolumns
    local nofrows    = dataset.nofrows
    --
    local c = t.c or 0
    local r = t.r or 0
    if c == 0 or r == 0 or c > nofcolumns or r > nofrows then
        return
    end
    local nc = t.nc or 0
    local nr = t.nr or 0
    if nc == 0 then
        return
    end
    if nr == 0 then
        return
    end
    local rr = r + nr - 1
    local cc = c + nc - 1
    if rr > nofrows then
        rr = nofrows
    end
    if cc > nofcolumns then
        cc = nofcolumns
    end
    for i=c,cc do
        local column = cells[i]
        for j=r,rr do
            column[j] = true
        end
    end
end

local function here(c,r,nr,nofcolumns,nofrows,cells,width,spans)
    local rr = r + nr - 1
    if rr > nofrows then
        report("%i rows needed, %i rows available, no slots free at (%i,%i), discarding",rr,nofrows,c,r)
        return false
    end
    local cc = 0
    local wd = spans[c]
    local wc = 0
    local nc = 0
    for i=c,nofcolumns do
        nc = nc + 1
        wc = wd[nc]
        if not wc then
            break
        elseif wc >= width then
            cc = i
            break
        end
    end
    if cc == 0 or cc > nofcolumns then
        report("needed %p, no slot free at (%i,%i), discarding",width,c,r)
        return false
    end
    for i=c,cc do
        local column = cells[i]
        for j=r,rr do
            if column[j] then
                report("width %p, needed %p, checking (%i,%i) x (%i,%i), %s",width,wc,c,r,nc,nr,"quit")
                return false
            end
        end
    end
 -- report("width %p, needed %p, checking (%i,%i) x (%i,%i), %s",width,wc,c,r,nc,nr,"match")
    return c, r, nc
end

-- we use c/r as range limiters

local methods = {
    [v_here] = here,
    [v_fixed] = here,
    tblr = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
        for j=r,nofrows-nr+1 do
            for i=c,nofcolumns do
                if not cells[i][j] then
                    local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
                    if c then
                        return c, r, cc
                    end
                end
            end
        end
    end,
    lrtb = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
        for i=c,nofcolumns do
            for j=r,nofrows-nr+1 do
                if not cells[i][j] then
                    local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
                    if c then
                        return c, r, cc
                    end
                end
            end
        end
    end,
    tbrl = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
        for j=r,nofrows-nr+1 do
            for i=nofcolumns,c,-1 do
                if not cells[i][j] then
                    local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
                    if c then
                        return c, r, cc
                    end
                end
            end
        end
    end,
    rltb = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
        for i=nofcolumns,c,-1 do
            for j=r,nofrows-nr+1 do
                if not cells[i][j] then
                    local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
                    if c then
                        return c, r, cc
                    end
                end
            end
        end
    end,
    btlr = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
     -- for j=nofrows-nr+1,1,-1 do
        for j=nofrows-nr+1-r+1,1,-1 do
            for i=c,nofcolumns do
                if not cells[i][j] then
                    local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
                    if c then
                        return c, r, cc
                    end
                end
            end
        end
    end,
    lrbt = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
        for i=c,nofcolumns do
         -- for j=nofrows-nr+1,1,-1 do
            for j=nofrows-nr+1-r+1,1,-1 do
                if not cells[i][j] then
                    local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
                    if c then
                        return c, r, cc
                    end
                end
            end
        end
    end,
    btrl = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
     -- for j=nofrows-nr+1,1,-1 do
        for j=nofrows-nr+1-r+1,1,-1 do
            for i=nofcolumns,c,-1 do
                if not cells[i][j] then
                    local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
                    if c then
                        return c, r, cc
                    end
                end
            end
        end
    end,
    rlbt = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
        for i=nofcolumns,c,-1 do
         -- for j=nofrows-nr+1,1,-1 do
            for j=nofrows-nr+1-r+1,1,-1 do
                if not cells[i][j] then
                    local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
                    if c then
                        return c, r, cc
                    end
                end
            end
        end
    end,
    fxtb = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
        for i=c,nofcolumns do
            for j=r,nofrows-nr+1 do
                if not cells[i][j] then
                    local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
                    if c then
                        return c, r, cc
                    end
                end
                r = 1
            end
        end
    end,
    fxbt = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
        for i=c,nofcolumns do
            for j=nofrows-nr+1,r,-1 do
                if not cells[i][j] then
                    local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
                    if c then
                        return c, r, cc
                    end
                end
            end
            r = 1
        end
    end,
    [v_top] = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
        for i=c,nofcolumns do
            for j=1,nofrows-nr+1 do
                if cells[i][j] then
                    break
                else
                    local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
                    if c then
                        return c, r, cc
                    end
                end
            end
        end
    end,
    [v_bottom] = function(c,r,nr,nofcolumns,nofrows,cells,width,spans)
        for i=c,nofcolumns do
            for j=1,nofrows-nr+1 do
                if cells[i][j] then
                    break
                else
                    local c, r, cc = here(i,j,nr,nofcolumns,nofrows,cells,width,spans)
                    if c then
                        return c, r, cc
                    end
                end
            end
        end
    end,
}

local threshold = 50

function columnsets.check(t)
    local dataset    = data[t.name]
    local cells      = dataset.cells
    local nofcolumns = dataset.nofcolumns
    local nofrows    = dataset.nofrows
    local widths     = dataset.widths
    local lineheight = dataset.lineheight
    local linedepth  = dataset.linedepth
    local distances  = dataset.distances
    local spans      = dataset.spans
    --
    local method     = lower(t.method or "tblr")
    local boxwidth   = t.width  or 0
    local boxheight  = t.height or 0
    local boxnumber  = t.box
    local box        = boxnumber and getbox(boxnumber)
    --
    if boxwidth > 0 and boxheight > 0 then
        -- we're ok
    elseif box then
        local wd, ht, dp = getwhd(box)
        boxwidth  = wd
        boxheight = ht + dp
    else
        report("empty box")
        return
    end
    --
    local c = t.c or 0
    local r = t.r or 0
    if c == 0 then
        c = dataset.currentcolumn
    end
    if r == 0 then
        r = dataset.currentrow
    end
    if c == 0 or r == 0 or c > nofcolumns or r > nofrows then
        texsetcount("c_page_grd_reserved_state",5)
        return
    end
 -- report("checking width %p, height %p, depth %p, slot (%i,%i)",boxwidth,boxheight,boxdepth,c,r)
    local nr = ceil(boxheight/(lineheight+linedepth))
    --
    local action = methods[method]
    local cfound = false
    local rfound = false
    local lastcolumn = dataset.lastcolumn
 -- if t.option == v_wide then
 --     lastcolumn = nofcolumns
 --     spans = dataset.spreads
 -- end
    if action then
        cfound, rfound, nc = action(c,r,nr,lastcolumn,nofrows,cells,boxwidth-threshold,spans)
    end
    if not cfound and method ~= v_here then
--     if not cfound and method == v_here then
        cfound, rfound, nc = here(c,r,nr,lastcolumn,nofrows,cells,boxwidth-threshold,spans)
    end
    if cfound then
        local ht = nr*(lineheight+linedepth)
        local wd = spans[cfound][nc]
        dataset.reserved_ht = ht
        dataset.reserved_wd = wd
        dataset.reserved_c  = cfound
        dataset.reserved_r  = rfound
        dataset.reserved_nc = nc
        dataset.reserved_nr = nr
        texsetcount("c_page_grd_reserved_state",0)
        texsetdimen("d_page_grd_reserved_height",ht)
        texsetdimen("d_page_grd_reserved_width",wd)
     -- report("using (%i,%i) x (%i,%i) @ (%p,%p)",cfound,rfound,nc,nr,wd,ht)
    else
        dataset.reserved_ht = false
        dataset.reserved_wd = false
        dataset.reserved_c  = false
        dataset.reserved_r  = false
        dataset.reserved_nc = false
        dataset.reserved_nr = false
        texsetcount("c_page_grd_reserved_state",4)
     -- texsetdimen("d_page_grd_reserved_height",0)
     -- texsetdimen("d_page_grd_reserved_width",0)
     -- report("no slot found")
    end
end

function columnsets.put(t)
    local dataset    = data[t.name]
    local cells      = dataset.cells
    local widths     = dataset.widths
    local lineheight = dataset.lineheight
    local linedepth  = dataset.linedepth
    local boxnumber  = t.box
    local box        = boxnumber and takebox(boxnumber)
    --
    local c = t.c or dataset.reserved_c
    local r = t.r or dataset.reserved_r
    if not c or not r then
     -- report("no reserved slot (%i,%i)",c,r)
        return
    end
    local lastc = c + dataset.reserved_nc - 1
    local lastr = r + dataset.reserved_nr - 1
    --
    for i=c,lastc do
        local column = cells[i]
        for j=r,lastr do
            column[j] = true
        end
    end
    cells[c][r] = box
    setwhd(box,widths[c],lineheight,linedepth)
    dataset.reserved_c  = false
    dataset.reserved_r  = false
    dataset.reserved_nc = false
    dataset.reserved_nr = false
    --
end

local function findgap(dataset)
    local cells         = dataset.cells
    local nofcolumns    = dataset.nofcolumns
    local nofrows       = dataset.nofrows
    local currentrow    = dataset.currentrow
    local currentcolumn = dataset.currentcolumn
    --
    local foundc = 0
    local foundr = 0
    local foundn = 0
    for c=currentcolumn,dataset.lastcolumn do
        local column = cells[c]
        foundn = 0
        for r=currentrow,nofrows do
            if not column[r] then
                if foundc == 0 then
                    foundc = c
                    foundr = r
                end
                foundn = foundn + 1
            elseif foundn > 0 then
                return foundc, foundr, foundn
            end
        end
        if foundn > 0 then
            return foundc, foundr, foundn
        end
        currentrow = 1
    end
end

-- we can enforce grid snapping

local nofcolumngaps = 0

function columnsets.add(name,box)
    local dataset       = data[name]
    local cells         = dataset.cells
    local nofcolumns    = dataset.nofcolumns
    local nofrows       = dataset.nofrows
    local currentrow    = dataset.currentrow
    local currentcolumn = dataset.currentcolumn
    local lineheight    = dataset.lineheight
    local linedepth     = dataset.linedepth
    local widths        = dataset.widths
    --
    local b = getbox(box)
    local l = b and getlist(b)
    if l then
        local hd = lineheight + linedepth
        while l do
            local foundc, foundr, foundn = findgap(dataset)
            if foundc then
                local available = foundn * hd
                --
                if available < hd then
                    local column = cells[foundc]
                    for i=foundr,foundr+foundn-1 do
                        column[i] = true
                    end
                    -- now l needs top skip
                    -- and the rest not
                else
                    local first = splitbox(box,available,"additional") -- upto
                    if first then

                        local used = nuts.gettotal(first)
                        local foundm = ceil(used/hd)
                        if abs(foundm-foundn) == 1 then
                            first = vpack(getlist(first),available,"exactly")
                            used = nuts.gettotal(first)
                            foundm = ceil(used/hd)
                        else
                            foundn = foundm
                        end

                        local v = first
                        nofcolumngaps = nofcolumngaps + 1
                        -- getmetatable(v).columngap = nofcolumngaps
                        properties[v] = { columngap = nofcolumngaps }
                     -- report("setting gap %a at (%i,%i)",nofcolumngaps,foundc,foundr)
                        setwhd(v,widths[currentcolumn],lineheight,linedepth)
                        local column = cells[foundc]
                        --
                        column[foundr] = v
                        used = used - hd
                        if used > 0 then
                            for r=foundr+1,foundr+foundn-1 do
                                used = used - hd
                                foundr = foundr + 1
                                column[r] = true
                                if used <= 0 then
                                    break
                                end
                            end
                        end
                        currentcolumn = foundc
                        currentrow    = foundr
                        dataset.currentcolumn = currentcolumn
                        dataset.currentrow    = currentrow
                        b = getbox(box)
                        l = b and getlist(b)
                    else
                        local column = cells[foundc]
                        for i=foundr,foundr+foundn-1 do
                            column[i] = true
                        end
                        b = getbox(box)
                        l = b and getlist(b)
                    end
                end
            else
                b = getbox(box)
                l = b and getlist(b)
                break
            end
        end
    end
end

do

    -- A split approach is more efficient than a context(followup) inside
    -- followup itself as we need less (internal) housekeeping.

    local followup = nil
    local splitter = lpeg.splitter("*",tonumber)

    columnsets["noto"] = function(t)
        return followup()
    end

    columnsets["goto"] = function(name,target)
        local dataset    = data[name]
        local nofcolumns = dataset.nofcolumns
        if target == v_yes or target == "" then
            local currentcolumn = dataset.currentcolumn
            followup = function()
                context(dataset.currentcolumn == currentcolumn and 1 or 0)
            end
            return followup()
        end
        if target == v_first then
            if dataset.currentcolumn > 1  then
                target = v_page
            else
                return context(0)
            end
        end
        if target == v_page then
            if dataset.currentcolumn == 1 and dataset.currentrow == 1 then
                return context(0)
            else
                local currentpage = dataset.page
                followup = function()
                    context(dataset.page == currentpage and 1 or 0)
                end
                return followup()
            end
        end
        if target == v_last then
            target = dataset.nofcolumns
            if dataset.currentcolumn ~= target then
                followup = function()
                    context(dataset.currentcolumn ~= target and 1 or 0)
                end
                return followup()
            end
            return
        end
        local targetpage = tonumber(target)
        if targetpage then
            followup = function()
                context(dataset.currentcolumn ~= targetpage and 1 or 0)
            end
            return followup()
        end
        local targetcolumn, targetrow = lpeg.match(splitter,target)
        if targetcolumn and targetrow then
            if dataset.currentcolumn ~= targetcolumn and dataset.currentrow ~= targetrow then
                followup = function()
                    if dataset.currentcolumn ~= targetcolumn then
                        context(1)
                        return
                    end
                    if dataset.currentcolumn == targetcolumn then
                        context(dataset.currentrow ~= targetrow and 1 or 0)
                    else
                        context(0)
                    end
                end
                return followup()
            end
        end
    end

end

function columnsets.currentcolumn(name)
    local dataset = data[name]
    context(dataset.currentcolumn)
end

function columnsets.flushrest(name,box)
    local dataset = data[name]
    local rest    = dataset.rest
    if rest then
        dataset.rest = nil
        setbox("global",box,new_vlist(rest))
    end
end

function columnsets.setvsize(name)
    local dataset = data[name]
    local c, r, n = findgap(dataset)
    if n then
        dataset.currentcolumn = c
        dataset.currentrow    = r
    else
        dataset.currentcolumn = 1
        dataset.currentrow    = 1
        n = 0
    end
    local gap = n*(dataset.lineheight+dataset.linedepth)
    texsetdimen("d_page_grd_gap_height",gap)
    -- can be integrated
 -- report("state %a, n %a, column %a, row %a",dataset.state,n,dataset.currentcolumn,dataset.currentrow)
end

function columnsets.sethsize(name)
    local dataset = data[name]
    texsetdimen("d_page_grd_column_width",dataset.widths[dataset.currentcolumn])
end

function columnsets.sethspan(name,span)
    -- no checking if there is really space, so we assume it can be
    -- placed which makes spans a very explicit feature
    local dataset   = data[name]
    local column    = dataset.currentcolumn
    local available = dataset.lastcolumn - column + 1
    if span > available then
        span = available
    end
    local width = dataset.spans[column][span]
    texsetdimen("d_page_grd_span_width",width)
end

function columnsets.setlines(t)
    local dataset = data[t.name]
    dataset.lines[t.page][t.column] = t.value
end

function columnsets.setstart(t)
    local dataset = data[t.name]
    dataset.start[t.page][t.column] = t.value
end

function columnsets.setproperties(t)
    local dataset = data[t.name]
    local column  = t.column
    dataset.distances[column] = t.distance
    dataset.widths[column] = t.width
end

local areas = { }

function columnsets.registerarea(t)
    -- maybe metatable with values
    areas[#areas+1] = t
end

-- state : repeat | start

-- local ctx_page_grd_set_area = context.protected.page_grd_set_area
local ctx_page_grd_set_area = context.page_grd_set_area

function columnsets.flushareas(name)
    local nofareas = #areas
    if nofareas == 0 then
        return
    end
    local dataset = data[name]
    local page    = dataset.page
    if odd(page) then
     -- report("checking %i areas",#areas)
        local kept = { }
        for i=1,nofareas do
            local area = areas[i]
         -- local page = area.page -- maybe use page counter in columnset
         -- local type = area.type
            local okay = false
            --
            local nofcolumns = area.nc
            local nofrows    = area.nr
            local column     = area.c
            local row        = area.r
            columnsets.block {
                name = name,
                c    = column,
                r    = row,
                nc   = nofcolumns,
                nr   = nofrows,
            }
            local left     = 0
            local start    = dataset.nofleft + 1
            local overflow = (column + nofcolumns - 1) - dataset.nofleft
            local height   = nofrows * (dataset.lineheight + dataset.linedepth)
            local width    = dataset.spreads[column][nofcolumns]
         -- report("span, width %p, overflow %i",width,overflow)
            if overflow > 0 then
                local used = nofcolumns - overflow
                left  = dataset.spreads[column][used] + texgetdimen("backspace")
            end
            ctx_page_grd_set_area(name,area.name,column,row,width,height,start,left) -- or via counters / dimens
            if area.state ~= v_repeat then
                area = nil
            end
            if area then
                kept[#kept+1] = area
            end
        end
        areas = kept
    end
end

function columnsets.setarea(t)
    local dataset = data[t.name]
    local cells   = dataset.cells
    local box     = takebox(t.box)
    local column  = t.c
    local row     = t.r
    if column and row then
        setwhd(box,dataset.widths[column],dataset.lineheight,dataset.linedepth)
        cells[column][row] = box
    end
end

-- The interface.

implement {
    name      = "definecolumnset",
    actions   = columnsets.define,
    arguments = { {
        { "name", "string" },
    } }
}

implement {
    name      = "resetcolumnset",
    actions   = columnsets.reset,
    arguments = { {
        { "name", "string" },
        { "nofleft", "integer" },
        { "nofright", "integer" },
        { "nofrows", "integer" },
        { "lineheight", "dimension" },
        { "linedepth", "dimension" },
        { "width", "dimension" },
        { "distance", "dimension" },
        { "maxwidth", "dimension" },
    } }
}

implement {
    name      = "preparecolumnsetflush",
    actions   = columnsets.prepareflush,
    arguments = "string",
}

implement {
    name      = "finishcolumnsetflush",
    actions   = columnsets.finishflush,
    arguments = "string",
}

implement {
    name      = "flushcolumnsetcolumn",
    actions   = columnsets.flushcolumn,
    arguments = { "string" ,"integer" },
}

implement {
    name      = "setvsizecolumnset",
    actions   = columnsets.setvsize,
    arguments = "string",
}

implement {
    name      = "sethsizecolumnset",
    actions   = columnsets.sethsize,
    arguments = "string",
}

implement {
    name      = "sethsizecolumnspan",
    actions   = columnsets.sethspan,
    arguments = { "string" ,"integer" },
}

implement {
    name      = "flushcolumnsetrest",
    actions   = columnsets.flushrest,
    arguments = { "string", "integer" },
}

implement {
    name      = "blockcolumnset",
    actions   = columnsets.block,
    arguments = { {
        { "name", "string" },
        { "c", "integer" },
        { "r", "integer" },
        { "nc", "integer" },
        { "nr", "integer" },
        { "method", "string" },
        { "box", "integer" },
    } }
}

implement {
    name      = "checkcolumnset",
    actions   = columnsets.check,
    arguments = { {
        { "name", "string" },
        { "method", "string" },
        { "c", "integer" },
        { "r", "integer" },
        { "method", "string" },
        { "box", "integer" },
        { "width", "dimension" },
        { "height", "dimension" },
        { "option", "string" },
    } }
}

implement {
    name      = "putincolumnset",
    actions   = columnsets.put,
    arguments = { {
        { "name", "string" },
        { "c", "integer" },
        { "r", "integer" },
        { "method", "string" },
        { "box", "integer" },
    } }
}

implement {
    name    = "addtocolumnset",
    actions = columnsets.add,
    arguments = { "string", "integer" },
}

implement {
    name    = "setcolumnsetlines",
    actions = columnsets.setlines,
    arguments = { {
        { "name", "string" },
        { "page", "integer" },
        { "column", "integer" },
        { "value", "integer" },
    } }
}

implement {
    name    = "setcolumnsetstart",
    actions = columnsets.setstart,
    arguments = { {
        { "name", "string" },
        { "page", "integer" },
        { "column", "integer" },
        { "value", "integer" },
    } }
}

implement {
    name    = "setcolumnsetproperties",
    actions = columnsets.setproperties,
    arguments = { {
        { "name", "string" },
        { "column", "integer" },
        { "distance", "dimension" },
        { "width", "dimension" },
    } }
}

implement {
    name      = "registercolumnsetarea",
    actions   = columnsets.registerarea,
    arguments = { {
        { "name", "string" },
        { "type", "string" },
        { "page", "integer" },
        { "state", "string" },
        { "c", "integer" },
        { "r", "integer" },
        { "nc", "integer" },
        { "nr", "integer" },
    } }
}

implement {
    name      = "flushcolumnsetareas",
    actions   = columnsets.flushareas,
    arguments = "string",
}

implement {
    name      = "setcolumnsetarea",
    actions   = columnsets.setarea,
    arguments = { {
        { "name", "string" },
        { "c", "integer" },
        { "r", "integer" },
        { "method", "string" },
        { "box", "integer" },
    } }
}

implement {
    name      = "columnsetgoto",
    actions   = columnsets["goto"],
    arguments = "2 strings",
}

implement {
    name      = "columnsetnoto",
    actions   = columnsets["noto"],
}

implement {
    name      = "columnsetcurrentcolumn",
    actions   = columnsets.currentcolumn,
    arguments = "string",
}
