Rearranged lfsr_rbyd_append to have a single sprial loop with black recoloring during deletes

Preliminary comparisons show a minor improvement to code size at the cost
of stack usage. Really this boils down to a toss up, I'm currently
leaning towards this implementation of lfsr_rbyd_append as it has the
fewest moving parts, reusing the core rbyd loop for all mutating
operations.

Note these numbers are _very_ rough, there are likely some low-hanging
optimizations/cleanup and the rbyd size measurements are simply found
from fuzzing 1000 random permutations:

               commit size                       rbyd size
               code  stack  append/removes  create/deletes
spiralrb:      3178    664            1609            1621
spiralb:       3058    664            1609            1606 (current)
2stepb:        3284    632            1609            1606
This commit is contained in:
Christopher Haster
2023-01-30 20:30:16 -06:00
parent d3d340c426
commit 739a6e6d98
+314 -285
View File
@@ -2100,15 +2100,17 @@ static int lfsr_rbyd_append(lfs_t *lfs, lfsr_rbyd_t *rbyd_,
// this gets a bit confusing as we also may need to keep // this gets a bit confusing as we also may need to keep
// track of both the lower and upper bounds of diverging paths // track of both the lower and upper bounds of diverging paths
// in the case of range deletions // in the case of range deletions
//bool diverged = false; bool diverged = false;
bool flipped = false;
uint8_t found = 0;
// TODO bool flipped? bool found? // TODO bool flipped? bool found?
lfsr_sid_t lower_lower_id = -1; lfsr_sid_t lower_lower_id = -1;
lfsr_sid_t lower_upper_id = rbyd_->weight; lfsr_sid_t lower_upper_id = rbyd_->weight;
lfsr_sid_t upper_lower_id = upper_lower_id;
lfsr_sid_t upper_upper_id = upper_upper_id;
lfsr_tag_t lower_lower_tag = 0; lfsr_tag_t lower_lower_tag = 0;
lfsr_tag_t lower_upper_tag = 0xffff; lfsr_tag_t lower_upper_tag = 0xffff;
lfsr_tag_t upper_lower_tag = upper_lower_tag; lfsr_sid_t upper_lower_id = lower_lower_id;
lfsr_sid_t upper_upper_id = lower_upper_id;
lfsr_tag_t upper_lower_tag = lower_lower_tag;
lfsr_tag_t upper_upper_tag = lower_upper_tag; lfsr_tag_t upper_upper_tag = lower_upper_tag;
// queue of pending alts we can emulate rotations with // queue of pending alts we can emulate rotations with
@@ -2120,19 +2122,17 @@ static int lfsr_rbyd_append(lfs_t *lfs, lfsr_rbyd_t *rbyd_,
// descend down tree, building alt pointers // descend down tree, building alt pointers
while (true) { while (true) {
// // do we need to flip bounds? // do we need to flip bounds?
// if (diverged if (diverged && found < 1) {
// && !lfsr_tag_isfound(upper_tag_) flipped = !flipped;
// && (!p_alts[0] || lfsr_tag_isblack(p_alts[0]))) { lfs_swap16(&lower_tag_, &upper_tag_);
// LFS_ASSERT(false); lfs_swaps32(&lower_id_, &upper_id_);
// lfs_swap16(&lower_tag_, &upper_tag_); lfs_swap32(&lower_branch, &upper_branch);
// lfs_swaps32(&lower_id_, &upper_id_); lfs_swaps32(&lower_lower_id, &upper_lower_id);
// lfs_swap32(&lower_branch, &upper_branch); lfs_swaps32(&lower_upper_id, &upper_upper_id);
// lfs_swaps32(&lower_lower_id, &upper_lower_id); lfs_swap16(&lower_lower_tag, &upper_lower_tag);
// lfs_swaps32(&lower_upper_id, &upper_upper_id); lfs_swap16(&lower_upper_tag, &upper_upper_tag);
// lfs_swap16(&lower_lower_tag, &upper_lower_tag); }
// lfs_swap16(&lower_upper_tag, &upper_upper_tag);
// }
// read the alt pointer // read the alt pointer
lfsr_tag_t alt; lfsr_tag_t alt;
@@ -2147,14 +2147,23 @@ static int lfsr_rbyd_append(lfs_t *lfs, lfsr_rbyd_t *rbyd_,
// found an alt? // found an alt?
if (lfsr_tag_isalt(alt)) { if (lfsr_tag_isalt(alt)) {
// TODO get rid of weight_
lfs_size_t weight_ = weight + 1; lfs_size_t weight_ = weight + 1;
// make jump absolute // make jump absolute
jump = lower_branch - jump; jump = lower_branch - jump;
lfs_off_t branch_ = lower_branch + delta; lfs_off_t branch_ = lower_branch + delta;
// go ahead and make alt black, this isn't perfect but it's
// simpler and compact will take care of any balance issues
// that may occur
if (diverged) {
LFS_ASSERT(!p_alts[0] || lfsr_tag_isblack(p_alts[0]));
alt = lfsr_tag_mkblack(alt);
}
// do bounds want to take different paths? begin cutting // do bounds want to take different paths? begin cutting
if (/*!diverged if (!diverged
&&*/ lfsr_tag_follow2(alt, weight_, && lfsr_tag_follow2(alt, weight_,
p_alts[0], p_weights[0], p_alts[0], p_weights[0],
lower_lower_id, lower_upper_id, lower_lower_id, lower_upper_id,
lower_tag_, lower_id_) lower_tag_, lower_id_)
@@ -2176,15 +2185,17 @@ static int lfsr_rbyd_append(lfs_t *lfs, lfsr_rbyd_t *rbyd_,
goto diverging_red; goto diverging_red;
} }
//diverged = true; diverged = true;
LFS_ASSERT(!p_alts[0] || lfsr_tag_isblack(p_alts[0]));
alt = lfsr_tag_mkblack(alt);
upper_branch = lower_branch; upper_branch = lower_branch;
upper_lower_id = lower_lower_id; upper_lower_id = lower_lower_id;
upper_upper_id = lower_upper_id; upper_upper_id = lower_upper_id;
upper_lower_id = lower_lower_id;
upper_upper_id = lower_upper_id;
upper_lower_tag = lower_lower_tag; upper_lower_tag = lower_lower_tag;
upper_upper_tag = lower_upper_tag; upper_upper_tag = lower_upper_tag;
goto diverged; //goto diverged;
// // make sure upper path is in sync with red alts! // // make sure upper path is in sync with red alts!
@@ -2297,6 +2308,7 @@ diverging_red:
lower_branch = jump; lower_branch = jump;
continue; continue;
} }
}
// // cut while following // // cut while following
// } else if (diverged // } else if (diverged
@@ -2388,7 +2400,7 @@ diverging_red:
// lower_branch = branch_; // lower_branch = branch_;
// continue; // continue;
// } // }
} // }
// two reds makes a yellow, split? // two reds makes a yellow, split?
if (lfsr_tag_isred(alt) if (lfsr_tag_isred(alt)
@@ -2531,30 +2543,33 @@ diverging_red:
lfs_swap32(&jump, &branch_); lfs_swap32(&jump, &branch_);
} }
// push alt onto queue // trim alt from our current bounds
LFS_ASSERT((lfs_ssize_t)weight_ >= 0);
// printf("pushed alt%c%s 0x%x w%d 0x%x\n",
// lfsr_tag_isred(alt) ? 'r' : 'b',
// lfsr_tag_isgt(alt) ? "gt" : "le",
// lfsr_tag_key(alt),
// weight_,
// jump);
int err = lfsr_rbyd_p_push(lfs, rbyd_,
p_alts, p_weights, p_jumps,
alt, weight_, jump);
if (err) {
return err;
}
// continue to next alt
// TODO move this up?
if (lfsr_tag_isblack(alt)) { if (lfsr_tag_isblack(alt)) {
lfsr_tag_trim2( lfsr_tag_trim2(
alt, weight_,
p_alts[0], p_weights[0], p_alts[0], p_weights[0],
p_alts[1], p_weights[1],
&lower_lower_id, &lower_upper_id, &lower_lower_id, &lower_upper_id,
&lower_lower_tag, &lower_upper_tag); &lower_lower_tag, &lower_upper_tag);
} }
if (!diverged || flipped == lfsr_tag_isgt(alt)) {
// push alts onto our queue
LFS_ASSERT((lfs_ssize_t)weight_ >= 0);
// printf("pushed alt%c%s 0x%x w%d 0x%x\n",
// lfsr_tag_isred(alt) ? 'r' : 'b',
// lfsr_tag_isgt(alt) ? "gt" : "le",
// lfsr_tag_key(alt),
// weight_,
// jump);
int err = lfsr_rbyd_p_push(lfs, rbyd_,
p_alts, p_weights, p_jumps,
alt, weight_, jump);
if (err) {
return err;
}
}
// continue to next alt
graft = lower_branch; graft = lower_branch;
lower_branch = branch_; lower_branch = branch_;
@@ -2584,264 +2599,278 @@ diverging_red:
// found end of tree? // found end of tree?
} else { } else {
// update the tag id, marking as found // update the tag id, marking as found
lower_tag_ = lfsr_tag_mkfound(alt); lower_tag_ = alt;
lower_id_ = lower_upper_id-1; lower_id_ = lower_upper_id-1;
// // if we diverged, we also need to find the other bound // TODO deduplicate flips somehow?
// if (diverged && !lfsr_tag_isfound(upper_tag_)) { if (diverged && found < 2) {
// continue; found += 1;
// } flipped = !flipped;
lfs_swap16(&lower_tag_, &upper_tag_);
// if we hit this we didn't diverge, and need to update upper bound lfs_swaps32(&lower_id_, &upper_id_);
upper_tag_ = lower_tag_; lfs_swap32(&lower_branch, &upper_branch);
upper_id_ = lower_id_; lfs_swaps32(&lower_lower_id, &upper_lower_id);
upper_branch = lower_branch; lfs_swaps32(&lower_upper_id, &upper_upper_id);
upper_lower_id = lower_lower_id; lfs_swap16(&lower_lower_tag, &upper_lower_tag);
upper_upper_id = lower_upper_id; lfs_swap16(&lower_upper_tag, &upper_upper_tag);
upper_lower_tag = lower_lower_tag; continue;
upper_upper_tag = lower_upper_tag; }
// almost done, we just need to insert a new alt pointer // almost done, we just need to insert a new alt pointer
// to connect our leaf to the tree // to connect our leaf to the tree
goto stem; goto stem;
//// // if we diverged, we also need to find the other bound
//// if (diverged && !lfsr_tag_isfound(upper_tag_)) {
//// continue;
//// }
//
// // if we hit this we didn't diverge, and need to update upper bound
// upper_tag_ = lower_tag_;
// upper_id_ = lower_id_;
// upper_branch = lower_branch;
// upper_lower_id = lower_lower_id;
// upper_upper_id = lower_upper_id;
// upper_lower_tag = lower_lower_tag;
// upper_upper_tag = lower_upper_tag;
} }
} }
diverged:; //diverged:;
// descend down two branches of the tree, trimming inner branches // // descend down two branches of the tree, trimming inner branches
// and building alt pointers // // and building alt pointers
while (!lfsr_tag_isfound(lower_tag_) || !lfsr_tag_isfound(upper_tag_)) { // while (!lfsr_tag_isfound(lower_tag_) || !lfsr_tag_isfound(upper_tag_)) {
// // do we need to flip bounds? //// // do we need to flip bounds?
// if (diverged //// if (diverged
// && !lfsr_tag_isfound(upper_tag_) //// && !lfsr_tag_isfound(upper_tag_)
// && (!p_alts[0] || lfsr_tag_isblack(p_alts[0]))) { //// && (!p_alts[0] || lfsr_tag_isblack(p_alts[0]))) {
// lfs_swap16(&lower_tag_, &upper_tag_); //// lfs_swap16(&lower_tag_, &upper_tag_);
// lfs_swaps32(&lower_id_, &upper_id_); //// lfs_swaps32(&lower_id_, &upper_id_);
// lfs_swap32(&lower_branch, &upper_branch); //// lfs_swap32(&lower_branch, &upper_branch);
// lfs_swaps32(&lower_lower_id, &upper_lower_id); //// lfs_swaps32(&lower_lower_id, &upper_lower_id);
// lfs_swaps32(&lower_upper_id, &upper_upper_id); //// lfs_swaps32(&lower_upper_id, &upper_upper_id);
// lfs_swap16(&lower_lower_tag, &upper_lower_tag); //// lfs_swap16(&lower_lower_tag, &upper_lower_tag);
// lfs_swap16(&lower_upper_tag, &upper_upper_tag); //// lfs_swap16(&lower_upper_tag, &upper_upper_tag);
//// }
//
// // lower bound
// if (!lfsr_tag_isfound(lower_tag_)) {
// // read the alt pointer
// lfsr_tag_t alt;
// lfsr_sid_t weight;
// lfs_off_t jump;
// lfs_ssize_t delta = lfsr_rbyd_readtag(lfs,
// &lfs->pcache, &lfs->rcache, 0,
// rbyd_->block, lower_branch, &alt, &weight, &jump, NULL);
// if (delta < 0) {
// return delta;
// }
//
// // found an alt?
// if (lfsr_tag_isalt(alt)) {
// // TODO get rid of weight_
// lfs_size_t weight_ = weight + 1;
// // make jump absolute
// jump = lower_branch - jump;
// lfs_off_t branch_ = lower_branch + delta;
//
// // go ahead and make alt black, this isn't perfect but it's
// // simpler and compact will take care of any balance issues
// // that may occur
// alt = lfsr_tag_mkblack(alt);
//
// // prune?
// // <b >b
// // .-'| .-'|
// // <y | | |
// // .-------'| | | |
// // | <r | => | <b
// // | .----' | .-----------|-'|
// // | | <b | <b |
// // | | .----'| | .----'| |
// // 1 2 3 4 4 1 2 3 4 4 2
// // TODO prune1?
// if (lfsr_tag_prune2(
// alt, weight_,
// p_alts[0], p_weights[0],
// lower_lower_id, lower_upper_id,
// lower_lower_tag, lower_upper_tag)) {
// LFS_ASSERT(!lfsr_tag_isred(p_alts[0]));
//// if (p_alts[0] && lfsr_tag_isred(p_alts[0])) {
//// alt = lfsr_tag_mkblack(p_alts[0]);
//// weight_ = p_weights[0];
//// branch_ = jump;
//// jump = p_jumps[0];
//// lfsr_rbyd_p_pop(p_alts, p_weights, p_jumps);
//// } else {
// // TODO does this ever happen with normal prunes?
// //LFS_ASSERT(false);
// lower_branch = jump;
// continue;
//// }
// }
//
// // take alt? needs a flip
// // <b >b
// // .-'| => .-'|
// // 1 2 1 2 1
// if (lfsr_tag_follow(
// alt, weight_,
// lower_lower_id, lower_upper_id,
// lower_tag_, lower_id_)) {
// alt = lfsr_tag_flipalt(alt);
// weight_ = lfsr_tag_flipweight(weight_,
// lower_lower_id, lower_upper_id);
// lfs_swap32(&jump, &branch_);
// }
//
// // only keep outer alts, push onto queue
// if (lfsr_tag_isle(alt)) {
// LFS_ASSERT((lfs_ssize_t)weight_ >= 0);
// int err = lfsr_rbyd_p_push(lfs, rbyd_,
// p_alts, p_weights, p_jumps,
// alt, weight_, jump);
// if (err) {
// return err;
// }
// }
//
// // continue to next alt
// lfsr_tag_trim__(
// alt, weight_,
// &lower_lower_id, &lower_upper_id,
// &lower_lower_tag, &lower_upper_tag);
// lower_branch = branch_;
//
// // found end of tree?
// } else {
// // update the tag id, marking as found
// lower_tag_ = lfsr_tag_mkfound(alt);
// lower_id_ = lower_upper_id-1;
// }
// } // }
//
// lower bound // // upper bound
if (!lfsr_tag_isfound(lower_tag_)) { // if (!lfsr_tag_isfound(upper_tag_)) {
// read the alt pointer // // read the alt pointer
lfsr_tag_t alt; // lfsr_tag_t alt;
lfsr_sid_t weight; // lfsr_sid_t weight;
lfs_off_t jump; // lfs_off_t jump;
lfs_ssize_t delta = lfsr_rbyd_readtag(lfs, // lfs_ssize_t delta = lfsr_rbyd_readtag(lfs,
&lfs->pcache, &lfs->rcache, 0, // &lfs->pcache, &lfs->rcache, 0,
rbyd_->block, lower_branch, &alt, &weight, &jump, NULL); // rbyd_->block, upper_branch, &alt, &weight, &jump, NULL);
if (delta < 0) { // if (delta < 0) {
return delta; // return delta;
} // }
//
// found an alt? // // found an alt?
if (lfsr_tag_isalt(alt)) { // if (lfsr_tag_isalt(alt)) {
// TODO get rid of weight_ // // TODO get rid of weight_
lfs_size_t weight_ = weight + 1; // lfs_size_t weight_ = weight + 1;
// make jump absolute // // make jump absolute
jump = lower_branch - jump; // jump = upper_branch - jump;
lfs_off_t branch_ = lower_branch + delta; // lfs_off_t branch_ = upper_branch + delta;
//
// go ahead and make alt black, this isn't perfect but it's // // go ahead and make alt black, this isn't perfect but it's
// simpler and compact will take care of any balance issues // // simpler and compact will take care of any balance issues
// that may occur // // that may occur
alt = lfsr_tag_mkblack(alt); // alt = lfsr_tag_mkblack(alt);
//
// prune? // // prune?
// <b >b // // <b >b
// .-'| .-'| // // .-'| .-'|
// <y | | | // // <y | | |
// .-------'| | | | // // .-------'| | | |
// | <r | => | <b // // | <r | => | <b
// | .----' | .-----------|-'| // // | .----' | .-----------|-'|
// | | <b | <b | // // | | <b | <b |
// | | .----'| | .----'| | // // | | .----'| | .----'| |
// 1 2 3 4 4 1 2 3 4 4 2 // // 1 2 3 4 4 1 2 3 4 4 2
// TODO prune1? // // TODO prune1?
if (lfsr_tag_prune2( // if (lfsr_tag_prune2(
alt, weight_, // alt, weight_,
p_alts[0], p_weights[0], // p_alts[0], p_weights[0],
lower_lower_id, lower_upper_id, // upper_lower_id, upper_upper_id,
lower_lower_tag, lower_upper_tag)) { // upper_lower_tag, upper_upper_tag)) {
LFS_ASSERT(!lfsr_tag_isred(p_alts[0])); // LFS_ASSERT(!lfsr_tag_isred(p_alts[0]));
// if (p_alts[0] && lfsr_tag_isred(p_alts[0])) { //// if (p_alts[0] && lfsr_tag_isred(p_alts[0])) {
// alt = lfsr_tag_mkblack(p_alts[0]); //// alt = lfsr_tag_mkblack(p_alts[0]);
// weight_ = p_weights[0]; //// weight_ = p_weights[0];
// branch_ = jump; //// branch_ = jump;
// jump = p_jumps[0]; //// jump = p_jumps[0];
// lfsr_rbyd_p_pop(p_alts, p_weights, p_jumps); //// lfsr_rbyd_p_pop(p_alts, p_weights, p_jumps);
// } else { //// } else {
// TODO does this ever happen with normal prunes? // // TODO does this ever happen with normal prunes?
//LFS_ASSERT(false); // //LFS_ASSERT(false);
lower_branch = jump; // upper_branch = jump;
continue; // continue;
//// }
// }
//
// // take alt? needs a flip
// // <b >b
// // .-'| => .-'|
// // 1 2 1 2 1
// if (lfsr_tag_follow(
// alt, weight_,
// upper_lower_id, upper_upper_id,
// upper_tag_, upper_id_)) {
// alt = lfsr_tag_flipalt(alt);
// weight_ = lfsr_tag_flipweight(weight_,
// upper_lower_id, upper_upper_id);
// lfs_swap32(&jump, &branch_);
// }
//
// // only keep outer alts, push onto queue
// if (lfsr_tag_isgt(alt)) {
// LFS_ASSERT((lfs_ssize_t)weight_ >= 0);
// int err = lfsr_rbyd_p_push(lfs, rbyd_,
// p_alts, p_weights, p_jumps,
// alt, weight_, jump);
// if (err) {
// return err;
// } // }
} // }
//
// take alt? needs a flip // // continue to next alt
// <b >b // lfsr_tag_trim__(
// .-'| => .-'| // alt, weight_,
// 1 2 1 2 1 // &upper_lower_id, &upper_upper_id,
if (lfsr_tag_follow( // &upper_lower_tag, &upper_upper_tag);
alt, weight_, // upper_branch = branch_;
lower_lower_id, lower_upper_id, //
lower_tag_, lower_id_)) { // // found end of tree?
alt = lfsr_tag_flipalt(alt); // } else {
weight_ = lfsr_tag_flipweight(weight_, // // update the tag id, marking as found
lower_lower_id, lower_upper_id); // upper_tag_ = lfsr_tag_mkfound(alt);
lfs_swap32(&jump, &branch_); // upper_id_ = upper_upper_id-1;
} // }
// }
// only keep outer alts, push onto queue // }
if (lfsr_tag_isle(alt)) { //
LFS_ASSERT((lfs_ssize_t)weight_ >= 0);
int err = lfsr_rbyd_p_push(lfs, rbyd_,
p_alts, p_weights, p_jumps,
alt, weight_, jump);
if (err) {
return err;
}
}
// continue to next alt
lfsr_tag_trim__(
alt, weight_,
&lower_lower_id, &lower_upper_id,
&lower_lower_tag, &lower_upper_tag);
lower_branch = branch_;
// found end of tree?
} else {
// update the tag id, marking as found
lower_tag_ = lfsr_tag_mkfound(alt);
lower_id_ = lower_upper_id-1;
}
}
// upper bound
if (!lfsr_tag_isfound(upper_tag_)) {
// read the alt pointer
lfsr_tag_t alt;
lfsr_sid_t weight;
lfs_off_t jump;
lfs_ssize_t delta = lfsr_rbyd_readtag(lfs,
&lfs->pcache, &lfs->rcache, 0,
rbyd_->block, upper_branch, &alt, &weight, &jump, NULL);
if (delta < 0) {
return delta;
}
// found an alt?
if (lfsr_tag_isalt(alt)) {
// TODO get rid of weight_
lfs_size_t weight_ = weight + 1;
// make jump absolute
jump = upper_branch - jump;
lfs_off_t branch_ = upper_branch + delta;
// go ahead and make alt black, this isn't perfect but it's
// simpler and compact will take care of any balance issues
// that may occur
alt = lfsr_tag_mkblack(alt);
// prune?
// <b >b
// .-'| .-'|
// <y | | |
// .-------'| | | |
// | <r | => | <b
// | .----' | .-----------|-'|
// | | <b | <b |
// | | .----'| | .----'| |
// 1 2 3 4 4 1 2 3 4 4 2
// TODO prune1?
if (lfsr_tag_prune2(
alt, weight_,
p_alts[0], p_weights[0],
upper_lower_id, upper_upper_id,
upper_lower_tag, upper_upper_tag)) {
LFS_ASSERT(!lfsr_tag_isred(p_alts[0]));
// if (p_alts[0] && lfsr_tag_isred(p_alts[0])) {
// alt = lfsr_tag_mkblack(p_alts[0]);
// weight_ = p_weights[0];
// branch_ = jump;
// jump = p_jumps[0];
// lfsr_rbyd_p_pop(p_alts, p_weights, p_jumps);
// } else {
// TODO does this ever happen with normal prunes?
//LFS_ASSERT(false);
upper_branch = jump;
continue;
// }
}
// take alt? needs a flip
// <b >b
// .-'| => .-'|
// 1 2 1 2 1
if (lfsr_tag_follow(
alt, weight_,
upper_lower_id, upper_upper_id,
upper_tag_, upper_id_)) {
alt = lfsr_tag_flipalt(alt);
weight_ = lfsr_tag_flipweight(weight_,
upper_lower_id, upper_upper_id);
lfs_swap32(&jump, &branch_);
}
// only keep outer alts, push onto queue
if (lfsr_tag_isgt(alt)) {
LFS_ASSERT((lfs_ssize_t)weight_ >= 0);
int err = lfsr_rbyd_p_push(lfs, rbyd_,
p_alts, p_weights, p_jumps,
alt, weight_, jump);
if (err) {
return err;
}
}
// continue to next alt
lfsr_tag_trim__(
alt, weight_,
&upper_lower_id, &upper_upper_id,
&upper_lower_tag, &upper_upper_tag);
upper_branch = branch_;
// found end of tree?
} else {
// update the tag id, marking as found
upper_tag_ = lfsr_tag_mkfound(alt);
upper_id_ = upper_upper_id-1;
}
}
}
stem:; stem:;
LFS_ASSERT(!p_alts[0] || lfsr_tag_isblack(p_alts[0])); LFS_ASSERT(!p_alts[0] || lfsr_tag_isblack(p_alts[0]));
// // TODO need this? // TODO can this be done more simply? min/max maybe?
// // unflip our bounds so lower_lower/upper_upper is correct // unflip our bounds so lower_lower/upper_upper is correct
// if (!diverged) { if (!diverged) {
// upper_tag_ = lower_tag_; upper_tag_ = lower_tag_;
// upper_id_ = lower_id_; upper_id_ = lower_id_;
// upper_branch = lower_branch; upper_branch = lower_branch;
// upper_lower_id = lower_lower_id; upper_lower_id = lower_lower_id;
// upper_upper_id = lower_upper_id; upper_upper_id = lower_upper_id;
// upper_lower_tag = lower_lower_tag; upper_lower_tag = lower_lower_tag;
// upper_upper_tag = lower_upper_tag; upper_upper_tag = lower_upper_tag;
//// } else if (lower_id_ > upper_id_ } else if (flipped) {
//// || (lower_id_ == upper_id_ && lower_tag_ > upper_tag_)) { lfs_swap16(&lower_tag_, &upper_tag_);
//// lfs_swap16(&lower_tag_, &upper_tag_); lfs_swaps32(&lower_id_, &upper_id_);
//// lfs_swaps32(&lower_id_, &upper_id_); lfs_swap32(&lower_branch, &upper_branch);
//// lfs_swap32(&lower_branch, &upper_branch); lfs_swaps32(&lower_lower_id, &upper_lower_id);
//// lfs_swaps32(&lower_lower_id, &upper_lower_id); lfs_swaps32(&lower_upper_id, &upper_upper_id);
//// lfs_swaps32(&lower_upper_id, &upper_upper_id); lfs_swap16(&lower_lower_tag, &upper_lower_tag);
//// lfs_swap16(&lower_lower_tag, &upper_lower_tag); lfs_swap16(&lower_upper_tag, &upper_upper_tag);
//// lfs_swap16(&lower_upper_tag, &upper_upper_tag); }
// }
// split leaf nodes? // split leaf nodes?
// //