rbyd-rr: Attempting correct balance of the diverging node itself
So far, our color-balance preserving range removal algorithm is working
great:
- Common trunk? color-balance preserving ✓
- Lower-diverged trunk? color-balance preserving ✓
- Upper-diverged trunk? color-balance preserving ✓
The only hole in our algorithm is the color-balance of the diverging
node itself.
Up until now we've simply recolored the diverging alt black, as this
avoids a large number of complicated corner cases. Unfortunately this
has the consequence of potentially offsetting the balance of our tree
by +-1:
.-> b-> .---b-> h=2 -.
.---b-> rm me | |
| .-> => => b-b-b-> +- unbalanced :(
| .-b-> | '-> |
| | .-> | .-> |
r-b-b-> '-b-> h=3 -'
^
diverging
This attempts to preserve the coloring of the diverging alt, and
preserve the color-balance, but we quickly run into the, uh, previously
mentioned complicated corner cases...
- First to note, we _can_ preserve red coloring on the gt path:
.-> b-> .---b-> h=2 -.
.---b-> rm me | |
| .-> => => r-b-b-> +- balanced :)
| .-b-> | '-> |
| | .-> | .-> |
r-b-b-> '-b-> h=2 -'
^
diverging
But only if it isn't a part of a pending yellow split. If it _is_ a
pending yellow split, the yellow split may try to reference the
yellow node in the history, but this won't work because our history
has been modified:
.-> .-> h=2 -.
.-----b-> .-----b-> |
| .-> b-> | .---b-> |
| .---b-> rm me => => | | +- unbalanced :(
| | .-> r-b-b-b-> |
| | .-b-> | '-> |
| | | .-> '-b-> |
y-r-b-b-> '-> h=3 -'
^ '-+-'
diverging wants to have split
- As for the le path, we can't even preserve the red coloring! For this
to work we would need to somehow color a flipped alt red (so the
"follow" edge is red, not the "not-follow"), but this isn't possible
with our encoding scheme (and definitely not worth reserving a whole
additional bit in every alt for):
r-b-b-> .-b-> .-b-> h=3 -.
| | '-> | '-> | '-> |
| '-b-> => | .-> => | .-> +- unbalanced :(
| '-> b-b-> .-b-b-> |
'---b-> rm me | |
'-> b---b-> h=2 -'
^ ^
diverging this wants to be red
The reason we can preserve reds on the gt path but not the le path is
because we write the le path first and stitch on the gt path. If
instead you wrote the gt path first, this would be flipped:
r-b-b-> .-b-> h=2 -.
| | '-> | '-> |
| '-b-> => => | .-> +- balanced :)
| '-> r-b-b-> |
'---b-> rm me | |
'-> b-> '---b-> h=2 -'
^
diverging
In theory, you could do _another_ pass over the tree to figure out
which order is needed to preserve coloring. But this would be an even
more complicated mess...
Not to mention this wouldn't even completely solve the color-balance
of the diverging alt because of yellow split issues...
And we haven't even touched issues related to yellow split color
propagation! Fortunately this JustWorksTM on the gt path, since it
mostly looks like a normal trunk after stitching. But we completely
ignore yellow split color propagation on the le path since this runs
into many of the same issues as red flipping.
But if you manage to make it though all of this mess while preserving
color-balance (code size be damned), we arive on what seems to be an
impossible case: How do you preserve color balance of a diverging alt
when both paths contain a pending yellow split?
.-> .-> .-> h=4 -.
.-----b-> .-b-> .-b-> |
| .-> | .-> | .-> |
| .---b-> .-y-b-> .-y-b-> |
| | .-> | .-> | .-> |
| | .-b-> | .-b-> | .-b-> |
| | | .-> b-b-b-> .-b-b-b-> |
.-y-r-b-b-> rm me => => | +- unbalanced :(
| .-> r-b-b-b-> |
| .-----b-> | | '-> |
| | .-> | | .-> |
| | .---b-> | '-b-> |
| | | .-> | .-> |
| | | .-b-> | .-b-> |
| | | | .-> | | .-> |
b-y-r-b-b-> '-b-b-> h=3 -'
^ ^
diverging lost color propagation
This seems to violate tail recursion!
Anyways, this turned into a bit of a rant and a bit of a mess.
If anyone reads this and is interested in exploring the balancing issues
further, the diverging alt logic currently contains some commented-out
coloring conditions:
(true) / (false) / (lfsr_tag_isred(p_alts[0]))
These are currently commented-out to what is currently known to be
optimal (see above), but can be tweaked to try to preserve different
colorings.
This commit is contained in:
@@ -2802,6 +2802,10 @@ static int lfsr_rbyd_appendattr(lfs_t *lfs, lfsr_rbyd_t *rbyd,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("%04x->%04x: --- appendattr ---\n",
|
||||||
|
lfsr_rbyd_trunk(rbyd),
|
||||||
|
rbyd->eoff);
|
||||||
|
|
||||||
// begin appending
|
// begin appending
|
||||||
int err = lfsr_rbyd_prepareappend(lfs, rbyd);
|
int err = lfsr_rbyd_prepareappend(lfs, rbyd);
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -2929,6 +2933,14 @@ again:;
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("%04x->%04x: tag 0x%x w%d (%d %d)\n",
|
||||||
|
branch,
|
||||||
|
rbyd->eoff,
|
||||||
|
alt,
|
||||||
|
weight,
|
||||||
|
lower_rid,
|
||||||
|
upper_rid);
|
||||||
|
|
||||||
// found an alt?
|
// found an alt?
|
||||||
if (lfsr_tag_isalt(alt)) {
|
if (lfsr_tag_isalt(alt)) {
|
||||||
// make jump absolute
|
// make jump absolute
|
||||||
@@ -2937,14 +2949,22 @@ again:;
|
|||||||
|
|
||||||
// do bounds want to take different paths? begin diverging
|
// do bounds want to take different paths? begin diverging
|
||||||
if (!lfsr_d_isdiverged(d_state)
|
if (!lfsr_d_isdiverged(d_state)
|
||||||
&& (lfsr_tag_follow2(alt, weight,
|
&& lfsr_tag_follow2(alt, weight,
|
||||||
p_alts[0], p_weights[0],
|
p_alts[0], p_weights[0],
|
||||||
lower_rid, upper_rid,
|
lower_rid, upper_rid,
|
||||||
a_rid, a_tag)
|
a_rid, a_tag)
|
||||||
^ lfsr_tag_follow2(alt, weight,
|
^ lfsr_tag_follow2(alt, weight,
|
||||||
p_alts[0], p_weights[0],
|
p_alts[0], p_weights[0],
|
||||||
lower_rid, upper_rid,
|
lower_rid, upper_rid,
|
||||||
b_rid, b_tag))) {
|
b_rid, b_tag)) {
|
||||||
|
printf("%04x->%04x: %cdiverge 0x%x w%d 0x%x w%d\n",
|
||||||
|
branch,
|
||||||
|
rbyd->eoff,
|
||||||
|
lfsr_tag_isred(alt) ? 'r' : 'b',
|
||||||
|
alt,
|
||||||
|
weight,
|
||||||
|
p_alts[0],
|
||||||
|
p_weights[0]);
|
||||||
LFS_ASSERT(d_state != LFSR_D_NOTDIVERGING);
|
LFS_ASSERT(d_state != LFSR_D_NOTDIVERGING);
|
||||||
|
|
||||||
// transition to the diverged state
|
// transition to the diverged state
|
||||||
@@ -2953,6 +2973,8 @@ again:;
|
|||||||
// caught on the previous pass
|
// caught on the previous pass
|
||||||
d_state = lfsr_d_diverge(d_state);
|
d_state = lfsr_d_diverge(d_state);
|
||||||
|
|
||||||
|
// TODO trim or something?
|
||||||
|
if (d_state != LFSR_D_DIVERGEDUPPER) {
|
||||||
if (lfsr_tag_follow2(
|
if (lfsr_tag_follow2(
|
||||||
alt, weight,
|
alt, weight,
|
||||||
p_alts[0], p_weights[0],
|
p_alts[0], p_weights[0],
|
||||||
@@ -2964,25 +2986,83 @@ again:;
|
|||||||
lower_rid, upper_rid);
|
lower_rid, upper_rid);
|
||||||
lfs_swap32(&jump, &branch_);
|
lfs_swap32(&jump, &branch_);
|
||||||
}
|
}
|
||||||
|
|
||||||
lfsr_tag_trim2(
|
lfsr_tag_trim2(
|
||||||
alt, weight,
|
alt, weight,
|
||||||
p_alts[0], p_weights[0],
|
p_alts[0], p_weights[0],
|
||||||
&lower_rid, &upper_rid,
|
&lower_rid, &upper_rid,
|
||||||
&lower_tag, &upper_tag);
|
&lower_tag, &upper_tag);
|
||||||
|
}
|
||||||
|
|
||||||
// stitch together diverged branches
|
// stitch together diverged branches
|
||||||
if (d_state == LFSR_D_DIVERGEDUPPER) {
|
if (d_state == LFSR_D_DIVERGEDUPPER) {
|
||||||
err = lfsr_rbyd_p_push(lfs, rbyd,
|
if (lfsr_tag_isle(alt)) {
|
||||||
p_alts, p_weights, p_jumps,
|
alt = LFSR_TAG_ALT(
|
||||||
LFSR_TAG_ALT(
|
|
||||||
LFSR_TAG_LE,
|
LFSR_TAG_LE,
|
||||||
LFSR_TAG_B,
|
(!lfsr_tag_isred(p_alts[0]))
|
||||||
d_tag),
|
? (LFSR_TAG_R & alt)
|
||||||
d_rid - lower_rid + weight,
|
: LFSR_TAG_B,
|
||||||
d_branch);
|
d_tag);
|
||||||
if (err) {
|
printf("%04x->%04x: dle 0x%x %d w%d (%d %d)\n",
|
||||||
return err;
|
branch,
|
||||||
|
rbyd->eoff,
|
||||||
|
d_tag,
|
||||||
|
d_rid,
|
||||||
|
weight,
|
||||||
|
lower_rid,
|
||||||
|
upper_rid);
|
||||||
|
lower_rid += weight;
|
||||||
|
weight = d_rid - lower_rid + weight;
|
||||||
|
if (lfsr_tag_isred(p_alts[0])
|
||||||
|
&& lfsr_tag_isle(p_alts[0])) {
|
||||||
|
weight -= p_weights[0];
|
||||||
}
|
}
|
||||||
|
lower_rid -= weight;
|
||||||
|
jump = d_branch;
|
||||||
|
} else {
|
||||||
|
lfsr_tag_flip2(
|
||||||
|
&alt, &weight,
|
||||||
|
p_alts[0], p_weights[0],
|
||||||
|
lower_rid, upper_rid);
|
||||||
|
alt = LFSR_TAG_ALT(
|
||||||
|
LFSR_TAG_LE,
|
||||||
|
(false) // (!lfsr_tag_isred(p_alts[0]))
|
||||||
|
? (LFSR_TAG_R & alt)
|
||||||
|
: LFSR_TAG_B,
|
||||||
|
d_tag);
|
||||||
|
printf("%04x->%04x: dgt 0x%x %d w%d (%d %d)\n",
|
||||||
|
branch,
|
||||||
|
rbyd->eoff,
|
||||||
|
d_tag,
|
||||||
|
d_rid,
|
||||||
|
weight,
|
||||||
|
lower_rid,
|
||||||
|
upper_rid);
|
||||||
|
lower_rid += weight;
|
||||||
|
weight = d_rid - lower_rid + weight;
|
||||||
|
if (lfsr_tag_isred(p_alts[0])
|
||||||
|
&& lfsr_tag_isle(p_alts[0])) {
|
||||||
|
weight -= p_weights[0];
|
||||||
|
}
|
||||||
|
lower_rid -= weight;
|
||||||
|
lfsr_tag_flip2(
|
||||||
|
&alt, &weight,
|
||||||
|
p_alts[0], p_weights[0],
|
||||||
|
lower_rid, upper_rid);
|
||||||
|
branch_ = d_branch;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%04x->%04x: dtag 0x%x w%d (%d %d)\n",
|
||||||
|
branch,
|
||||||
|
rbyd->eoff,
|
||||||
|
alt,
|
||||||
|
weight,
|
||||||
|
lower_rid,
|
||||||
|
upper_rid);
|
||||||
|
|
||||||
|
// TODO doc
|
||||||
|
y_branch = d_branch;
|
||||||
|
goto dont_trim_me;
|
||||||
}
|
}
|
||||||
|
|
||||||
branch = branch_;
|
branch = branch_;
|
||||||
@@ -3018,6 +3098,7 @@ again:;
|
|||||||
weight = 0;
|
weight = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dont_trim_me:;
|
||||||
// prune?
|
// prune?
|
||||||
// <b >b
|
// <b >b
|
||||||
// .-'| .-'|
|
// .-'| .-'|
|
||||||
@@ -3050,6 +3131,11 @@ again:;
|
|||||||
|
|
||||||
// prune unreachable red-black alts
|
// prune unreachable red-black alts
|
||||||
if (lfsr_tag_isred(p_alts[0])) {
|
if (lfsr_tag_isred(p_alts[0])) {
|
||||||
|
printf("%04x->%04x: rbprune 0x%x w%d\n",
|
||||||
|
branch,
|
||||||
|
rbyd->eoff,
|
||||||
|
alt,
|
||||||
|
weight);
|
||||||
alt = p_alts[0] & ~LFSR_TAG_R;
|
alt = p_alts[0] & ~LFSR_TAG_R;
|
||||||
weight = p_weights[0];
|
weight = p_weights[0];
|
||||||
jump = p_jumps[0];
|
jump = p_jumps[0];
|
||||||
@@ -3059,12 +3145,22 @@ again:;
|
|||||||
} else if (lfsr_tag_isred(alt)
|
} else if (lfsr_tag_isred(alt)
|
||||||
// prune unreachable black alts if root
|
// prune unreachable black alts if root
|
||||||
|| (!p_alts[0] && !lfsr_d_isdiverged(d_state))) {
|
|| (!p_alts[0] && !lfsr_d_isdiverged(d_state))) {
|
||||||
|
printf("%04x->%04x: rprune 0x%x w%d\n",
|
||||||
|
branch,
|
||||||
|
rbyd->eoff,
|
||||||
|
alt,
|
||||||
|
weight);
|
||||||
branch = branch_;
|
branch = branch_;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// convert unreachable non-root black alts into alt-nevers,
|
// convert unreachable non-root black alts into alt-nevers,
|
||||||
// if we prune these it would break the coloring of our tree
|
// if we prune these it would break the coloring of our tree
|
||||||
} else {
|
} else {
|
||||||
|
printf("%04x->%04x: bprune 0x%x w%d\n",
|
||||||
|
branch,
|
||||||
|
rbyd->eoff,
|
||||||
|
alt,
|
||||||
|
weight);
|
||||||
alt = LFSR_TAG_ALT(LFSR_TAG_LE, LFSR_TAG_B, 0);
|
alt = LFSR_TAG_ALT(LFSR_TAG_LE, LFSR_TAG_B, 0);
|
||||||
weight = 0;
|
weight = 0;
|
||||||
jump = 0;
|
jump = 0;
|
||||||
@@ -3089,6 +3185,16 @@ again:;
|
|||||||
p_alts[0], p_weights[0],
|
p_alts[0], p_weights[0],
|
||||||
lower_rid, upper_rid,
|
lower_rid, upper_rid,
|
||||||
a_rid, a_tag)) {
|
a_rid, a_tag)) {
|
||||||
|
printf("%04x->%04x: ysplit1 0x%x w%d 0x%x w%d (%x %x %x)\n",
|
||||||
|
branch,
|
||||||
|
rbyd->eoff,
|
||||||
|
alt,
|
||||||
|
weight,
|
||||||
|
p_alts[0],
|
||||||
|
p_weights[0],
|
||||||
|
p_jumps[0],
|
||||||
|
jump,
|
||||||
|
branch_);
|
||||||
lfsr_tag_flip2(&alt, &weight,
|
lfsr_tag_flip2(&alt, &weight,
|
||||||
p_alts[0], p_weights[0],
|
p_alts[0], p_weights[0],
|
||||||
lower_rid, upper_rid);
|
lower_rid, upper_rid);
|
||||||
@@ -3117,6 +3223,16 @@ again:;
|
|||||||
// | | .-'| | | .----'|
|
// | | .-'| | | .----'|
|
||||||
// 1 2 3 4 1 2 3 4 4
|
// 1 2 3 4 1 2 3 4 4
|
||||||
} else {
|
} else {
|
||||||
|
printf("%04x->%04x: ysplit2 0x%x w%d 0x%x w%d (%x %x %x)\n",
|
||||||
|
branch,
|
||||||
|
rbyd->eoff,
|
||||||
|
alt,
|
||||||
|
weight,
|
||||||
|
p_alts[0],
|
||||||
|
p_weights[0],
|
||||||
|
p_jumps[0],
|
||||||
|
jump,
|
||||||
|
branch_);
|
||||||
LFS_ASSERT(y_branch != 0);
|
LFS_ASSERT(y_branch != 0);
|
||||||
p_alts[0] = alt;
|
p_alts[0] = alt;
|
||||||
p_weights[0] += weight;
|
p_weights[0] += weight;
|
||||||
@@ -3207,11 +3323,17 @@ again:;
|
|||||||
// no divergence? guess we only need one trunk then, actually write
|
// no divergence? guess we only need one trunk then, actually write
|
||||||
// it out this time
|
// it out this time
|
||||||
if (d_state == LFSR_D_DIVERGINGLOWER) {
|
if (d_state == LFSR_D_DIVERGINGLOWER) {
|
||||||
|
printf("%04x->%04x: not diverging\n",
|
||||||
|
branch,
|
||||||
|
rbyd->eoff);
|
||||||
d_state = LFSR_D_NOTDIVERGING;
|
d_state = LFSR_D_NOTDIVERGING;
|
||||||
goto again;
|
goto again;
|
||||||
|
|
||||||
// diverged lower trunk? we need an upper trunk too
|
// diverged lower trunk? we need an upper trunk too
|
||||||
} else if (d_state == LFSR_D_DIVERGEDLOWER) {
|
} else if (d_state == LFSR_D_DIVERGEDLOWER) {
|
||||||
|
printf("%04x->%04x: diverging again\n",
|
||||||
|
branch,
|
||||||
|
rbyd->eoff);
|
||||||
// keep track of last alt on diverged trunk to stitch the trunks
|
// keep track of last alt on diverged trunk to stitch the trunks
|
||||||
// together with
|
// together with
|
||||||
d_state = LFSR_D_DIVERGINGUPPER;
|
d_state = LFSR_D_DIVERGINGUPPER;
|
||||||
|
|||||||
Reference in New Issue
Block a user