summaryrefslogtreecommitdiff
path: root/storage/ndb/src/kernel/blocks/dbtux/DbtuxTree.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'storage/ndb/src/kernel/blocks/dbtux/DbtuxTree.cpp')
-rw-r--r--storage/ndb/src/kernel/blocks/dbtux/DbtuxTree.cpp709
1 files changed, 709 insertions, 0 deletions
diff --git a/storage/ndb/src/kernel/blocks/dbtux/DbtuxTree.cpp b/storage/ndb/src/kernel/blocks/dbtux/DbtuxTree.cpp
new file mode 100644
index 00000000000..5107a8d8e31
--- /dev/null
+++ b/storage/ndb/src/kernel/blocks/dbtux/DbtuxTree.cpp
@@ -0,0 +1,709 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#define DBTUX_TREE_CPP
+#include "Dbtux.hpp"
+
+/*
+ * Add entry. Handle the case when there is room for one more. This
+ * is the common case given slack in nodes.
+ */
+void
+Dbtux::treeAdd(Frag& frag, TreePos treePos, TreeEnt ent)
+{
+ TreeHead& tree = frag.m_tree;
+ NodeHandle node(frag);
+ if (treePos.m_loc != NullTupLoc) {
+ // non-empty tree
+ jam();
+ selectNode(node, treePos.m_loc);
+ unsigned pos = treePos.m_pos;
+ if (node.getOccup() < tree.m_maxOccup) {
+ // node has room
+ jam();
+ nodePushUp(node, pos, ent, RNIL);
+ return;
+ }
+ treeAddFull(frag, node, pos, ent);
+ return;
+ }
+ jam();
+ insertNode(node);
+ nodePushUp(node, 0, ent, RNIL);
+ node.setSide(2);
+ tree.m_root = node.m_loc;
+}
+
+/*
+ * Add entry when node is full. Handle the case when there is g.l.b
+ * node in left subtree with room for one more. It will receive the min
+ * entry of this node. The min entry could be the entry to add.
+ */
+void
+Dbtux::treeAddFull(Frag& frag, NodeHandle lubNode, unsigned pos, TreeEnt ent)
+{
+ TreeHead& tree = frag.m_tree;
+ TupLoc loc = lubNode.getLink(0);
+ if (loc != NullTupLoc) {
+ // find g.l.b node
+ NodeHandle glbNode(frag);
+ do {
+ jam();
+ selectNode(glbNode, loc);
+ loc = glbNode.getLink(1);
+ } while (loc != NullTupLoc);
+ if (glbNode.getOccup() < tree.m_maxOccup) {
+ // g.l.b node has room
+ jam();
+ Uint32 scanList = RNIL;
+ if (pos != 0) {
+ jam();
+ // add the new entry and return min entry
+ nodePushDown(lubNode, pos - 1, ent, scanList);
+ }
+ // g.l.b node receives min entry from l.u.b node
+ nodePushUp(glbNode, glbNode.getOccup(), ent, scanList);
+ return;
+ }
+ treeAddNode(frag, lubNode, pos, ent, glbNode, 1);
+ return;
+ }
+ treeAddNode(frag, lubNode, pos, ent, lubNode, 0);
+}
+
+/*
+ * Add entry when there is no g.l.b node in left subtree or the g.l.b
+ * node is full. We must add a new left or right child node which
+ * becomes the new g.l.b node.
+ */
+void
+Dbtux::treeAddNode(Frag& frag, NodeHandle lubNode, unsigned pos, TreeEnt ent, NodeHandle parentNode, unsigned i)
+{
+ NodeHandle glbNode(frag);
+ insertNode(glbNode);
+ // connect parent and child
+ parentNode.setLink(i, glbNode.m_loc);
+ glbNode.setLink(2, parentNode.m_loc);
+ glbNode.setSide(i);
+ Uint32 scanList = RNIL;
+ if (pos != 0) {
+ jam();
+ // add the new entry and return min entry
+ nodePushDown(lubNode, pos - 1, ent, scanList);
+ }
+ // g.l.b node receives min entry from l.u.b node
+ nodePushUp(glbNode, 0, ent, scanList);
+ // re-balance the tree
+ treeAddRebalance(frag, parentNode, i);
+}
+
+/*
+ * Re-balance tree after adding a node. The process starts with the
+ * parent of the added node.
+ */
+void
+Dbtux::treeAddRebalance(Frag& frag, NodeHandle node, unsigned i)
+{
+ while (true) {
+ // height of subtree i has increased by 1
+ int j = (i == 0 ? -1 : +1);
+ int b = node.getBalance();
+ if (b == 0) {
+ // perfectly balanced
+ jam();
+ node.setBalance(j);
+ // height change propagates up
+ } else if (b == -j) {
+ // height of shorter subtree increased
+ jam();
+ node.setBalance(0);
+ // height of tree did not change - done
+ break;
+ } else if (b == j) {
+ // height of longer subtree increased
+ jam();
+ NodeHandle childNode(frag);
+ selectNode(childNode, node.getLink(i));
+ int b2 = childNode.getBalance();
+ if (b2 == b) {
+ jam();
+ treeRotateSingle(frag, node, i);
+ } else if (b2 == -b) {
+ jam();
+ treeRotateDouble(frag, node, i);
+ } else {
+ // height of subtree increased so it cannot be perfectly balanced
+ ndbrequire(false);
+ }
+ // height of tree did not increase - done
+ break;
+ } else {
+ ndbrequire(false);
+ }
+ TupLoc parentLoc = node.getLink(2);
+ if (parentLoc == NullTupLoc) {
+ jam();
+ // root node - done
+ break;
+ }
+ i = node.getSide();
+ selectNode(node, parentLoc);
+ }
+}
+
+/*
+ * Remove entry. Optimize for nodes with slack. Handle the case when
+ * there is no underflow i.e. occupancy remains at least minOccup. For
+ * interior nodes this is a requirement. For others it means that we do
+ * not need to consider merge of semi-leaf and leaf.
+ */
+void
+Dbtux::treeRemove(Frag& frag, TreePos treePos)
+{
+ TreeHead& tree = frag.m_tree;
+ unsigned pos = treePos.m_pos;
+ NodeHandle node(frag);
+ selectNode(node, treePos.m_loc);
+ TreeEnt ent;
+ if (node.getOccup() > tree.m_minOccup) {
+ // no underflow in any node type
+ jam();
+ nodePopDown(node, pos, ent, 0);
+ return;
+ }
+ if (node.getChilds() == 2) {
+ // underflow in interior node
+ jam();
+ treeRemoveInner(frag, node, pos);
+ return;
+ }
+ // remove entry in semi/leaf
+ nodePopDown(node, pos, ent, 0);
+ if (node.getLink(0) != NullTupLoc) {
+ jam();
+ treeRemoveSemi(frag, node, 0);
+ return;
+ }
+ if (node.getLink(1) != NullTupLoc) {
+ jam();
+ treeRemoveSemi(frag, node, 1);
+ return;
+ }
+ treeRemoveLeaf(frag, node);
+}
+
+/*
+ * Remove entry when interior node underflows. There is g.l.b node in
+ * left subtree to borrow an entry from. The max entry of the g.l.b
+ * node becomes the min entry of this node.
+ */
+void
+Dbtux::treeRemoveInner(Frag& frag, NodeHandle lubNode, unsigned pos)
+{
+ TreeHead& tree = frag.m_tree;
+ TreeEnt ent;
+ // find g.l.b node
+ NodeHandle glbNode(frag);
+ TupLoc loc = lubNode.getLink(0);
+ do {
+ jam();
+ selectNode(glbNode, loc);
+ loc = glbNode.getLink(1);
+ } while (loc != NullTupLoc);
+ // borrow max entry from semi/leaf
+ Uint32 scanList = RNIL;
+ nodePopDown(glbNode, glbNode.getOccup() - 1, ent, &scanList);
+ // g.l.b may be empty now
+ // a descending scan may try to enter the empty g.l.b
+ // we prevent this in scanNext
+ nodePopUp(lubNode, pos, ent, scanList);
+ if (glbNode.getLink(0) != NullTupLoc) {
+ jam();
+ treeRemoveSemi(frag, glbNode, 0);
+ return;
+ }
+ treeRemoveLeaf(frag, glbNode);
+}
+
+/*
+ * Handle semi-leaf after removing an entry. Move entries from leaf to
+ * semi-leaf to bring semi-leaf occupancy above minOccup, if possible.
+ * The leaf may become empty.
+ */
+void
+Dbtux::treeRemoveSemi(Frag& frag, NodeHandle semiNode, unsigned i)
+{
+ TreeHead& tree = frag.m_tree;
+ ndbrequire(semiNode.getChilds() < 2);
+ TupLoc leafLoc = semiNode.getLink(i);
+ NodeHandle leafNode(frag);
+ selectNode(leafNode, leafLoc);
+ if (semiNode.getOccup() < tree.m_minOccup) {
+ jam();
+ unsigned cnt = min(leafNode.getOccup(), tree.m_minOccup - semiNode.getOccup());
+ nodeSlide(semiNode, leafNode, cnt, i);
+ if (leafNode.getOccup() == 0) {
+ // remove empty leaf
+ jam();
+ treeRemoveNode(frag, leafNode);
+ }
+ }
+}
+
+/*
+ * Handle leaf after removing an entry. If parent is semi-leaf, move
+ * entries to it as in the semi-leaf case. If parent is interior node,
+ * do nothing.
+ */
+void
+Dbtux::treeRemoveLeaf(Frag& frag, NodeHandle leafNode)
+{
+ TreeHead& tree = frag.m_tree;
+ TupLoc parentLoc = leafNode.getLink(2);
+ if (parentLoc != NullTupLoc) {
+ jam();
+ NodeHandle parentNode(frag);
+ selectNode(parentNode, parentLoc);
+ unsigned i = leafNode.getSide();
+ if (parentNode.getLink(1 - i) == NullTupLoc) {
+ // parent is semi-leaf
+ jam();
+ if (parentNode.getOccup() < tree.m_minOccup) {
+ jam();
+ unsigned cnt = min(leafNode.getOccup(), tree.m_minOccup - parentNode.getOccup());
+ nodeSlide(parentNode, leafNode, cnt, i);
+ }
+ }
+ }
+ if (leafNode.getOccup() == 0) {
+ jam();
+ // remove empty leaf
+ treeRemoveNode(frag, leafNode);
+ }
+}
+
+/*
+ * Remove empty leaf.
+ */
+void
+Dbtux::treeRemoveNode(Frag& frag, NodeHandle leafNode)
+{
+ TreeHead& tree = frag.m_tree;
+ ndbrequire(leafNode.getChilds() == 0);
+ TupLoc parentLoc = leafNode.getLink(2);
+ unsigned i = leafNode.getSide();
+ deleteNode(leafNode);
+ if (parentLoc != NullTupLoc) {
+ jam();
+ NodeHandle parentNode(frag);
+ selectNode(parentNode, parentLoc);
+ parentNode.setLink(i, NullTupLoc);
+ // re-balance the tree
+ treeRemoveRebalance(frag, parentNode, i);
+ return;
+ }
+ // tree is now empty
+ tree.m_root = NullTupLoc;
+}
+
+/*
+ * Re-balance tree after removing a node. The process starts with the
+ * parent of the removed node.
+ */
+void
+Dbtux::treeRemoveRebalance(Frag& frag, NodeHandle node, unsigned i)
+{
+ while (true) {
+ // height of subtree i has decreased by 1
+ int j = (i == 0 ? -1 : +1);
+ int b = node.getBalance();
+ if (b == 0) {
+ // perfectly balanced
+ jam();
+ node.setBalance(-j);
+ // height of tree did not change - done
+ return;
+ } else if (b == j) {
+ // height of longer subtree has decreased
+ jam();
+ node.setBalance(0);
+ // height change propagates up
+ } else if (b == -j) {
+ // height of shorter subtree has decreased
+ jam();
+ // child on the other side
+ NodeHandle childNode(frag);
+ selectNode(childNode, node.getLink(1 - i));
+ int b2 = childNode.getBalance();
+ if (b2 == b) {
+ jam();
+ treeRotateSingle(frag, node, 1 - i);
+ // height of tree decreased and propagates up
+ } else if (b2 == -b) {
+ jam();
+ treeRotateDouble(frag, node, 1 - i);
+ // height of tree decreased and propagates up
+ } else {
+ jam();
+ treeRotateSingle(frag, node, 1 - i);
+ // height of tree did not change - done
+ return;
+ }
+ } else {
+ ndbrequire(false);
+ }
+ TupLoc parentLoc = node.getLink(2);
+ if (parentLoc == NullTupLoc) {
+ jam();
+ // root node - done
+ return;
+ }
+ i = node.getSide();
+ selectNode(node, parentLoc);
+ }
+}
+
+/*
+ * Single rotation about node 5. One of LL (i=0) or RR (i=1).
+ *
+ * 0 0
+ * | |
+ * 5 ==> 3
+ * / \ / \
+ * 3 6 2 5
+ * / \ / / \
+ * 2 4 1 4 6
+ * /
+ * 1
+ *
+ * In this change 5,3 and 2 must always be there. 0, 1, 2, 4 and 6 are
+ * all optional. If 4 are there it changes side.
+*/
+void
+Dbtux::treeRotateSingle(Frag& frag, NodeHandle& node, unsigned i)
+{
+ ndbrequire(i <= 1);
+ /*
+ 5 is the old top node that have been unbalanced due to an insert or
+ delete. The balance is still the old balance before the update.
+ Verify that bal5 is 1 if RR rotate and -1 if LL rotate.
+ */
+ NodeHandle node5 = node;
+ const TupLoc loc5 = node5.m_loc;
+ const int bal5 = node5.getBalance();
+ const int side5 = node5.getSide();
+ ndbrequire(bal5 + (1 - i) == i);
+ /*
+ 3 is the new root of this part of the tree which is to swap place with
+ node 5. For an insert to cause this it must have the same balance as 5.
+ For deletes it can have the balance 0.
+ */
+ TupLoc loc3 = node5.getLink(i);
+ NodeHandle node3(frag);
+ selectNode(node3, loc3);
+ const int bal3 = node3.getBalance();
+ /*
+ 2 must always be there but is not changed. Thus we mereley check that it
+ exists.
+ */
+ ndbrequire(node3.getLink(i) != NullTupLoc);
+ /*
+ 4 is not necessarily there but if it is there it will move from one
+ side of 3 to the other side of 5. For LL it moves from the right side
+ to the left side and for RR it moves from the left side to the right
+ side. This means that it also changes parent from 3 to 5.
+ */
+ TupLoc loc4 = node3.getLink(1 - i);
+ NodeHandle node4(frag);
+ if (loc4 != NullTupLoc) {
+ jam();
+ selectNode(node4, loc4);
+ ndbrequire(node4.getSide() == (1 - i) &&
+ node4.getLink(2) == loc3);
+ node4.setSide(i);
+ node4.setLink(2, loc5);
+ }//if
+
+ /*
+ Retrieve the address of 5's parent before it is destroyed
+ */
+ TupLoc loc0 = node5.getLink(2);
+
+ /*
+ The next step is to perform the rotation. 3 will inherit 5's parent
+ and side. 5 will become a child of 3 on the right side for LL and on
+ the left side for RR.
+ 5 will get 3 as the parent. It will get 4 as a child and it will be
+ on the right side of 3 for LL and left side of 3 for RR.
+ The final step of the rotate is to check whether 5 originally had any
+ parent. If it had not then 3 is the new root node.
+ We will also verify some preconditions for the change to occur.
+ 1. 3 must have had 5 as parent before the change.
+ 2. 3's side is left for LL and right for RR before change.
+ */
+ ndbrequire(node3.getLink(2) == loc5);
+ ndbrequire(node3.getSide() == i);
+ node3.setLink(1 - i, loc5);
+ node3.setLink(2, loc0);
+ node3.setSide(side5);
+ node5.setLink(i, loc4);
+ node5.setLink(2, loc3);
+ node5.setSide(1 - i);
+ if (loc0 != NullTupLoc) {
+ jam();
+ NodeHandle node0(frag);
+ selectNode(node0, loc0);
+ node0.setLink(side5, loc3);
+ } else {
+ jam();
+ frag.m_tree.m_root = loc3;
+ }//if
+ /* The final step of the change is to update the balance of 3 and
+ 5 that changed places. There are two cases here. The first case is
+ when 3 unbalanced in the same direction by an insert or a delete.
+ In this case the changes will make the tree balanced again for both
+ 3 and 5.
+ The second case only occurs at deletes. In this case 3 starts out
+ balanced. In the figure above this could occur if 4 starts out with
+ a right node and the rotate is triggered by a delete of 6's only child.
+ In this case 5 will change balance but still be unbalanced and 3 will
+ be unbalanced in the opposite direction of 5.
+ */
+ if (bal3 == bal5) {
+ jam();
+ node3.setBalance(0);
+ node5.setBalance(0);
+ } else if (bal3 == 0) {
+ jam();
+ node3.setBalance(-bal5);
+ node5.setBalance(bal5);
+ } else {
+ ndbrequire(false);
+ }//if
+ /*
+ Set node to 3 as return parameter for enabling caller to continue
+ traversing the tree.
+ */
+ node = node3;
+}
+
+/*
+ * Double rotation about node 6. One of LR (i=0) or RL (i=1).
+ *
+ * 0 0
+ * | |
+ * 6 ==> 4
+ * / \ / \
+ * 2 7 2 6
+ * / \ / \ / \
+ * 1 4 1 3 5 7
+ * / \
+ * 3 5
+ *
+ * In this change 6, 2 and 4 must be there, all others are optional.
+ * We will start by proving a Lemma.
+ * Lemma:
+ * The height of the sub-trees 1 and 7 and the maximum height of the
+ * threes from 3 and 5 are all the same.
+ * Proof:
+ * maxheight(3,5) is defined as the maximum height of 3 and 5.
+ * If height(7) > maxheight(3,5) then the AVL condition is ok and we
+ * don't need to perform a rotation.
+ * If height(7) < maxheight(3,5) then the balance of 6 would be at least
+ * -3 which cannot happen in an AVL tree even before a rotation.
+ * Thus we conclude that height(7) == maxheight(3,5)
+ *
+ * The next step is to prove that the height of 1 is equal to maxheight(3,5).
+ * If height(1) - 1 > maxheight(3,5) then we would have
+ * balance in 6 equal to -3 at least which cannot happen in an AVL-tree.
+ * If height(1) - 1 = maxheight(3,5) then we should have solved the
+ * unbalance with a single rotate and not with a double rotate.
+ * If height(1) + 1 = maxheight(3,5) then we would be doing a rotate
+ * with node 2 as the root of the rotation.
+ * If height(1) + k = maxheight(3,5) where k >= 2 then the tree could not have
+ * been an AVL-tree before the insert or delete.
+ * Thus we conclude that height(1) = maxheight(3,5)
+ *
+ * Thus we conclude that height(1) = maxheight(3,5) = height(7).
+ *
+ * Observation:
+ * The balance of node 4 before the rotation can be any (-1, 0, +1).
+ *
+ * The following changes are needed:
+ * Node 6:
+ * 1) Changes parent from 0 -> 4
+ * 2) 1 - i link stays the same
+ * 3) i side link is derived from 1 - i side link from 4
+ * 4) Side is set to 1 - i
+ * 5) Balance change:
+ * If balance(4) == 0 then balance(6) = 0
+ * since height(3) = height(5) = maxheight(3,5) = height(7)
+ * If balance(4) == +1 then balance(6) = 0
+ * since height(5) = maxheight(3,5) = height(7)
+ * If balance(4) == -1 then balance(6) = 1
+ * since height(5) + 1 = maxheight(3,5) = height(7)
+ *
+ * Node 2:
+ * 1) Changes parent from 6 -> 4
+ * 2) i side link stays the same
+ * 3) 1 - i side link is derived from i side link of 4
+ * 4) Side is set to i (thus not changed)
+ * 5) Balance change:
+ * If balance(4) == 0 then balance(2) = 0
+ * since height(3) = height(5) = maxheight(3,5) = height(1)
+ * If balance(4) == -1 then balance(2) = 0
+ * since height(3) = maxheight(3,5) = height(1)
+ * If balance(4) == +1 then balance(6) = 1
+ * since height(3) + 1 = maxheight(3,5) = height(1)
+ *
+ * Node 4:
+ * 1) Inherits parent from 6
+ * 2) i side link is 2
+ * 3) 1 - i side link is 6
+ * 4) Side is inherited from 6
+ * 5) Balance(4) = 0 independent of previous balance
+ * Proof:
+ * If height(1) = 0 then only 2, 4 and 6 are involved and then it is
+ * trivially true.
+ * If height(1) >= 1 then we are sure that 1 and 7 exist with the same
+ * height and that if 3 and 5 exist they are of the same height as 1 and
+ * 7 and thus we know that 4 is balanced since newheight(2) = newheight(6).
+ *
+ * If Node 3 exists:
+ * 1) Change parent from 4 to 2
+ * 2) Change side from i to 1 - i
+ *
+ * If Node 5 exists:
+ * 1) Change parent from 4 to 6
+ * 2) Change side from 1 - i to i
+ *
+ * If Node 0 exists:
+ * 1) previous link to 6 is replaced by link to 4 on proper side
+ *
+ * Node 1 and 7 needs no changes at all.
+ *
+ * Some additional requires are that balance(2) = - balance(6) = -1/+1 since
+ * otherwise we would do a single rotate.
+ *
+ * The balance(6) is -1 if i == 0 and 1 if i == 1
+ *
+ */
+void
+Dbtux::treeRotateDouble(Frag& frag, NodeHandle& node, unsigned i)
+{
+ TreeHead& tree = frag.m_tree;
+
+ // old top node
+ NodeHandle node6 = node;
+ const TupLoc loc6 = node6.m_loc;
+ // the un-updated balance
+ const int bal6 = node6.getBalance();
+ const unsigned side6 = node6.getSide();
+
+ // level 1
+ TupLoc loc2 = node6.getLink(i);
+ NodeHandle node2(frag);
+ selectNode(node2, loc2);
+ const int bal2 = node2.getBalance();
+
+ // level 2
+ TupLoc loc4 = node2.getLink(1 - i);
+ NodeHandle node4(frag);
+ selectNode(node4, loc4);
+ const int bal4 = node4.getBalance();
+
+ ndbrequire(i <= 1);
+ ndbrequire(bal6 + (1 - i) == i);
+ ndbrequire(bal2 == -bal6);
+ ndbrequire(node2.getLink(2) == loc6);
+ ndbrequire(node2.getSide() == i);
+ ndbrequire(node4.getLink(2) == loc2);
+
+ // level 3
+ TupLoc loc3 = node4.getLink(i);
+ TupLoc loc5 = node4.getLink(1 - i);
+
+ // fill up leaf before it becomes internal
+ if (loc3 == NullTupLoc && loc5 == NullTupLoc) {
+ jam();
+ if (node4.getOccup() < tree.m_minOccup) {
+ jam();
+ unsigned cnt = tree.m_minOccup - node4.getOccup();
+ ndbrequire(cnt < node2.getOccup());
+ nodeSlide(node4, node2, cnt, i);
+ ndbrequire(node4.getOccup() >= tree.m_minOccup);
+ ndbrequire(node2.getOccup() != 0);
+ }
+ } else {
+ if (loc3 != NullTupLoc) {
+ jam();
+ NodeHandle node3(frag);
+ selectNode(node3, loc3);
+ node3.setLink(2, loc2);
+ node3.setSide(1 - i);
+ }
+ if (loc5 != NullTupLoc) {
+ jam();
+ NodeHandle node5(frag);
+ selectNode(node5, loc5);
+ node5.setLink(2, node6.m_loc);
+ node5.setSide(i);
+ }
+ }
+ // parent
+ TupLoc loc0 = node6.getLink(2);
+ NodeHandle node0(frag);
+ // perform the rotation
+ node6.setLink(i, loc5);
+ node6.setLink(2, loc4);
+ node6.setSide(1 - i);
+
+ node2.setLink(1 - i, loc3);
+ node2.setLink(2, loc4);
+
+ node4.setLink(i, loc2);
+ node4.setLink(1 - i, loc6);
+ node4.setLink(2, loc0);
+ node4.setSide(side6);
+
+ if (loc0 != NullTupLoc) {
+ jam();
+ selectNode(node0, loc0);
+ node0.setLink(side6, loc4);
+ } else {
+ jam();
+ frag.m_tree.m_root = loc4;
+ }
+ // set balance of changed nodes
+ node4.setBalance(0);
+ if (bal4 == 0) {
+ jam();
+ node2.setBalance(0);
+ node6.setBalance(0);
+ } else if (bal4 == -bal2) {
+ jam();
+ node2.setBalance(0);
+ node6.setBalance(bal2);
+ } else if (bal4 == bal2) {
+ jam();
+ node2.setBalance(-bal2);
+ node6.setBalance(0);
+ } else {
+ ndbrequire(false);
+ }
+ // new top node
+ node = node4;
+}