new tree implementation
this about halves the time spent on level_order_it and drastically reduces the time spent in children_it
This commit is contained in:
parent
c046c6af52
commit
7f8b5196a5
284
lib/ugui.c3l/src/mtree.c3
Normal file
284
lib/ugui.c3l/src/mtree.c3
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
module mtree{Type};
|
||||||
|
|
||||||
|
import std::core::mem;
|
||||||
|
import std::core::mem::allocator;
|
||||||
|
import std::io;
|
||||||
|
import std::bits;
|
||||||
|
import std::collections::list;
|
||||||
|
|
||||||
|
|
||||||
|
alias Bitmap = ulong;
|
||||||
|
const BITS = Bitmap.sizeof*8;
|
||||||
|
|
||||||
|
alias IdxList = List{int};
|
||||||
|
|
||||||
|
// more: if positive it contains the index of the next node that contains the children information
|
||||||
|
struct Node {
|
||||||
|
int more;
|
||||||
|
int parent;
|
||||||
|
Bitmap children;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MTree {
|
||||||
|
usz elements;
|
||||||
|
Allocator allocator;
|
||||||
|
IdxList queue;
|
||||||
|
Bitmap[] used;
|
||||||
|
Type[] elem_mat; // element matrix
|
||||||
|
Node[] refs_mat; // relationship matrix
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn void MTree.init(&tree, usz size, Allocator allocator = mem)
|
||||||
|
{
|
||||||
|
// round size to the nearest multiple of BITS
|
||||||
|
size = size + size%BITS;
|
||||||
|
|
||||||
|
tree.elements = 0;
|
||||||
|
tree.allocator = allocator;
|
||||||
|
tree.queue.init(tree.allocator, size);
|
||||||
|
tree.used = allocator::new_array(tree.allocator, Bitmap, size/BITS);
|
||||||
|
tree.elem_mat = allocator::new_array(tree.allocator, Type, size);
|
||||||
|
tree.refs_mat = allocator::new_array(tree.allocator, Node, size);
|
||||||
|
|
||||||
|
foreach (&r: tree.refs_mat) {
|
||||||
|
r.more = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn void MTree.free(&tree)
|
||||||
|
{
|
||||||
|
tree.elements = 0;
|
||||||
|
tree.queue.free();
|
||||||
|
(void)allocator::free(tree.allocator, tree.used);
|
||||||
|
(void)allocator::free(tree.allocator, tree.elem_mat);
|
||||||
|
(void)allocator::free(tree.allocator, tree.refs_mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn int MTree.get_free_spot(&tree)
|
||||||
|
{
|
||||||
|
foreach (idx, d: tree.used) {
|
||||||
|
if (d != $typeof(d).max) {
|
||||||
|
int spot = (int)idx*BITS + BITS-(int)d.clz();
|
||||||
|
return spot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unreachable("no free spots left");
|
||||||
|
}
|
||||||
|
|
||||||
|
<* @require idx >= 0 *>
|
||||||
|
fn void MTree.set_used(&tree, int idx)
|
||||||
|
{
|
||||||
|
int r = idx % BITS;
|
||||||
|
int q = idx / BITS;
|
||||||
|
tree.used[q] |= (1l << r);
|
||||||
|
}
|
||||||
|
|
||||||
|
<* @require idx >= 0 *>
|
||||||
|
fn void MTree.unset_used(&tree, int idx)
|
||||||
|
{
|
||||||
|
int r = idx % BITS;
|
||||||
|
int q = idx / BITS;
|
||||||
|
tree.used[q] &= ~(1l << r);
|
||||||
|
}
|
||||||
|
|
||||||
|
<* @require idx >= 0 *>
|
||||||
|
fn bool MTree.is_used(&tree, int idx)
|
||||||
|
{
|
||||||
|
int r = idx % BITS;
|
||||||
|
int q = idx / BITS;
|
||||||
|
return !!(tree.used[q] & (1l << r));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// get the last node in the "more" chain
|
||||||
|
<* @require tree.is_used(parent) == true *>
|
||||||
|
fn int MTree.last_node(&tree, int parent)
|
||||||
|
{
|
||||||
|
while(tree.refs_mat[parent].more >= 0) {
|
||||||
|
parent = tree.refs_mat[parent].more;
|
||||||
|
}
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
<* @require tree.elements == 0 || tree.is_used(parent) == true *>
|
||||||
|
fn int MTree.add(&tree, int parent, Type t)
|
||||||
|
{
|
||||||
|
int idx = tree.get_free_spot();
|
||||||
|
int subtree = idx / BITS;
|
||||||
|
|
||||||
|
tree.set_used(idx);
|
||||||
|
tree.elem_mat[idx] = t;
|
||||||
|
tree.refs_mat[idx] = (Node){
|
||||||
|
.parent = parent,
|
||||||
|
.more = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
tree.elements++;
|
||||||
|
// root element, has no parent
|
||||||
|
if (tree.elements == 1) {
|
||||||
|
tree.refs_mat[idx].parent = -1;
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// if the parent already has a node in the same subtree as the child then update that node's
|
||||||
|
// children bitmap
|
||||||
|
bool done;
|
||||||
|
for (int p = parent; p >= 0; p = tree.refs_mat[p].more) {
|
||||||
|
int ps = p/BITS;
|
||||||
|
if (ps == subtree) {
|
||||||
|
tree.refs_mat[p].children |= (1l << (idx%BITS));
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// on fail we need to create another parent node
|
||||||
|
if (!done) {
|
||||||
|
int new_more = tree.get_free_spot();
|
||||||
|
// if the new node does not land in the same subtree as the child we cannot do
|
||||||
|
// anything since the references are immutable
|
||||||
|
if (new_more/BITS != subtree) {
|
||||||
|
unreachable("cannot allocate new child for parent");
|
||||||
|
}
|
||||||
|
tree.set_used(new_more);
|
||||||
|
tree.elements++;
|
||||||
|
// update the "more" chain
|
||||||
|
int last_link = tree.last_node(parent);
|
||||||
|
tree.refs_mat[last_link].more = new_more;
|
||||||
|
tree.refs_mat[new_more].more = -1;
|
||||||
|
tree.refs_mat[new_more].children |= (long)(1 << (idx%BITS));
|
||||||
|
tree.refs_mat[new_more].parent = last_link;
|
||||||
|
// FIXME: the elem_mat is not updated, do we need to?
|
||||||
|
}
|
||||||
|
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// get the index of the n-th children of parent, -1 otherwise
|
||||||
|
// usage: for (int i, c; (c = tree.children_it(parent, i)) >= 0; i++) { ... }
|
||||||
|
fn int MTree.children_it(&tree, int parent, int n)
|
||||||
|
{
|
||||||
|
int tot_children;
|
||||||
|
int child;
|
||||||
|
for (int p = parent; p >= 0; p = tree.refs_mat[p].more) {
|
||||||
|
int cn = (int)tree.refs_mat[p].children.popcount();
|
||||||
|
tot_children += cn;
|
||||||
|
|
||||||
|
// we are in the right subtree
|
||||||
|
if (tot_children > n) {
|
||||||
|
child = (p/BITS) * BITS; // start at the parent's subtree index
|
||||||
|
int j = cn - (tot_children - n); // we need the j-th children of this node
|
||||||
|
Bitmap u = tree.refs_mat[p].children;
|
||||||
|
|
||||||
|
child += j; // add the children number
|
||||||
|
do {
|
||||||
|
child += (int)u.ctz(); // increment by the skipped zeroes
|
||||||
|
u >>= u.ctz() + 1;
|
||||||
|
j--;
|
||||||
|
} while (j >= 0);
|
||||||
|
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int MTree.children_num(&tree, int parent)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
for (int p = parent; p >= 0; p = tree.refs_mat[p].more) {
|
||||||
|
n += (int)tree.refs_mat[p].children.popcount();
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int MTree.subtree_size(&tree, int parent)
|
||||||
|
{
|
||||||
|
int x = tree.children_num(parent);
|
||||||
|
int c;
|
||||||
|
for (int n; (c = tree.children_it(parent, n)) >= 0; n++) {
|
||||||
|
x += tree.subtree_size(c);
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fn int MTree.level_order_it(&tree, int parent, int i)
|
||||||
|
{
|
||||||
|
if (i == 0) {
|
||||||
|
tree.queue.clear();
|
||||||
|
tree.queue.push(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tree.queue.len() == 0) return -1;
|
||||||
|
|
||||||
|
int p = tree.queue.pop_first()!!;
|
||||||
|
int c;
|
||||||
|
for (int n; (c = tree.children_it(p, n)) >= 0; n++) {
|
||||||
|
tree.queue.push(c);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void MTree.prune(&tree, int parent)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
for (int i = 0; (c = tree.children_it(parent, i)) >= 0; i++) {
|
||||||
|
tree.prune(c); // prune the subtree
|
||||||
|
|
||||||
|
// delete all children including their more chain
|
||||||
|
for (int p = c; p >= 0;) {
|
||||||
|
int next = tree.refs_mat[p].more;
|
||||||
|
tree.unset_used(p);
|
||||||
|
tree.refs_mat[p] = {.more = -1};
|
||||||
|
p = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally delete the parent
|
||||||
|
for (int p = parent; p >= 0;) {
|
||||||
|
int next = tree.refs_mat[p].more;
|
||||||
|
tree.unset_used(p);
|
||||||
|
tree.elements--;
|
||||||
|
tree.refs_mat[p] = {.more = -1};
|
||||||
|
p = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
<* @require tree.is_used(ref) *>
|
||||||
|
fn Type MTree.get(&tree, int ref) => tree.elem_mat[ref];
|
||||||
|
|
||||||
|
<* @require tree.is_used(ref) *>
|
||||||
|
fn Type MTree.parentof(&tree, int ref) => tree.refs_mat[ref].parent;
|
||||||
|
|
||||||
|
fn void MTree.nuke(&tree)
|
||||||
|
{
|
||||||
|
foreach (idx, &b: tree.used) {
|
||||||
|
*b = 0;
|
||||||
|
tree.refs_mat[idx] = {.more = -1};
|
||||||
|
}
|
||||||
|
tree.elements = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
macro bool MTree.is_root(&t, int i) => t.refs_mat[i].parent == -1;
|
||||||
|
|
||||||
|
fn void MTree.print(&tree)
|
||||||
|
{
|
||||||
|
foreach (idx, c: tree.elem_mat) {
|
||||||
|
if (tree.is_used((int)idx)) {
|
||||||
|
io::printfn("[%d](%s) parent:%d more:%d children:%b",
|
||||||
|
idx, c, tree.refs_mat[idx].parent, tree.refs_mat[idx].more,
|
||||||
|
tree.refs_mat[idx].children
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
module ugui;
|
module ugui;
|
||||||
|
|
||||||
import vtree;
|
import mtree;
|
||||||
import cache;
|
import cache;
|
||||||
import fifo;
|
import fifo;
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ bitstruct ElemEvents : uint {
|
|||||||
// element structure
|
// element structure
|
||||||
struct Elem {
|
struct Elem {
|
||||||
Id id;
|
Id id;
|
||||||
isz tree_idx;
|
int tree_idx;
|
||||||
ElemFlags flags;
|
ElemFlags flags;
|
||||||
ElemEvents events;
|
ElemEvents events;
|
||||||
Rect bounds;
|
Rect bounds;
|
||||||
@ -69,7 +69,7 @@ struct Elem {
|
|||||||
|
|
||||||
|
|
||||||
// relationships between elements are stored in a tree, it stores just the ids
|
// relationships between elements are stored in a tree, it stores just the ids
|
||||||
alias IdTree = vtree::VTree{Id};
|
alias IdTree = mtree::MTree{Id};
|
||||||
|
|
||||||
// elements themselves are kept in a cache
|
// elements themselves are kept in a cache
|
||||||
const uint MAX_ELEMENTS = 256;
|
const uint MAX_ELEMENTS = 256;
|
||||||
@ -122,13 +122,13 @@ struct Ctx {
|
|||||||
Id focus_id;
|
Id focus_id;
|
||||||
|
|
||||||
Rect div_scissor; // the current div bounds used for scissor test
|
Rect div_scissor; // the current div bounds used for scissor test
|
||||||
isz active_div; // tree node indicating the current active div
|
int active_div; // tree node indicating the current active div
|
||||||
}
|
}
|
||||||
|
|
||||||
// return a pointer to the parent of the current active div
|
// return a pointer to the parent of the current active div
|
||||||
fn Elem*? Ctx.get_parent(&ctx)
|
fn Elem*? Ctx.get_parent(&ctx)
|
||||||
{
|
{
|
||||||
Id parent_id = ctx.tree.get(ctx.active_div)!;
|
Id parent_id = ctx.tree.get(ctx.active_div);
|
||||||
Elem*? parent = ctx.cache.search(parent_id);
|
Elem*? parent = ctx.cache.search(parent_id);
|
||||||
if (catch parent) return parent;
|
if (catch parent) return parent;
|
||||||
if (parent.type != ETYPE_DIV) return WRONG_ELEMENT_TYPE?;
|
if (parent.type != ETYPE_DIV) return WRONG_ELEMENT_TYPE?;
|
||||||
@ -144,7 +144,7 @@ const uint GOLDEN_RATIO = 0x9E3779B9;
|
|||||||
fn Id? Ctx.gen_id(&ctx, Id id2)
|
fn Id? Ctx.gen_id(&ctx, Id id2)
|
||||||
{
|
{
|
||||||
// FIXME: this is SHIT
|
// FIXME: this is SHIT
|
||||||
Id id1 = ctx.tree.get(ctx.active_div)!;
|
Id id1 = ctx.tree.get(ctx.active_div);
|
||||||
// Mix the two IDs non-linearly
|
// Mix the two IDs non-linearly
|
||||||
Id mixed = id1 ^ id2.rotate_left(13);
|
Id mixed = id1 ^ id2.rotate_left(13);
|
||||||
mixed ^= id1.rotate_left(7);
|
mixed ^= id1.rotate_left(7);
|
||||||
@ -178,7 +178,7 @@ fn Elem*? Ctx.get_elem(&ctx, Id id, ElemType type)
|
|||||||
} else {
|
} else {
|
||||||
elem.type = type;
|
elem.type = type;
|
||||||
}
|
}
|
||||||
elem.tree_idx = ctx.tree.add(id, ctx.active_div)!;
|
elem.tree_idx = ctx.tree.add(ctx.active_div, id);
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,13 +196,13 @@ macro Elem* Ctx.find_elem(&ctx, Id id)
|
|||||||
|
|
||||||
fn Elem*? Ctx.get_active_div(&ctx)
|
fn Elem*? Ctx.get_active_div(&ctx)
|
||||||
{
|
{
|
||||||
Id id = ctx.tree.get(ctx.active_div)!;
|
Id id = ctx.tree.get(ctx.active_div);
|
||||||
return ctx.cache.search(id);
|
return ctx.cache.search(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void? Ctx.init(&ctx, Allocator allocator)
|
fn void? Ctx.init(&ctx, Allocator allocator)
|
||||||
{
|
{
|
||||||
ctx.tree.init(MAX_ELEMENTS, allocator)!;
|
ctx.tree.init(MAX_ELEMENTS, allocator);
|
||||||
defer catch { (void)ctx.tree.free(); }
|
defer catch { (void)ctx.tree.free(); }
|
||||||
|
|
||||||
ctx.cache.init(allocator)!;
|
ctx.cache.init(allocator)!;
|
||||||
|
@ -316,18 +316,16 @@ fn void resolve_placement(Elem* c, Elem* p)
|
|||||||
|
|
||||||
fn void Ctx.layout_element_tree(&ctx)
|
fn void Ctx.layout_element_tree(&ctx)
|
||||||
{
|
{
|
||||||
isz cursor = -1;
|
int current;
|
||||||
isz current = ctx.tree.level_order_it(0, &cursor)!!;
|
for (int n; (current = ctx.tree.level_order_it(0, n)) >= 0; n++) {
|
||||||
for (; current >= 0; current = ctx.tree.level_order_it(0, &cursor)!!) {
|
Elem* p = ctx.find_elem(ctx.tree.get(current));
|
||||||
Elem* p = ctx.find_elem(ctx.tree.get(current))!!;
|
|
||||||
//if (ctx.tree.is_root(current)!!) p = &&{};
|
//if (ctx.tree.is_root(current)!!) p = &&{};
|
||||||
|
|
||||||
|
int ch;
|
||||||
// RESOLVE KNOWN DIMENSIONS
|
// RESOLVE KNOWN DIMENSIONS
|
||||||
isz ch_cur = 0;
|
for (int i; (ch = ctx.tree.children_it(current, i)) >= 0; i++) {
|
||||||
isz ch = ctx.tree.children_it(current, &ch_cur)!!;
|
Elem* c = ctx.find_elem(ctx.tree.get(ch));
|
||||||
for (; ch >= 0; ch = ctx.tree.children_it(current, &ch_cur)!!) {
|
if (ctx.tree.is_root(ch)) {
|
||||||
Elem* c = ctx.find_elem(ctx.tree.get(ch))!!;
|
|
||||||
if (ctx.tree.is_root(ch)!!) {
|
|
||||||
resolve_dimensions(p, &&{});
|
resolve_dimensions(p, &&{});
|
||||||
} else {
|
} else {
|
||||||
resolve_dimensions(c, p);
|
resolve_dimensions(c, p);
|
||||||
@ -335,11 +333,9 @@ fn void Ctx.layout_element_tree(&ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RESOLVE GROW CHILDREN
|
// RESOLVE GROW CHILDREN
|
||||||
ch_cur = 0;
|
for (int i; (ch = ctx.tree.children_it(current, i)) >= 0; i++) {
|
||||||
ch = ctx.tree.children_it(current, &ch_cur)!!;
|
Elem* c = ctx.find_elem(ctx.tree.get(ch));
|
||||||
for (; ch >= 0; ch = ctx.tree.children_it(current, &ch_cur)!!) {
|
if (ctx.tree.is_root(ch)) {
|
||||||
Elem* c = ctx.find_elem(ctx.tree.get(ch))!!;
|
|
||||||
if (ctx.tree.is_root(ch)!!) {
|
|
||||||
resolve_grow_elements(p, &&{});
|
resolve_grow_elements(p, &&{});
|
||||||
} else {
|
} else {
|
||||||
resolve_grow_elements(c, p);
|
resolve_grow_elements(c, p);
|
||||||
@ -347,11 +343,9 @@ fn void Ctx.layout_element_tree(&ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RESOLVE CHILDREN PLACEMENT
|
// RESOLVE CHILDREN PLACEMENT
|
||||||
ch_cur = 0;
|
for (int i; (ch = ctx.tree.children_it(current, i)) >= 0; i++) {
|
||||||
ch = ctx.tree.children_it(current, &ch_cur)!!;
|
Elem* c = ctx.find_elem(ctx.tree.get(ch));
|
||||||
for (; ch >= 0; ch = ctx.tree.children_it(current, &ch_cur)!!) {
|
if (ctx.tree.is_root(ch)) {
|
||||||
Elem* c = ctx.find_elem(ctx.tree.get(ch))!!;
|
|
||||||
if (ctx.tree.is_root(ch)!!) {
|
|
||||||
resolve_placement(p, &&{});
|
resolve_placement(p, &&{});
|
||||||
update_children_bounds(p, &&{});
|
update_children_bounds(p, &&{});
|
||||||
} else {
|
} else {
|
||||||
|
@ -135,7 +135,7 @@ fn Id? Ctx.div_end(&ctx)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// the active_div returns to the parent of the current one
|
// the active_div returns to the parent of the current one
|
||||||
ctx.active_div = ctx.tree.parentof(ctx.active_div)!;
|
ctx.active_div = ctx.tree.parentof(ctx.active_div);
|
||||||
Elem* parent = ctx.get_parent()!;
|
Elem* parent = ctx.get_parent()!;
|
||||||
ctx.div_scissor = parent.bounds.pad(parent.layout.content_offset);
|
ctx.div_scissor = parent.bounds.pad(parent.layout.content_offset);
|
||||||
ctx.reset_scissor(elem.div.z_index)!;
|
ctx.reset_scissor(elem.div.z_index)!;
|
||||||
|
342
test/test_mtree.c3
Normal file
342
test/test_mtree.c3
Normal file
@ -0,0 +1,342 @@
|
|||||||
|
module mtree{Type};
|
||||||
|
|
||||||
|
import std::core::mem;
|
||||||
|
import std::core::mem::allocator;
|
||||||
|
import std::io;
|
||||||
|
import std::bits;
|
||||||
|
import std::collections::list;
|
||||||
|
|
||||||
|
|
||||||
|
alias Bitmap = ulong;
|
||||||
|
const BITS = Bitmap.sizeof*8;
|
||||||
|
|
||||||
|
alias IdxList = list::List{int};
|
||||||
|
|
||||||
|
// more: if positive it contains the index of the next node that contains the children information
|
||||||
|
struct Node {
|
||||||
|
int more;
|
||||||
|
int parent;
|
||||||
|
Bitmap children;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MTree {
|
||||||
|
usz elements;
|
||||||
|
Allocator allocator;
|
||||||
|
IdxList queue;
|
||||||
|
Bitmap[] used;
|
||||||
|
Type[] elem_mat; // element matrix
|
||||||
|
Node[] refs_mat; // relationship matrix
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn void MTree.init(&tree, usz size, Allocator allocator = mem)
|
||||||
|
{
|
||||||
|
// round size to the nearest multiple of BITS
|
||||||
|
size = size + size%BITS;
|
||||||
|
|
||||||
|
tree.elements = 0;
|
||||||
|
tree.allocator = allocator;
|
||||||
|
tree.queue.init(allocator, size);
|
||||||
|
tree.used = allocator::new_array(tree.allocator, Bitmap, size/BITS);
|
||||||
|
tree.elem_mat = allocator::new_array(tree.allocator, Type, size);
|
||||||
|
tree.refs_mat = allocator::new_array(tree.allocator, Node, size);
|
||||||
|
|
||||||
|
foreach (&r: tree.refs_mat) {
|
||||||
|
r.more = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn void MTree.free(&tree)
|
||||||
|
{
|
||||||
|
tree.elements = 0;
|
||||||
|
tree.queue.free();
|
||||||
|
(void)allocator::free(tree.allocator, tree.used);
|
||||||
|
(void)allocator::free(tree.allocator, tree.elem_mat);
|
||||||
|
(void)allocator::free(tree.allocator, tree.refs_mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn int MTree.get_free_spot(&tree)
|
||||||
|
{
|
||||||
|
foreach (idx, d: tree.used) {
|
||||||
|
if (d != $typeof(d).max) {
|
||||||
|
int spot = (int)idx*BITS + BITS-(int)d.clz();
|
||||||
|
return spot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unreachable("no free spots left");
|
||||||
|
}
|
||||||
|
|
||||||
|
<* @require idx >= 0 *>
|
||||||
|
fn void MTree.set_used(&tree, int idx)
|
||||||
|
{
|
||||||
|
int r = idx % BITS;
|
||||||
|
int q = idx / BITS;
|
||||||
|
tree.used[q] |= (1l << r);
|
||||||
|
}
|
||||||
|
|
||||||
|
<* @require idx >= 0 *>
|
||||||
|
fn void MTree.unset_used(&tree, int idx)
|
||||||
|
{
|
||||||
|
int r = idx % BITS;
|
||||||
|
int q = idx / BITS;
|
||||||
|
tree.used[q] &= ~(1l << r);
|
||||||
|
}
|
||||||
|
|
||||||
|
<* @require idx >= 0 *>
|
||||||
|
fn bool MTree.is_used(&tree, int idx)
|
||||||
|
{
|
||||||
|
int r = idx % BITS;
|
||||||
|
int q = idx / BITS;
|
||||||
|
return !!(tree.used[q] & (1l << r));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// get the last node in the "more" chain
|
||||||
|
<* @require tree.is_used(parent) == true *>
|
||||||
|
fn int MTree.last_node(&tree, int parent)
|
||||||
|
{
|
||||||
|
while(tree.refs_mat[parent].more >= 0) {
|
||||||
|
parent = tree.refs_mat[parent].more;
|
||||||
|
}
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
<* @require tree.elements == 0 || tree.is_used(parent) == true *>
|
||||||
|
fn int MTree.add(&tree, int parent, Type t)
|
||||||
|
{
|
||||||
|
int idx = tree.get_free_spot();
|
||||||
|
int subtree = idx / BITS;
|
||||||
|
|
||||||
|
tree.set_used(idx);
|
||||||
|
tree.elem_mat[idx] = t;
|
||||||
|
tree.refs_mat[idx] = (Node){
|
||||||
|
.parent = parent,
|
||||||
|
.more = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
tree.elements++;
|
||||||
|
// root element, has no parent
|
||||||
|
if (tree.elements == 1) {
|
||||||
|
tree.refs_mat[idx].parent = -1;
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// if the parent already has a node in the same subtree as the child then update that node's
|
||||||
|
// children bitmap
|
||||||
|
bool done;
|
||||||
|
for (int p = parent; p >= 0; p = tree.refs_mat[p].more) {
|
||||||
|
int ps = p/BITS;
|
||||||
|
if (ps == subtree) {
|
||||||
|
tree.refs_mat[p].children |= (1l << (idx%BITS));
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// on fail we need to create another parent node
|
||||||
|
if (!done) {
|
||||||
|
int new_more = tree.get_free_spot();
|
||||||
|
// if the new node does not land in the same subtree as the child we cannot do
|
||||||
|
// anything since the references are immutable
|
||||||
|
if (new_more/BITS != subtree) {
|
||||||
|
unreachable("cannot allocate new child for parent");
|
||||||
|
}
|
||||||
|
tree.set_used(new_more);
|
||||||
|
tree.elements++;
|
||||||
|
// update the "more" chain
|
||||||
|
int last_link = tree.last_node(parent);
|
||||||
|
tree.refs_mat[last_link].more = new_more;
|
||||||
|
tree.refs_mat[new_more].more = -1;
|
||||||
|
tree.refs_mat[new_more].children |= (long)(1 << (idx%BITS));
|
||||||
|
tree.refs_mat[new_more].parent = last_link;
|
||||||
|
// FIXME: the elem_mat is not updated, do we need to?
|
||||||
|
}
|
||||||
|
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// get the index of the n-th children of parent, -1 otherwise
|
||||||
|
// usage: for (int i, c; (c = tree.children_it(parent, i)) >= 0; i++) { ... }
|
||||||
|
fn int MTree.children_it(&tree, int parent, int n)
|
||||||
|
{
|
||||||
|
int tot_children;
|
||||||
|
int child;
|
||||||
|
for (int p = parent; p >= 0; p = tree.refs_mat[p].more) {
|
||||||
|
int cn = (int)tree.refs_mat[p].children.popcount();
|
||||||
|
tot_children += cn;
|
||||||
|
|
||||||
|
// we are in the right subtree
|
||||||
|
if (tot_children > n) {
|
||||||
|
child = (p/BITS) * BITS; // start at the parent's subtree index
|
||||||
|
int j = cn - (tot_children - n); // we need the j-th children of this node
|
||||||
|
Bitmap u = tree.refs_mat[p].children;
|
||||||
|
|
||||||
|
child += j; // add the children number
|
||||||
|
do {
|
||||||
|
child += (int)u.ctz(); // increment by the skipped zeroes
|
||||||
|
u >>= u.ctz() + 1;
|
||||||
|
j--;
|
||||||
|
} while (j >= 0);
|
||||||
|
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int MTree.children_num(&tree, int parent)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
for (int p = parent; p >= 0; p = tree.refs_mat[p].more) {
|
||||||
|
n += (int)tree.refs_mat[p].children.popcount();
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int MTree.subtree_size(&tree, int parent)
|
||||||
|
{
|
||||||
|
int x = tree.children_num(parent);
|
||||||
|
int c;
|
||||||
|
for (int n; (c = tree.children_it(parent, n)) >= 0; n++) {
|
||||||
|
x += tree.subtree_size(c);
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn int MTree.level_order_it(&tree, int parent, int i)
|
||||||
|
{
|
||||||
|
if (i == 0) {
|
||||||
|
tree.queue.clear();
|
||||||
|
tree.queue.push(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tree.queue.len() == 0) return -1;
|
||||||
|
|
||||||
|
int p = tree.queue.pop_first()!!;
|
||||||
|
int c;
|
||||||
|
for (int n; (c = tree.children_it(p, n)) >= 0; n++) {
|
||||||
|
tree.queue.push(c);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void MTree.prune(&tree, int parent)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
for (int i = 0; (c = tree.children_it(parent, i)) >= 0; i++) {
|
||||||
|
tree.prune(c); // prune the subtree
|
||||||
|
|
||||||
|
// delete all children including their more chain
|
||||||
|
for (int p = c; p >= 0;) {
|
||||||
|
int next = tree.refs_mat[p].more;
|
||||||
|
tree.unset_used(p);
|
||||||
|
tree.refs_mat[p] = {.more = -1};
|
||||||
|
p = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally delete the parent
|
||||||
|
for (int p = parent; p >= 0;) {
|
||||||
|
int next = tree.refs_mat[p].more;
|
||||||
|
tree.unset_used(p);
|
||||||
|
tree.elements--;
|
||||||
|
tree.refs_mat[p] = {.more = -1};
|
||||||
|
p = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
macro bool MTree.is_root(&t, int i) => t.refs_mat[i].parent == -1;
|
||||||
|
|
||||||
|
fn void MTree.print(&tree)
|
||||||
|
{
|
||||||
|
foreach (idx, c: tree.elem_mat) {
|
||||||
|
if (tree.is_used((int)idx)) {
|
||||||
|
io::printfn("[%d](%s) parent:%d more:%d children:%b",
|
||||||
|
idx, c, tree.refs_mat[idx].parent, tree.refs_mat[idx].more,
|
||||||
|
tree.refs_mat[idx].children
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module foo;
|
||||||
|
|
||||||
|
import std::io;
|
||||||
|
import mtree;
|
||||||
|
|
||||||
|
alias Tree = mtree::MTree{int};
|
||||||
|
|
||||||
|
fn int main()
|
||||||
|
{
|
||||||
|
Tree t;
|
||||||
|
t.init(256);
|
||||||
|
defer t.free();
|
||||||
|
/*
|
||||||
|
int root = t.add(0, 0);
|
||||||
|
int c1 = t.add(root, 1);
|
||||||
|
int c2 = t.add(root, 2);
|
||||||
|
|
||||||
|
int c11 = t.add(c1, 11);
|
||||||
|
int c12 = t.add(c1, 12);
|
||||||
|
|
||||||
|
int c3 = t.add(root, 3);
|
||||||
|
|
||||||
|
for (int x = 0; x < 70; x++) {
|
||||||
|
t.add(c2, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
int c31 = t.add(c3, 31);
|
||||||
|
int c32 = t.add(c3, 32);
|
||||||
|
|
||||||
|
int c4 = t.add(root, 4);
|
||||||
|
|
||||||
|
int c13 = t.add(c1, 13);
|
||||||
|
int c14 = t.add(c1, 14);
|
||||||
|
int c15 = t.add(c1, 15);
|
||||||
|
|
||||||
|
t.prune(c2);
|
||||||
|
|
||||||
|
io::printn("printing tree");
|
||||||
|
t.print();
|
||||||
|
|
||||||
|
usz x;
|
||||||
|
foreach_r (u: t.used) {
|
||||||
|
x += u.popcount();
|
||||||
|
io::printf("%b ", u);
|
||||||
|
}
|
||||||
|
io::printfn("TOT:%d/%d",x,t.elements);
|
||||||
|
|
||||||
|
io::printn(t.subtree_size(root));
|
||||||
|
|
||||||
|
io::printn();
|
||||||
|
*/
|
||||||
|
|
||||||
|
int root = t.add(0, 0);
|
||||||
|
int c1 = t.add(root, 1);
|
||||||
|
int c2 = t.add(root, 2);
|
||||||
|
int c3 = t.add(root, 3);
|
||||||
|
|
||||||
|
int c11 = t.add(c1, 11);
|
||||||
|
int c12 = t.add(c1, 12);
|
||||||
|
|
||||||
|
int c111 = t.add(c11, 111);
|
||||||
|
int c121 = t.add(c12, 121);
|
||||||
|
|
||||||
|
int c31 = t.add(c3, 31);
|
||||||
|
|
||||||
|
int c;
|
||||||
|
for (int i; (c = t.level_order_it(root, i)) >= 0; i++) {
|
||||||
|
io::printfn("%d-th: [%d](%d)", i, c, t.elem_mat[c]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user