# ludic.base/ecs_index.ludic - a cached query: for each value of an int column (a kind), the rows # holding it, kept current by the table's setters, so "every fire" is a list and "how many" a length export property IntIndex { col: int = 0 gate: int = -1 # an int column: a row is filed only while it is non-zero (-1: always) lists: []words = null # value -> its rows pos: words = null # row -> where it stands in its value's list, -1 not filed val: words = null # row -> the slot of the value it is filed under empty: words = null # what a value with no rows answers sparse: IntMap = null # a value past IX_DENSE (a chunk's packed cell) to its list's slot } export function tb_index(tb: Table, col: int, gate: int) -> IntIndex { let ix = new IntIndex ix.col = col ix.gate = gate ix.lists = new []words ix.pos = words(0) ix.val = words(0) ix.empty = words(0) ix.sparse = imap_new() for r in 0 .. tb.n { ix_grow(ix) } push(tb.idx, ix) for r in 0 .. tb.n { tb_ix_refile(tb, ix, r) } return ix } # the rows filed under v - the index's own list: read it, never push to it export function ix_rows(ix: IntIndex, v: int) -> words { let s = ix_slot(ix, v, false) if s < 0 { return ix.empty } return ix.lists[s] } export function ix_count(ix: IntIndex, v: int) -> int { return len(ix_rows(ix, v)) } # the first row filed under v, or -1 export function ix_first(ix: IntIndex, v: int) -> int { let rows = ix_rows(ix, v) if len(rows) == 0 { return -1 } return rows[0] } function ix_grow(ix: IntIndex) -> void { kept_push(ix.pos, -1) kept_push(ix.val, -1) } function ix_shrink(ix: IntIndex) -> void { List.pop(ix.pos) List.pop(ix.val) } function ix_file(ix: IntIndex, r: int, v: int) -> void { let s = ix_slot(ix, v, true) if ix.pos[r] >= 0 and ix.val[r] == s { return } ix_unfile(ix, r) if s < 0 { return } ix.pos[r] = len(ix.lists[s]) ix.val[r] = s kept_push(ix.lists[s], r) } # out of its list: the list's last row takes its place function ix_unfile(ix: IntIndex, r: int) -> void { let p = ix.pos[r] if p < 0 { return } let l = ix.lists[ix.val[r]] let last = List.pop(l) if last != r { l[p] = last ix.pos[last] = p } ix.pos[r] = -1 ix.val[r] = -1 } # row `from` becomes row `to` (which is not filed) function ix_moved(ix: IntIndex, from: int, to: int) -> void { let p = ix.pos[from] ix.pos[to] = p ix.val[to] = ix.val[from] if p >= 0 { ix.lists[ix.val[from]][p] = to } ix.pos[from] = -1 ix.val[from] = -1 }