Views
DB_btree_dbix.stl
by
Paul McJones
—
last modified
2021-02-25 11:26
- History
-
Action Performed by Date and Time Comment Publish Paul McJones 2021-02-25 11:26 No comments.
"B-tree variant for database index, 2 cumulants (cumulated record size, last rec. id) , refcounts."
-- file B_tree_for_bigs_dbix.stl
package B_tree_for_dbix; -- B-trees, realized as objects
-- ***************************************************************************************************************
-- ****** B-tree variant for database index, 2 cumulants (cumulated record size, last rec. id) , refcounts ******
-- ***************************************************************************************************************
-- Refcounts can be ignored except in the dbix_set_comp, dbix_insert, split_node, share_right, join_left,
-- and join_left routines.
-- This code is closely modeled after that of package B_tree_for_wdocstring (file B_tree_for_bigs_wdoc.stl),
-- which als supports two cumulants, one a cumulating integer, the other a 4-byte field representing a type
-- of 'record key' which appears in order. The difference is only that in this case the non-compound nodes
-- have no children, but directly contain the data (record keys and lengths) that the leaves of trees
-- of this type store. For code compatibility with 'B_tree_for_wdocstring' we represent this data
-- in cumulated form, so at the non-compound level we store cumulated sums of lengths rather than
-- raw lengths, recovering the raw lengths by subtraction when they are wanted. This confines most of
-- the code changes to the bottom-level access routines dbix_get_ch_cum, dbix_set_ch_cum, dbix_dbix_get_cum2,
-- dbix_get_ch_cum2, dbix_set_ch_cum2, dbix_vect_of_cums, dbix_set_vect_of_cums, and dbix_set_vect_of_cums2.
-- In non-compound nodes we use the 'number of children' byte to represent the number of data items present;
-- there are no real children. Since space is not needed for children, these can store up to 14 cumulants.
-- this B-tree represents the record-id-to-length of record index
const dbix_code_pts := {"dbix_cc_end", "dbix_srch_on2", "dbix_srch_nc1", "dbix_srch_nc2",
"dbix_set_copy", "dbix_set_last",
"dbix_set_nodnc", "dbix_set_comp", "dbix_set_final", "dbix_set_notfinal", "dbix_delnc",
"dbix_delnorem", "dbix_delrem", "dbix_delcomp", "dbix_delcomplast", "dbix_delcomplast",
"dbix_delnojs", "dbix_canpull", "dbix_canjoin", "dbix_delnonly", "dbix_delonly",
"dbix_in_copy", "dbix_in_nend", "dbix_in_srch2", "dbix_in_srch1",
"dbix_in_srch_notend", "dbix_in_srch_end", "dbix_in_srch_ncend",
"dbix_in_srch_ncend_nos", "dbix_in_srch_ncend_split", "dbix_in_srch_compend_nos",
"dbix_in_srch_compend_split", "dbix_in_srch_compend_nosrec",
"dbix_in_srch_compend_splrec", "dbix_inin_nc", "dbix_inin_ncnos",
"dbix_inin_nctopspl", "dbix_inin_compnos", "dbix_inin_compsplit",
"dbix_inin_compnosthis", "dbix_inin_compsplitthis", "dbix_halves_nc",
"dbix_halves_comp", "dbix_canpull_left", "dbix_canpull_right",
"dbix_share_nc", "dbix_share_comp", "dbix_share_copy", "dbix_share_copy2",
"dbix_share_move_left", "dbix_share_move_left_comp", "dbix_share_move_left_nc",
"dbix_share_move_right_comp", "dbix_share_move_right_nc", "dbix_join_left_comp",
"dbix_join_left_nc", "dbix_join_left_copy", "dbix_join_right_comp",
"dbix_join_right_nc", "dbix_join_right_copy"}; -- code points to be traversed
var debug_flag := false;
var prior_debug_c := 0,debug_c := 0; -- global variables for debugging
procedure hdbix_create(); -- create a new B_tree_for_dbix node, as a non_compound node
procedure hdbix_comp(rec,j); -- fetch of component containing cumulant j
procedure hdbix_comp2(rec,j); -- fetch of component containing second cumulant j
procedure hdbix_comp_cum(rec,x); -- fetch of component containing cumulant x, with cumulants
procedure hdbix_comp_cum2(rec,x); -- fetch of component containing second cumulant x, with cumulants
procedure hdbix_set_comp(rw rec,w,x); -- assignment of the first component whose cumulant is at least w; w must be in range
procedure hdbix_set_comp2(rw rec,w,x); -- assignment of the first component whose second cumulant is at least w
procedure hdbix_insert(rw rec,j,x); -- insertion before j-th component; or at the end if j = OM
procedure hdbix_insert2(rw rec,j,x); -- insertion before component with second cumulant at least j; or at end (j = OM)
procedure hdbix_get_cum(rec); -- get the cum value (string length) for this node
procedure hdbix_get_cum2(rec); -- get the record id string cum value of the final child of this node
procedure dbix_make_from_tuple(t); -- make B-tree representation from tuple (DEBUGGING ONLY)
procedure dbix_dump(rec); -- get tuple from B-tree representation (DEBUGGING ONLY)
procedure shexify(stg); -- put into abbreviated hex (DEBUGGING ONLY)
procedure num_leaves(a_tree); -- (DEBUGGING ONLY)
procedure dbix_check_tree_structure(tree); -- recursive check of tree structure (DEBUGGING ONLY)
end B_tree_for_dbix;
package body B_tree_for_dbix; -- B-trees, realized as objects
use setldb,byteutil,disk_records_pak,db_records,string_utility_pak;
-- there are two cumulants, the total length of the records and the recid
procedure hdbix_create(); -- creation routine
rec := dr_new_rec(); -- create a new record (at this point, the record is loaded
-- dirty, and holds all zeroes; so it is not compound)
set_type(rec,db_index_node_ncr); -- set to non-compound tree
return rec;
end hdbix_create;
procedure hdbix_get_cum(rec); -- get the integer cum value of the final child of this node
if (nc := num_childr(rec)) = 0 then return 0; end if;
return get_ch_cum(rec,nc);
end hdbix_get_cum;
procedure get_ch_cum(rec,j); -- get the integer cum value for the j-th child of this node
the_start := if dr_is_compound(rec) then dbix_cum_start else dbnc_cum_start end if;
return int_of_5(dr_load(rec)(the_start + (j - 1) * 5..the_start + j * 5 - 1));
end get_ch_cum;
procedure set_ch_cum(rec,j,cum_int); -- set the integer cum value for the j-th child of this node
the_start := if dr_is_compound(rec) then dbix_cum_start else dbnc_cum_start end if;
stg := dr_load(rec); -- make sure that this record is loaded
stg(the_start + (j - 1) * 5..the_start + j * 5 - 1) := stg_of_5(cum_int);
dr_setrecbuf(rec,stg);
dr_dirtify(rec);
end set_ch_cum;
procedure hdbix_get_cum2(rec); -- get the record id string cum value of the final child of this node
if (nc := num_childr(rec)) = 0 then return OM; end if;
return get_ch_cum2(rec,num_childr(rec));
end hdbix_get_cum2;
procedure get_ch_cum2(rec,j); -- get the record id string cum value for the j-th child of this node
the_start := if dr_is_compound(rec) then dbix_cum2_start else dbnc_cum2_start end if;
return dr_load(rec)(the_start + (j - 1) * 4..the_start + j * 4 - 1);
end get_ch_cum2;
procedure set_ch_cum2(rec,j,new_rec_id); -- set the record id string cum value for the j-th child of this node
the_start := if dr_is_compound(rec) then dbix_cum2_start else dbnc_cum2_start end if;
stg := dr_load(rec); -- make sure that this record is loaded
stg(the_start + (j - 1) * 4..the_start + j * 4 - 1) := new_rec_id;
dr_setrecbuf(rec,stg);
dr_dirtify(rec);
end set_ch_cum2;
procedure vect_of_children(rec); -- gets vector of children, as a string of 4-byte record numbers
if not dr_is_compound(rec) then return ""; end if;
nch := num_childr(rec); -- number of children
return dr_load(rec)(dbix_ch_start..dbix_ch_start - 1 + nch * 4);
end vect_of_children;
procedure set_vect_of_children(rec,stg); -- sets vector of children, from a string of 4-byte record numbers
--print("set_vect_of_children: ",hexify(rec)," ",hexify(stg));
if not dr_is_compound(rec) then print("Illegal effort to set vector of children for non_compound node"); stop; end if;
set_num_childr(rec,(nstg := #stg)/4); -- set the number of children (also loads)
missing := dbix_cum_start - dbix_ch_start - #stg;
--if missing < 0 then print("missing,dbix_cum_start,dbix_ch_start,#stg: ",missing," ",#stg," ",hexify(stg)); stop; end if;
stg2:=dr_load(rec);
stg2(dbix_ch_start..dbix_cum_start - 1) := (stg + missing * "\x00");
-- set the children, remembering not to change the length of the string section containing them
dr_setrecbuf(rec,stg2);
dr_dirtify(rec);
end set_vect_of_children;
procedure vect_of_cums(rec); -- gets vector of cums, as a string of 5-byte fields
the_start := if dr_is_compound(rec) then dbix_cum_start else dbnc_cum_start end if;
nch := num_childr(rec); -- number of children
return dr_load(rec)(the_start..the_start - 1 + nch * 5);
end vect_of_cums;
procedure set_vect_of_cums(rec,stg); -- sets vector of cumulants, from a string of 5-byte record numbers
the_start := if (ic := dr_is_compound(rec)) then dbix_cum_start else dbnc_cum_start end if;
the_end := if ic then dbix_cum2_start else dbnc_cum2_start end if;
missing := the_end - the_start - #stg;
stg2:=dr_load(rec); -- force load
stg2(the_start..the_end - 1) := (stg + missing * "\x00");
-- set the cumulants, remembering not to change the length of the string section containing them
dr_setrecbuf(rec,stg2);
dr_dirtify(rec);
end set_vect_of_cums;
procedure vect_of_cums2(rec); -- gets vector of cums, as a string of 4-byte fields
the_start := if dr_is_compound(rec) then dbix_cum2_start else dbnc_cum2_start end if;
nch := num_childr(rec); -- number of children
return dr_load(rec)(the_start..the_start - 1 + nch * 4);
end vect_of_cums2;
procedure set_vect_of_cums2(rec,stg); -- sets vector of children, from a string of 4-byte record numbers
the_start := if (ic := dr_is_compound(rec)) then dbix_cum2_start else dbnc_cum2_start end if;
the_end := if ic then dbix_cum2_end else dbnc_cum2_end end if;
missing := the_end - the_start + 1 - #stg;
stg2:=dr_load(rec); -- force load
stg2(the_start..the_end) := (stg + missing * "\x00");
-- set the children, remembering not to change the length of the string section containing them
dr_setrecbuf(rec,stg2);
dr_dirtify(rec);
end set_vect_of_cums2;
procedure num_leaves(a_tree); -- debugging only
return if not dr_is_compound(a_tree) then num_childr(a_tree)
else 0 +/[num_leaves(voc(a_tree,j)): j in [1..num_childr(a_tree)]] end if;
end num_leaves;
procedure voc(rec,j); -- n'th member of vector of children
stg := dr_load(rec); -- load this string
if not dr_is_compound(rec) then print("Illegal effort to access vector of children for non_compound node"); stop; end if;
cjstrt := (j - 1) * 4 + dbix_ch_start;
return stg(cjstrt..cjstrt + 3); -- return child rec
end voc;
procedure set_voc(rec,j,chrec); -- set n'th member of vector of children
cjstrt := (j - 1) * 4 + dbix_ch_start;
stg2:=dr_load(rec); -- force load; then set the first character of the record
if not dr_is_compound(rec) then print("Illegal effort to access element of vector of children for non_compound node"); stop; end if;
stg2(cjstrt..cjstrt + 3) := chrec;
dr_setrecbuf(rec,stg2);
dr_dirtify(rec);
end set_voc;
procedure dbix_make_from_tuple(t); -- make nested representation from tuple
--print("make_from_tuple: ",t);
if #t <= dbnc_hi_lim then -- put into just one section
stg := dr_load(rec := dbix_create());
[icums,rids] := convert_tup_sect(t); -- convert to cumulant string form
set_vect_of_cums(rec,icums); set_vect_of_cums2(rec,rids);
set_num_childr(rec,#t); -- set the nominal number of children
return rec;
end if;
last_used := 0; -- the last component of t already incorporated into a tuple
tx := [ ];
for j in [1,dbnc_hi_lim + 1..#t - 2 * dbnc_hi_lim] loop
-- put the data into a list of non_compound B_tree nodes, saving enough for at least
-- one full final section
rec := dbix_create();
[icums,rids] := convert_tup_sect(t(j..last_used := j + dbnc_hi_lim - 1));
-- convert one section of input to cumulant string form
set_vect_of_cums(rec,icums); set_vect_of_cums2(rec,rids);
set_num_childr(rec,dbnc_hi_lim); -- set the nominal number of children
tx with:= rec;
end loop;
unusedo2 := (#t - last_used)/2;
t1 := t(last_used + 1..last_used + unusedo2); t2 := t(last_used + unusedo2 + 1..);
rec := dbix_create(); [icums,rids] := convert_tup_sect(t1);
set_num_childr(rec,unusedo2); -- set the nominal number of children
set_vect_of_cums(rec,icums); set_vect_of_cums2(rec,rids); tx with:= rec;
rec := dbix_create(); [icums,rids] := convert_tup_sect(t2);
set_vect_of_cums(rec,icums); set_vect_of_cums2(rec,rids); tx with:= rec;
set_num_childr(rec,#rids/4); -- set the nominal number of children
t := tx; -- use the converted t
compound_now := false; -- otherwise we must chop into sections; bottom level sections are not compound
while (nt := #t) > dbix_hi_lim loop
-- build tree progressively from bottom, continuing as long as list of nodes obtained is too long
sections := [t(j..j + dbix_hi_lim - 1): j in [1,dbix_hi_lim + 1..nt - dbix_hi_lim + 1]]; -- these are lists of nodes
if (nt mod dbix_hi_lim) /= 0 then -- may have to rearrange last 2 pieces
if #(piece := t((nrv := #sections) * dbix_hi_lim + 1..)) >= dbix_low_lim then
sections with:= piece;
else -- otherwise rearrange last 2 pieces
ntp := #(tdbix_pieces := sections(nrv) + piece);
sections(nrv) := tdbix_pieces(1..ntpo2 := ntp/2);
sections with:= tdbix_pieces(ntpo2 + 1..);
end if;
end if;
t := [ ]; -- recalculate the vector of items to work with
for sec = sections(j) loop -- convert each section into a tree node
sec_node := dr_new_rec(); -- make a tree top for this tuple (i.e. this section)
set_type(sec_node,db_index_node_record);
-- set the type of the record
sec_stg := "" +/ sec; -- convert the list of children to a string
set_vect_of_children(sec_node,sec_stg); -- set the list of children
cumulate(sec_node); -- initialize the cumulants
t with:= sec_node; -- assemble new vector of nodes
--print("sec_node: ",sec_node," ",hexify(dr_load(sec_node)));
end loop;
compound_now := true; -- after first iteration, sections are not compound
end loop;
rec := dr_new_rec(); -- create a new, top level record
t_stg := "" +/t; -- convert the list of children to a string
set_type(rec,db_index_node_record); -- at this point must be compound
set_vect_of_children(rec,t_stg); -- these become the top-level children
cumulate(rec); -- initialize the final cumulants
return rec;
end dbix_make_from_tuple;
procedure convert_tup_sect(t); -- converts a tuple of pairs [rid,ridlen] into a pair of strings
cum := 0; -- integer cumulant, developed below
rid_stg := "" +/ [stg_of_4(rid): [rid,ridlen] in t];
len_stg := "" +/ [stg_of_5(cum): [rid,ridlen] in t | (cum := cum + ridlen) >= 0];
return [len_stg,rid_stg];
end convert_tup_sect;
procedure dbix_dump(rec); -- get tuple from nested representation
var indent := 0;
return dbix_dump_in(rec); -- call inner workhorse
procedure dbix_dump_in(rec); -- inner workhorse
-- is compound representation or direct, depending on whether 'dr_is_compound' flag is set
-- note that this shows the vector of components, without their cumulants
-- we ignore the cumulants in the tree nodes
--print("dbix_dump: ",hexify(dr_load(rec)));
if dr_is_compound(rec) then -- compound case
indent +:= 1; nc := num_childr(rec);
stg := (["\n" + (indent * " ") + "("] +/ [dbix_dump_in(voc(rec,j)): j in [1..nc]])
+ ["\n" + (indent * " ")+ "[" + str(nc) + "]" + shexify(dbix_get_cum2(rec)) + ":" + str(dbix_get_cum(rec)) + ")"];
indent -:= 1;
return stg;
end if; -- done with compound case
if (ncr := num_childr(rec)) = 0 then return ["(,:0)"]; end if;
t := []; -- otherwise we must analyze the non-compound case
prev := 0;
for j in [1..ncr] loop
int := (new := get_ch_cum(rec,j)) - prev; prev := new;
rid := shexify(get_ch_cum2(rec,j));
t with := rid + ":" + str(int);
end loop;
return ["\n" + (indent * " ") + "("] + t + [shexify(dbix_get_cum2(rec)) + ":" + str(dbix_get_cum(rec)) + ")"];
end dbix_dump_in;
end dbix_dump;
procedure shexify(stg); -- put into abbreviated hex
ns := #(stg := hexify(stg)); zers := span(stg,"0");
return if ns = 8 then "." + stg else "." + stg + "." + str(#zers) end if;
end shexify;
procedure hdbix_comp(rec,j); -- fetch of component containing cumulant j
oo:=hdbix_comp(rec,j);
pp:= comp_cum_ix(rec,1,j)(1..2);
if (oo/=pp) then
print("DBIX_COMP(",int_of_4(rec)," ",j,")");
print("Exp ",pp," Got ",oo);
print("# = ",[#pp(1),#oo(1),int_of_4(pp(1)),int_of_4(oo(1))]);
stop;
end if;
return pp;
end hdbix_comp;
procedure hdbix_comp2(rec,j); -- fetch of component containing second cumulant j
oo:=hdbix_comp2(rec,j);
pp:= comp_cum_ix(rec,2,j)(1..2);
if (oo/=pp) then
print("DBIX_COMP2(",int_of_4(rec)," ",int_of_4(j),")");
print("Exp ",pp," Got ",oo);
stop;
end if;
return pp;
end hdbix_comp2;
procedure hdbix_comp_cum(rec,x); -- fetch of x-th component, with cumulant
oo:=hdbix_comp_cum(rec,x);
pp:= comp_cum_ix(rec,1,x);
if (oo/=pp) then
print("DBIX_COMP_CUM(",int_of_4(rec)," ",x,")");
print("Exp ",pp," Got ",oo);
print("# = ",[#pp(1),#oo(1),int_of_4(pp(1)),int_of_4(oo(1))]);
stop;
end if;
return pp;
end hdbix_comp_cum;
procedure hdbix_comp_cum2(rec,x); -- fetch of component containing second cumulant x, with cumulant
oo:=hdbix_comp_cum2(rec,x);
pp:= comp_cum_ix(rec,2,x);
if (oo/=pp) then
print("DBIX_COMP_CUM2(",int_of_4(rec)," ",int_of_4(x),")");
print("Exp ",pp," Got ",oo);
print("# = ",[#pp(1),#oo(1),int_of_4(pp(1)),int_of_4(oo(1))]);
stop;
end if;
return pp;
end hdbix_comp_cum2;
procedure comp_cum_ix(rec,srch_on,x); -- fetch of x-th component by search on specified cumulant
-- if (srch_on=1) then
-- print(" 1 - comp_cum_ix ",int_of_4(rec)," ",x);
-- else
-- print(" 2 - comp_cum_ix ",int_of_4(rec)," ",int_of_4(x));
-- end if;
-- returns value in the form [rid,len,cum_len]
-- component should be found by binary search
-- search for the first index component with cumulant past
-- the specified x; Return OM if there is none such.
-- NOTE: this should be by binary search
-- find the first node for which a cumulant >= j
if x = OM then -- want last node
pass("dbix_cc_end");
j := num_childr(rec);
the_cum := if srch_on = 1 then get_ch_cum(rec,j) else get_ch_cum2(rec,j) end if;
-- Note: only needed in non-compound case
else
if srch_on = 2 then
iofx := int_of_4(x);
pass("dbix_srch_on2");
end if;
if not (exists j in [1..num_childr(rec)] |
if srch_on = 1 then (the_cum := get_ch_cum(rec,j)) >= x else
int_of_4(the_cum := get_ch_cum2(rec,j)) >= iofx end if) then
--print("******** return OM: ",x," ",nc := num_childr(rec)," ",dbix_dump(rec)," ",if nc > 0 then get_ch_cum(rec,nc) else "NONE" end if);
return OM; -- desired node not found
end if;
end if;
prev_cum := if j = 1 then 0 else get_ch_cum(rec,j - 1) end if;
-- previous integer cumulant, needed at several points below
if not dr_is_compound(rec) then -- if node is not compound we have what we want
if srch_on = 1 then -- the integer cum was calculated above
pass("dbix_srch_nc1");
return [get_ch_cum2(rec,j),the_cum - prev_cum,the_cum];
else -- the rec id was calculated above
pass("dbix_srch_nc2");
int_cum := get_ch_cum(rec,j);
return [the_cum,int_cum - prev_cum,int_cum];
end if;
end if; -- otherwise we deal with the compound case
res := comp_cum_ix(voc(rec,j),srch_on,if x = OM or srch_on = 2 then x else x - prev_cum end if);
-- continue the search recursively
[rid,ridl,lencum] := res; -- decode the result returned recursively
return [rid] + [ridl,prev_cum + lencum];
end comp_cum_ix;
procedure hdbix_set_comp(rw rec,w,x);-- assignment of the first component whose second cumulant is at least w
return hdbix_set_comp(rec,w,x);
if (x/=OM) then
print("DBIX_SET_COMP ",#rec," ",int_of_4(rec)," w=",w," x=",x);
if (x/=OM) then
print([int_of_4(x(1)),x(2)]);
end if;
end if;
set_comp_ix(rec,1,w,x);
end hdbix_set_comp;
procedure hdbix_set_comp2(rw rec,w,x);-- assignment of the first component whose second cumulant is at least w
return hdbix_set_comp2(rec,w,x);
set_comp_ix(rec,2,w,x);
end hdbix_set_comp2;
procedure set_comp_ix(rw rec,srch_on,w,x);
-- assignment of the first component whose specified cumulant is at least w; w must be in range
-- we must first copy rec if its refcount is greater than 1, and transfer one reference
-- from its old to its copied version
if refcount(int_of_4(rec)) > 1 then -- must copy
pass("dbix_set_copy");
stg := dr_load(rec); new_r := dr_new_rec();
dr_setrecbuf(new_r,stg);
dr_dirtify(new_r);
increfs(new_r,1); incref(rec,-1); rec := new_r; -- substitute copy for original
end if;
ic := dr_is_compound(rec); if srch_on = 2 then wint := int_of_4(w); end if;
ncr := num_childr(rec);
if w = OM then -- make change in last node
pass("dbix_set_last");
w := if srch_on = 1 then get_ch_cum(rec,ix := ncr) else get_ch_cum2(rec,ix := ncr) end if;
if ix = 0 then print("Deletion at end of empty tree is not allowed."); stop; end if;
elseif not (exists ix in [1..ncr] |
if srch_on = 1 then get_ch_cum(rec,ix) >= w else int_of_4(get_ch_cum2(rec,ix)) >= wint end if) then
print("DBIX - Search index " + str(w) + " out of range in assignment to cumulating vector " + hexify(rec)
+ " " + str(ncr) + " " + str(ic) + " " + hexify(dr_load(rec))); stop;
end if;
-- get the local cumulant of the child
old_cum := get_ch_cum(rec,ix) - (prev_cum := if ix = 1 then 0 else get_ch_cum(rec,ix - 1) end if);
-- find the last cumulant value of voc(rec,ix), or the length of active part of voc(rec,ix)
if x /= OM then -- we are not dealing with a deletion
if not ic then -- non-compound case; we must add the change in the leaf cumulant
-- to the cumulant of all following nodes.
cum_change := x(2) - old_cum;
update_cums(rec,ix,cum_change); -- update the cums,starting with the given child
pass("dbix_set_nodnc");
set_ch_cum2(rec,ix,x(1));
-- modify the cum of the changed component. Note that the following
-- second cumulants need not be changed. However, if ix references the
-- final child of rec, a cum2 entry in the parent of rec may need to be changed below.
return; -- done with this case
end if; -- otherwise we have the compound case
pass("dbix_set_comp");
the_child := voc(rec,ix); -- the next operation mayy copy the child
set_comp_ix(the_child,srch_on,if srch_on = 1 then w - prev_cum else w end if,x);
-- make the change in the child
cum_change := dbix_get_cum(the_child) - old_cum;
set_voc(rec,ix,the_child); -- put the revised, possibly copied child back into position
-- the 'update_cums' procedure which we call now must then start with the (properly set)
-- cumulant of the preceding node, and then left_add the change in the the cumulant
-- to the cumulant of this child to all the subsequent children. This assumes that
-- (new_d - old_d) + c + old_d + e = c + new_d + e
-- for all cumulant values. This is obviously true for values using associative-commutative
-- cumulator functions with an inverse, and also in the (string) case where a + b = b.
update_cums(rec,ix,cum_change); -- update the cums,starting with the given child
if ix = ncr then
set_ch_cum2(rec,ix,dbix_get_cum2(the_child));
pass("dbix_set_final");
else
pass("dbix_set_notfinal");
end if;
-- the final cumulant of the_child might have been changed by
-- the preceding dbix_set_comp(the_child,..) operation; see preceding comment.
--print("compound self,ix: ",dbix_get_cum()," ",ix);
return; -- done with the non-deletion case
end if; -- otherwise we are dealing with a deletion
if not dr_is_compound(rec) then -- non-compound case
pass("dbix_delnc");
if num_childr(rec) > 0 then -- need not delete cumulant if no remaining children
pass("dbix_delnorem");
stg := vect_of_cums(rec); -- get the vector of cums
stg((ix - 1) * 5 + 1..ix * 5) := ""; -- drop one element
set_vect_of_cums(rec,stg); -- put back into place
stg := vect_of_cums2(rec); -- get the second vector of cums
stg((ix - 1) * 4 + 1..ix * 4) := ""; -- drop one element
set_vect_of_cums2(rec,stg); -- put back into place
else
pass("dbix_delrem");
end if;
-- delete the child from the string of descendants
set_num_childr(rec,num_childr(rec) - 1); -- count down the number of children
-- note: sets the 'dirty' bit
-- delete the cumulant of the ix-th node. note that this has already been loaded
cum_change := -old_cum;
update_cums(rec,ix,cum_change); -- update the cums,starting with the appropriate child
return; -- done with this case
end if; -- otherwise we are dealing with a deletion in a compound case
pass("dbix_delcomp");
the_child := voc(rec,ix); -- get the child
old_ch_leafsum := dbix_get_cum(old_child := the_child);
set_comp_ix(the_child,srch_on,if srch_on=1 then w - prev_cum else w end if,OM); -- make the deletion in the child
new_ch_leafsum := dbix_get_cum(the_child);
set_voc(rec,ix,the_child := the_child); -- re-insert the possibly modified child
cum_change := new_ch_leafsum - old_ch_leafsum;
update_cums(rec,ix,cum_change); -- update the cums,starting with the given child
-- since the last child may have been deleted, we also need to update the second cum
if ix = ncr then
pass("dbix_delcomplast");
set_ch_cum2(rec,ix,dbix_get_cum2(the_child));
else
pass("dbix_delcomplast");
end if;
-- now it is possible that the child has lost enough children to have fallen below the required dbix_low_lim
-- if this has happened, we attempt to share or join children with one of the adjacent siblings
if num_childr(the_child) >= dbix_low_lim then -- otherwise try to join or share
pass("dbix_delnojs");
return;
end if;
if pull_from_left(rec,ix) or pull_from_right(rec,ix) then
pass("dbix_canpull");
return;
end if;
if join_with_left(rec,ix) or join_with_right(rec,ix) then
pass("dbix_canjoin");
null;
end if;
-- In the 'join' case, either the left or the right join must work,
-- since in this compound case we must have at least one sibling.
-- but we must check to see if the node being processed has fallen to
-- just one child, and if it has, replace it by its single child.
if num_childr(rec) > 1 then
pass("dbix_delnonly");
return;
end if;
pass("dbix_delonly");
ch_rec := dr_load(the_ch := voc(rec,1)); dr_load(rec);
dr_setrecbuf(rec,ch_rec);
dr_dirtify(rec);
if dr_is_compound(the_ch) then set_vect_of_children(the_ch,""); end if; -- the children have all moved
incref(the_ch,-1); -- child data is inherited from single child, which loses a reference
end set_comp_ix;
procedure update_cums(rec,ix,cum_change); -- update the cums of this tree,starting with the given child
-- add cum_change to all subsequent children
for j in [ix..nvc := num_childr(rec)] loop
set_ch_cum(rec,j,cum_change + get_ch_cum(rec,j));
end loop;
end update_cums;
procedure cumulate(rec);
-- initalize the cumulants of a node whose children are either leaves or already initialized
if not dr_is_compound(rec) then return; end if; -- non-compound nodes are already cumulated
the_cum := 0;
for j in [1..num_childr(rec)] loop
nd := voc(rec,j); -- get the j-th child
nd_cum := dbix_get_cum(nd);
-- cumulant of the final child of the subnode, or occurence string length
the_cum := the_cum + nd_cum;
set_ch_cum(rec,j,the_cum); -- update the child's cumulant value
nd_cum2 := dbix_get_cum2(nd);
-- cumulant2 of the final child of the subnode, or last element of occurence string
set_ch_cum2(rec,j,nd_cum2); -- update the child's cumulant value
end loop;
end cumulate;
procedure hdbix_insert(rw rec,j,x); -- insertion before j-th component; or at the end if j = OM
insert(rec,1,j,x);
end hdbix_insert;
procedure hdbix_insert2(rw rec,j,x); -- insertion before component with second cumulant at least j;
insert(rec,2,j,x); -- or at the end if j = OM
end hdbix_insert2;
procedure insert(rw rec,srch_on,j,x); -- insertion before j-th component with at least specified cumulant;
-- or at the end if j = OM
-- component should be found by binary search
if refcount(int_of_4(rec)) > 1 then -- must copy
pass("dbix_in_copy");
stg := dr_load(rec); new_r := dr_new_rec();
dr_setrecbuf(new_r,stg);
dr_dirtify(new_r);
increfs(new_r,1); incref(rec,-1); rec := new_r; -- substitute copy for original
end if;
result := OM; -- in case desired element not found
ic := dr_is_compound(rec); ncr := num_childr(rec);
the_cum := dbix_get_cum(rec); -- get final cumulant of this tree
[x_cum2,x_cum] := x; -- cumulant values of the leaf x
if j /= OM then -- look for target node of insertion, if any
pass("dbix_in_nend");
if srch_on = 2 then
pass("dbix_in_srch2");
iofj := int_of_4(j);
else
pass("dbix_in_srch1");
end if;
if exists ix in [1..ncr] |
if srch_on = 1 then (cum := get_ch_cum(rec,ix)) >= j else
int_of_4(cum := get_ch_cum2(rec,ix)) >= iofj end if then
result := [ix,if ix = 1 then 0 else get_ch_cum(rec,ix - 1) end if,cum];
if ic then nd := voc(rec,ix); end if;
pass("dbix_in_srch_notend");
else
result := OM;
pass("dbix_in_srch_end");
end if;
end if;
if result = OM then -- we have insertion at the very end
if not ic then -- simply append to vector
pass("dbix_in_srch_ncend");
set_num_childr(rec,nvc := ncr + 1);
-- add the cumulant of x to the present cumulant of this tree
set_ch_cum(rec,nvc,the_cum := the_cum + x_cum);
set_ch_cum2(rec,nvc,x_cum2);
--print("num_childr(rec): ",nvc," ",num_childr(rec)," ",ncr," ",dbix_hi_lim + 1);
if nvc <= dbnc_hi_lim then
pass("dbix_in_srch_ncend_nos");
return;
end if; -- no need to split
-- otherwise we must split, and becomes compound
pass("dbix_in_srch_ncend_split");
-- note that in this case we are at the very top of the tree
th := two_halves(rec); -- get the two halves
set_is_compound(rec,true); -- note that it is indeed compound
set_vect_of_children(rec,th); -- split into 2 non-compound subtrees
set_ch_cum(rec,1,dbix_get_cum(voc(rec,1))); set_ch_cum(rec,2,the_cum);
set_ch_cum2(rec,1,dbix_get_cum2(voc(rec,1))); set_ch_cum2(rec,2,dbix_get_cum2(voc(rec,2)));
return; -- done with this case
end if; -- otherwise we have insertion at the very end of a compound vector
last_child:= voc(rec,nvc := num_childr(rec)); -- get the last child
dbix_insert(last_child,OM,x); -- insert at the end of this last child
set_voc(rec,ncr,last_child); -- insert the possibly modified child back into vect_of_children
-- add the cumulant of x to the present cumulant of this tree
set_ch_cum(rec,ncr,the_cum := the_cum + x_cum);
set_ch_cum2(rec,ncr,x_cum2);
if (if dr_is_compound(last_child) then dbix_hi_lim else dbnc_hi_lim end if) > num_childr(last_child) then
pass("dbix_in_srch_compend_nos");
return; -- no need to split the child
end if;
-- otherwise we must split the last child
pass("dbix_in_srch_compend_split");
split_node(rec,nvc); -- split the nvc-th node into two. we insert an empty node to the right
-- of node nvc, and then move half the children of the nvc-th node into the new node
if (nvc := num_childr(rec)) <= dbix_hi_lim then -- no need to split this node
pass("dbix_in_srch_compend_nosrec");
return; -- done with this case
end if;
-- otherwise we are at the very top of the tree, so we must split it into two parts and create a new tree level
pass("dbix_in_srch_compend_splrec");
set_vect_of_children(rec,two_halves(rec));
set_ch_cum(rec,1,dbix_get_cum(voc(rec,1))); set_ch_cum(rec,2,the_cum);
set_ch_cum2(rec,1,dbix_get_cum2(voc(rec,1))); set_ch_cum2(rec,2,dbix_get_cum2(voc(rec,2)));
return; -- done with this case
end if; -- end of case of insertion at the very end
-- in the remaining cases we have an insertion before one of our nodes
[ix,prev_cum,cum] := result; -- decode the result returned, geting the insertion result and the preceding cumulant
if not ic then -- insertion at appropriate position in non-compound vector
pass("dbix_inin_nc");
the_cums := vect_of_cums(rec); the_cums2 := vect_of_cums2(rec);
the_cums(5 * ix - 4..5 * ix - 5) :=
if ix = 1 then "\x00\x00\x00\x00\x00" else the_cums(5 * ix - 9..5 * ix - 5) end if;
the_cums2(4 * ix - 3..4 * ix - 4) := x_cum2;
--print("#the_cums: ",#the_cums); if #the_cums > 70 then stop; end if;
set_vect_of_cums(rec,the_cums); -- make insertion into list of cums
set_vect_of_cums2(rec,the_cums2); -- make insertion into list of cums
set_num_childr(rec,nvc := ncr + 1); -- there is one more child
update_cums(rec,ix,x_cum); -- adjust the given and following cumulants
if nvc <= dbnc_hi_lim then -- no need to split
pass("dbix_inin_ncnos");
return; -- done with this case
end if; -- otherwise we must split, and becomes compound
pass("dbix_inin_nctopspl");
-- otherwise we are at the very top of the tree, so we must split it into two parts and create a new tree level
th := two_halves(rec); -- get the two halves
set_is_compound(rec,true); -- note that it is indeed compound
set_vect_of_children(rec,th); -- split into 2 non-compound subtrees
set_ch_cum(rec,1,dbix_get_cum(voc(rec,1))); set_ch_cum(rec,2,the_cum + x_cum);
set_ch_cum2(rec,1,dbix_get_cum2(voc(rec,1))); set_ch_cum2(rec,2,dbix_get_cum2(voc(rec,2)));
--print("just became compound: ",dr_is_compound(rec)," ",dbix_dump(rec)); stop;
return; -- done with this case
end if; -- otherwise we deal with the compound case
c := voc(rec,ix); -- get the child into which the insertion will now be made
-- and get the position in this child at which the insertion will be made
insert(c,srch_on,if srch_on = 1 then j - prev_cum else j end if,x); -- insert x into this child
set_voc(rec,ix,c); -- put the possibly revised child back into position
update_cums(rec,ix,x_cum); -- adjust the given and following cumulants
set_ch_cum2(rec,ix,dbix_get_cum2(c)); -- adjust the second cumulant on record for the child
-- which may have changed if the insertion was at the end of the child
if (if dr_is_compound(c) then dbix_hi_lim else dbnc_hi_lim end if) > num_childr(c) then
pass("dbix_inin_compnos");
return; -- no need to split the child
end if;
pass("dbix_inin_compsplit");
-- otherwise we must split the child
split_node(rec,ix); -- split the child into two. we insert an empty node to the right
-- of node ix, and then move half the nodes into it
if (nvc := num_childr(rec)) <= dbix_hi_lim then -- no need to split this node
pass("dbix_inin_compnosthis");
return; -- done with this case
end if; -- otherwise we are at the very top of the tree, so we must split it into two parts and create a new tree level
pass("dbix_inin_compsplitthis");
set_vect_of_children(rec,two_halves(rec));
set_ch_cum(rec,1,dbix_get_cum(voc(rec,1))); set_ch_cum(rec,2,the_cum + x_cum);
set_ch_cum2(rec,1,dbix_get_cum2(voc(rec,1))); set_ch_cum2(rec,2,dbix_get_cum2(voc(rec,2)));
end insert;
procedure two_halves(rec); -- split this tree into two halves
-- the two nodes created share the children of the original tree, so that the refcounts of
-- these children need no adjustment. The nodes created each have a refcount of 1.
-- Note that this routine is only called if rec has no parent.
the_type := if (ic := dr_is_compound(rec)) then db_index_node_record else db_index_node_ncr end if;
u1 := dr_new_rec(); u2 := dr_new_rec(); -- make and initialize two subtrees
set_type(u1,the_type); set_type(u2,the_type); -- the halves are compound iff this tree is compound
-- we must subtract the cumulant of the last retained child from all the children that move
-- to get the cumulant of the left node
hnvc := (nvc := num_childr(rec))/2; cum_last_retained := get_ch_cum(rec,hnvc);
for j in [hnvc + 1..nvc] loop -- subtract this from the cumulant of each child that will move
set_ch_cum(rec,j,get_ch_cum(rec,j) - cum_last_retained);
end loop;
vocums := vect_of_cums(rec); -- get the vector of cums
vocums2 := vect_of_cums2(rec); -- get the vector of cums
if ic then
pass("dbix_halves_nc");
set_vect_of_children(u1,(voch := vect_of_children(rec))(1..4 * hnvc));
else
pass("dbix_halves_comp");
set_num_childr(u1,hnvc); -- set the number of children of u1
end if;
set_vect_of_cums(u1,vocums(1..5 * hnvc)); -- u1 gets half the children and cums
set_vect_of_cums2(u1,vocums2(1..4 * hnvc)); -- u1 gets half the children and cums
set_vect_of_cums(u2,vocums(5 * hnvc + 1..)); -- the second half inherits adjusted cumulants from the original tree
set_vect_of_cums2(u2,vocums2(4 * hnvc + 1..)); -- likewise for the second cums
set_num_childr(u1,hnvc); -- set the number of children of u1
if ic then
set_vect_of_children(u2,voch(4 * hnvc + 1..)); -- u2 gets the other half of the children
else
set_num_childr(u2,nvc - hnvc); -- set the number of children of u2
end if;
--print("two_halves: ",str(dbix_dump(u1)),"\n",str(dbix_dump(u2)),"\n",hexify(voch(4 * hnvc + 1..)),"\n",hexify(dr_load(u2)));
return u1 + u2;
-- assemble the two nodes of the new compound tree; return as a string
end two_halves;
procedure pull_from_left(rec,k); -- split children with left sibling
if k = 1 or num_childr(voc(rec,k - 1)) <= dbix_low_lim then
return false;
end if;
--print("pull_from_left before share: ",k," ",dbix_low_lim," ",if k > 1 then num_childr()(k - 1) else OM end if);
pass("dbix_canpull_left");
share_right(rec,k - 1);
--print("pull_from_left after share: ",k));
return true;
end pull_from_left;
procedure pull_from_right(rec,k); -- split children with right sibling
if k >= (nrv := num_childr(rec)) or num_childr(voc(rec,k + 1)) <= dbix_low_lim then
return false;
end if;
pass("dbix_canpull_right");
--print("pull_from_right before share: ",k," ",#vect_of_children(k + 1)," ",dbix_low_lim," ",num_cums," ",#vect_of_children(k + 1) <= dbix_low_lim + num_cums);
share_right(rec,k);
--print("pull_from_right after share: ",k));
return true;
end pull_from_right;
procedure ushare_right(rec,k); -- share children with right-hand sibling
-- we divide the children of the k-th node, together with those of the k+1'st,
-- into two roughly equal groups, and make these the k-th and k+1'st nodes. The
-- cumulative totals must be adjusted in the k-th node, in the
-- children moved between nodes, and in the children of the k+1'st node
-- this routine must first copy the nodes among which children will move, if they have more than 1 reference.
-- but it does not change the number of references to the children, so that their refcounts need no adjustment
--print("share_right: ",k," ",str(dbix_dump(rec)));
if (ic := dr_is_compound(ndk := voc(rec,k))) then -- a compound node is involved in the sharing
pass("dbix_share_nc");
nchkp1 := #(rkp1 := vect_of_children(ndkp1 := voc(rec,k + 1)))/4;
nchk := #(rk := vect_of_children(ndk))/4; -- get the two groups of children, and their lengths
else
pass("dbix_share_comp");
nchkp1 := num_childr(ndkp1 := voc(rec,k + 1)); nchk := num_childr(ndk);
end if;
numleft := (nchk + nchkp1)/2; -- half the children; the left-hand will get this number of children
if refcount(int_of_4(ndkp1)) > 1 then -- must copy
pass("dbix_share_copy");
stg := dr_load(ndkp1); new_r := dr_new_rec();
dr_setrecbuf(new_r,stg);
dr_dirtify(new_r);
incref(ndkp1,-1); ndkp1 := new_r; -- substitute copy for original
increfs(new_r,1); set_voc(rec,k + 1,new_r);
end if;
if refcount(int_of_4(ndk)) > 1 then -- must copy
pass("dbix_share_copy2");
stg := dr_load(ndk); new_r := dr_new_rec();
dr_setrecbuf(new_r,stg);
dr_dirtify(new_r);
incref(ndk,-1); ndk := new_r; -- substitute copy for original
increfs(new_r,1); set_voc(rec,k,new_r);
end if;
if numleft > nchk then -- children will move left
pass("dbix_share_move_left");
num_mov := numleft - nchk; -- the number that will move left
--print("move left: ",num_mov," ",hexify(dr_load(ndk)),"\n",hexify(dr_load(ndkp1)));
-- we must subtract the cumulant of the last child moving left
-- from the cumulants of all the right-hand children which do not move,
-- and must add this to the cumulant of the k-th node. We must also
-- add the cumulant of the last left-hand child of ndk to the cumulants of all
-- the right-hand children which do move.
ndk_cum := get_ch_cum(rec,k); -- get the cumulant of ndk
right_cum := dbix_get_cum(ndk); -- cumulant of the last child of ndk
moved_cum := get_ch_cum(ndkp1,num_mov); -- cumulant of the last child of ndkp1 that moves
set_ch_cum(rec,k,moved_cum + ndk_cum); -- add moved_cum to the cumulant of the k-th node
set_ch_cum2(rec,k,get_ch_cum2(ndkp1,num_mov)); -- correct the second cumulant of the k-th node
for j in [1..num_mov] loop
set_ch_cum(ndkp1,j,right_cum + get_ch_cum(ndkp1,j));
end loop;
for j in [num_mov + 1..nchkp1] loop
set_ch_cum(ndkp1,j,get_ch_cum(ndkp1,j) - moved_cum);
end loop;
-- and now we must move the corresponding cums
cumvp1 := vect_of_cums(ndkp1); -- second vector of cums
--print("ndkp1: ",hexify(ndkp1)," ",hexify(dr_load(ndkp1))); stop;
set_vect_of_cums(ndk,vect_of_cums(ndk) + cumvp1(1..num_mov * 5)); -- move the cums in
set_vect_of_cums(ndkp1,cumvp1(num_mov * 5 + 1..)); -- move the cums out
-- since the number of cums of ndkp1 is defined by its number of children, we need not edit that list
cumvp1 := vect_of_cums2(ndkp1); -- second vector of second cums
--print("ndkp1: ",hexify(ndkp1)," ",hexify(dr_load(ndkp1))); stop;
set_vect_of_cums2(ndk,vect_of_cums2(ndk) + cumvp1(1..num_mov * 4)); -- move the cums in
set_vect_of_cums2(ndkp1,cumvp1(num_mov * 4 + 1..)); -- move the cums out
if ic then -- if the nodes are compound we must move children in addition to cumulants
pass("dbix_share_move_left_comp");
set_vect_of_children(ndk,rk + rkp1(1..4 * num_mov)); -- now actually move the children
set_vect_of_children(ndkp1,rkp1(4 * num_mov + 1..));
else -- not compound; must adjust the number of children
pass("dbix_share_move_left_nc");
set_num_childr(ndk,nchk + num_mov); set_num_childr(ndkp1,nchkp1 - num_mov);
end if;
--print("move the children: ",rk," ",rkp1," ",num_mov);
else -- children will move right
--print("move right: ",nchk - numleft);
--