// Grows child 'i' to make sure it's possible to remove an // item from it while keeping it at minItems, then calls remove to actually // remove it // // Most documentation says we have to do two sets of special casing: // 1) item is in this node // 2) item is in child // In both cases, we need to handle the two subcases: // A) node has enough values that it can spare one // B) node doesn't have enough values // For the latter, we have to check: // a) left sibling has node to spare // b) right sibling has node to spare // c) we must merge // To simplify our code here, we handle cases #1 and #2 the same: // If a node doesn't have enough items, we make sure it does (using a,b,c). // We then simply redo our remove call, and the second time (regardless of // whether we're in case 1 or 2), we'll have enough items and can guarantee // that we hit case A. public T GrowChildAndRemove(int i, T item, int minItems, ToRemove typ) { if (i > 0 && Children[i - 1].Items.Length > minItems) { // Steal from left child Node <T> child = MutableChild(i); Node <T> stealFrom = MutableChild(i - 1); T stolenItem = stealFrom.Items.Pop(); child.Items.InsertAt(0, Items[i - 1]); Items[i - 1] = stolenItem; if (stealFrom.Children.Length > 0) { child.Children.InsertAt(0, stealFrom.Children.Pop()); } } else if (i < Items.Length && Children[i + 1].Items.Length > minItems) { // steal from right child Node <T> child = MutableChild(i); Node <T> stealFrom = MutableChild(i + 1); T stolenItem = stealFrom.Items.RemoveAt(0); child.Items.Append(Items[i]); Items[i] = stolenItem; if (stealFrom.Children.Length > 0) { child.Children.Append(stealFrom.Children.RemoveAt(0)); } } else { if (i >= Items.Length) { i--; } Node <T> child = MutableChild(i); // merge with right child T mergeItem = Items.RemoveAt(i); Node <T> mergeChild = Children.RemoveAt(i + 1); child.Items.Append(mergeItem); child.Items.Append(mergeChild.Items); child.Children.Append(mergeChild.Children); _ = Cow.FreeNode(mergeChild); } return(Remove(item, minItems, typ)); }