ouija_bh

B-Tree with better performance

Aug 3rd, 2025 (edited)
159
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 34.71 KB | Source Code | 0 0
  1. /* An implementation of B-Tree. It has tree methods add, remove, findGE,
  2.  * findLT, iteratorGE, @@iterator, size, clear, and toString. Ported from
  3.  * Open Data Structures by Pat Morin. Includes a short demonstration of
  4.  * those methods.
  5.  * This script includes the performance improvements described in Morin
  6.  * Exercise 14..7. Search for ##perf## to skip to the relevant additions
  7.  * and modifications.
  8.  */
  9.  
  10. /** A list of blocks used to simulate external memory storage. credit: Morin BlockStore.java. */
  11. class BlockStore {
  12.   constructor() {
  13.     this.blocks = new Array();  // array of BTNode's
  14.     this.free = new Array();    // array of indexes of available blocks
  15.   }
  16.  
  17.   /** placeBlock() Allocate a new block and return its index.
  18.    * @param {BTNode} block the new block
  19.    * @returns {number} the index of the newly allocated block
  20.    */
  21.   placeBlock(block) {
  22.     var ndx;
  23.     if (this.free.length > 0) { ndx = this.free.pop(); }
  24.     else { ndx = this.blocks.length; }
  25.     this.blocks[ndx] = block;
  26.     return ndx;
  27.   }
  28.  
  29.   /** freeBlock() Free a block, adding its index to the free list.
  30.    * @param {number} ndx the block index to free
  31.    */
  32.   freeBlock(ndx) {
  33.     this.blocks[ndx] = undefined;
  34.     this.free.push(ndx);
  35.   }
  36.  
  37.   /** readBlock() Read a block.
  38.    * @param {number} ndx the index of the block to read
  39.    * @returns {BTNode} the block
  40.    */
  41.   readBlock(ndx) {
  42.     return this.blocks[ndx];
  43.   }
  44.  
  45.   /** writeBlock() Write a block.
  46.    * @param {number} ndx the index of the block
  47.    * @param {BTNode} block the block
  48.    */
  49.   writeBlock(ndx, block) {
  50.     this.blocks[ndx] = block;
  51.   }
  52.  
  53.   clear() {
  54.     this.blocks.length = 0;
  55.     this.free.length = 0;
  56.   }
  57. }
  58.  
  59. /** Properties of a BTree. */
  60. class BTProps {
  61.   bkSz;     // block size
  62.   hfBS;     // half of block size
  63.   bkSr;     // block store
  64.   rootNdx;  // index of the root node
  65.   numEl;    // number of elements stored in the tree
  66.   compare;  // comparator function
  67. }
  68.  
  69. /** findIt() Find the index at which a value should be inserted into an
  70.  *  undefined-padded sorted array. credit: Morin BTree.java.
  71.  * @param {Array} array the sorted array (padded with undefined entries)
  72.  * @param {any} value the value to search for
  73.  * @param {function} comparator function taking (any, any) and returning -1, 0, or 1
  74.  * @returns index or (-index - 1) if array[index] equals value
  75.  */
  76. function findIt(array, value, comparator) {
  77.   var lo = 0, hi = array.length;
  78.   while (hi != lo) {
  79.     var mid = Math.floor((hi + lo) / 2);
  80.     var cmp;
  81.     if (array[mid] == undefined) { cmp = -1; }
  82.     else { cmp = comparator(value, array[mid]); }
  83.     if (cmp < 0) {
  84.       hi = mid;      // look in first half
  85.     } else if (cmp > 0) {
  86.       lo = mid + 1;    // look in second half
  87.     } else {
  88.       return -mid - 1; // found it
  89.     }
  90.   }
  91.   return lo;
  92. }
  93.  
  94. /** A node in a BTree, or a block in a BlockStore. Has an array of up to bkSz
  95.  *  keys and up to (bkSz + 1) children. credit: Morin BTree.java Node.
  96.  */
  97. class BTNode {
  98.   index;    // this block's index
  99.   keys;     // array of any
  100.   children; // array of indexes of the children of this block (if any)
  101.   bp;   // BTProps
  102.   /** constructor()
  103.    * @param {BTProps} bp properties of the BTree
  104.    */
  105.   constructor(bp) {
  106.     this.bp = bp;
  107.     this.keys = new Array(this.bp.bkSz);
  108.     this.children = new Array(this.bp.bkSz + 1);
  109.     this.children.fill(-1);
  110.     this.index = bp.bkSr.placeBlock(this);
  111.   }
  112.  
  113.   /** isLeaf()
  114.    * @returns {boolean}
  115.    */
  116.   isLeaf() {
  117.     return this.children[0] < 0;
  118.   }
  119.  
  120.   /** isFull() Test if this block is full (contains bkSz keys).
  121.    * @returns {boolean} true if the block is full
  122.    */
  123.   isFull() {
  124.     return this.keys[this.keys.length - 1] != undefined;
  125.   }
  126.  
  127.   /** size() Count the number of keys in this block, using binary search.
  128.    * @returns {number} the number of keys in this block
  129.    */
  130.   size() {
  131.     var lo = 0, hi = this.keys.length;
  132.     while (hi != lo) {
  133.       var mid = Math.floor((hi + lo) / 2);
  134.       if (this.keys[mid] == undefined) { hi = mid; }
  135.       else { lo = mid + 1; }
  136.     }
  137.     return lo;
  138.   }
  139.  
  140.   /** add() Add a value to this block.
  141.    * @param {any} value the value to add
  142.    * @param {number} cIndex the index of the child associated with value
  143.    * @param {boolean} cFlag true if cIndex of this key, false if cIndex of next key ##perf##
  144.    * @returns {boolean} true on success or false if value was not added
  145.    */
  146.   add(value, cIndex, cFlag) {
  147.     var i = findIt(this.keys, value, this.bp.compare);
  148.     if (i < 0) { return false; }
  149.     if (i < this.keys.length - 1) {
  150.       this.keys = this.keys.copyWithin(i + 1, i, this.bp.bkSz - 1);
  151.     }
  152.     this.keys[i] = value;
  153.     if (i < this.keys.length - 1) {
  154.       if (cFlag) {
  155.         this.children = this.children.copyWithin(i + 1, i, this.bp.bkSz);
  156.       } else {
  157.         this.children = this.children.copyWithin(i + 2, i + 1, this.bp.bkSz);
  158.       }
  159.     }
  160.     if (cFlag) { this.children[i] = cIndex; }
  161.     else { this.children[i + 1] = cIndex; }
  162.     return true;
  163.   }
  164.  
  165.   /** remove() Remove value at given index from this block. Does not affect
  166.    *  this block's children.
  167.    * @param {number} index the index of the element to remove
  168.    * @returns {any} the value of the element removed
  169.    */
  170.   remove(index) {
  171.     var value = this.keys[index];
  172.     this.keys = this.keys.copyWithin(index, index + 1, this.keys.length);
  173.     this.keys[this.keys.length - 1] = undefined;
  174.     return value;
  175.   }
  176.  
  177.   /** split() Split this node into two nodes.
  178.    * @returns {BTNode} the newly created block, which has the larger keys
  179.    */
  180.   split() {
  181.     var block = new BTNode(this.bp);
  182.     var mid = this.bp.hfBS;
  183.     var larger = this.keys.slice(mid);
  184.     block.keys.splice(0, larger.length, ...larger);
  185.     this.keys.fill(undefined, mid);
  186.     larger = this.children.slice(mid + 1);
  187.     block.children.splice(0, larger.length, ...larger);
  188.     this.children.fill(-1, mid + 1);
  189.     this.bp.bkSr.writeBlock(this.index, this);
  190.     return block;
  191.   }
  192.  
  193.   /** toString()
  194.    * @returns {string} this node as a string
  195.    */
  196.   toString() {
  197.     var str = "[" + this.index + "][";
  198.     for (var i = 0; i < this.bp.bkSz; i++) {
  199.       str += "(";
  200.       if (this.children[i] < 0) { str += "."; }
  201.       else { str += this.children[i]; }
  202.       str += ")";
  203.       if (this.keys[i] == undefined) { str += "_"; }
  204.       else { str += this.keys[i]; }
  205.     }
  206.     str += "(";
  207.     if (this.children[this.bp.bkSz] < 0) { str += "."; }
  208.     else { str += this.children[this.bp.bkSz]; }
  209.     str += ")]";
  210.     return str;
  211.   }
  212. }
  213.  
  214. /** Iterator of BTree. credit: Morin BTree.java. */
  215. class BTIterator {
  216.   #nodeSk;   // stack of BTNode's
  217.   #indexSk;  // stack of indexes
  218.   bp;   // BTProps
  219.   /** #iterator() Iterator will contain all elements. */
  220.   #iterator() {
  221.     var cur = this.bp.rootNdx;
  222.     do {
  223.       var block = this.bp.bkSr.readBlock(cur);
  224.       this.#nodeSk.push(block);
  225.       this.#indexSk.push(0);
  226.       cur = block.children[0];
  227.     } while (cur >= 0);
  228.   }
  229.  
  230.   #advance() {
  231.     var block = this.#nodeSk[this.#nodeSk.length - 1];
  232.     var i = this.#indexSk[this.#indexSk.length - 1];
  233.     if (block.isLeaf()) { // this is a leaf, walk up
  234.       while (this.#nodeSk.length > 0 && i == block.size()) {
  235.         this.#nodeSk.pop();
  236.         this.#indexSk.pop();
  237.         if (this.#nodeSk.length > 0) {
  238.           block = this.#nodeSk[this.#nodeSk.length - 1];
  239.           i = this.#indexSk[this.#indexSk.length - 1];
  240.         }
  241.       }
  242.     } else { // this is an internal node, walk down
  243.       var cur = block.children[i];
  244.       do {
  245.         block = this.bp.bkSr.readBlock(cur);
  246.         this.#nodeSk.push(block);
  247.         this.#indexSk.push(0);
  248.         cur = block.children[0];
  249.       } while (cur >= 0);
  250.     }
  251.   }
  252.  
  253.   /** constructor() Create an iterator starting with the smallest element in the
  254.    *  tree that is greater than or equal to value. If value is undefined, create
  255.    *  an iterator starting with the smallest element in the tree.
  256.    * @param {BTProps} bp properties of the BTree
  257.    * @param {any} value the value
  258.    */
  259.   constructor(bp, value) {
  260.     this.#nodeSk = new Array();
  261.     this.#indexSk = new Array();
  262.     this.bp = bp;
  263.    
  264.     if (this.bp.numEl == 0) { return; }
  265.     if (value == undefined) {
  266.       this.#iterator();
  267.     } else {
  268.       var block, i, cur = this.bp.rootNdx;
  269.       do {
  270.         block = this.bp.bkSr.readBlock(cur);
  271.         i = findIt(block.keys, value, this.bp.compare);
  272.         this.#nodeSk.push(block);
  273.         if (i < 0) {  // found the value, add its index and stop
  274.           this.#indexSk.push(-(i + 1));
  275.           return;
  276.         }
  277.         this.#indexSk.push(i);  // add index of smallest value
  278.         cur = block.children[i];
  279.       } while (cur >= 0);
  280.       if (i == block.size()) { this.#advance(); }
  281.     }
  282.   }
  283.  
  284.   /** hasNext()
  285.    * @returns {boolean}
  286.    */
  287.   hasNext() {
  288.     return this.#nodeSk.length > 0;
  289.   }
  290.  
  291.   /** next()
  292.    * @returns {any}
  293.    */
  294.   next() {
  295.     var block = this.#nodeSk[this.#nodeSk.length - 1];
  296.     var i = this.#indexSk[this.#indexSk.length - 1];
  297.     var value = block.keys[i++];
  298.     this.#indexSk[this.#indexSk.length - 1] = i;
  299.     this.#advance();
  300.     return value;
  301.   }
  302. }
  303.  
  304. /** A simple string buffer. */
  305. class StringBuffer {
  306.   str = "";
  307. }
  308.  
  309. /** A sorted set. credit: Morin SSet.java, BTree.java. */
  310. class BTree {
  311.   /** constructor()
  312.    * @param {number} blockSize maximum number of keys of a node
  313.    * @param {function} comparator function taking (any, any) and returning -1, 0, or 1
  314.    */
  315.   constructor(blockSize, comparator) {
  316.     this.bp = new BTProps();
  317.     blockSize += 1 - (blockSize % 2); // an odd number
  318.     this.bp.bkSz = blockSize;
  319.     this.bp.hfBS = Math.floor(blockSize / 2);
  320.     this.bp.bkSr = new BlockStore();
  321.     this.bp.numEl = 0;
  322.     this.bp.rootNdx = (new BTNode(this.bp)).index;
  323.     this.bp.compare = comparator;
  324.   }
  325.  
  326.   /** #redist() A new node will be created and nodes H and J will be redistributed. ##perf##
  327.    * @param {BTNode} block the parent of H and J
  328.    * @param {number} h the index h in block.children
  329.    * @param {BTNode} bkH the left sibling of J
  330.    * @param {BTNode} bkJ the right sibling of H
  331.    * @param {number} sizeJ size of node J
  332.    */
  333.   #redist(block, h, bkH, bkJ, sizeJ) {
  334.     var bkI = new BTNode(this.bp);
  335.     var sizeH = bkH.size(), sizeI = 0;
  336.     var numH = sizeH, numJ = sizeJ, shift;
  337.     // calculate the numbers of keys to shift out of H and out of J
  338.     if (numH > numJ) {  // H to I
  339.       shift = Math.floor(numH / 2);
  340.       numH -= shift;
  341.       if (shift - numJ > 1) { // I to J
  342.         numJ += Math.floor((numJ + shift) / 2) - numJ;
  343.       } else if (numJ - shift > 1) {  // J to I
  344.         numJ -= Math.floor((shift + numJ) / 2) - shift;
  345.       }
  346.     } else {  // J to I
  347.       shift = Math.floor(numJ / 2);
  348.       numJ -= shift;
  349.       if (shift - numH > 1) {  // I to H
  350.         numH += Math.floor((numH + shift) / 2) - numH;
  351.       } else if (numH - shift > 1) { // H to I
  352.         numH -= Math.floor((shift + numH) / 2) - shift;
  353.       }
  354.     }
  355.     var sftH = sizeH - numH, sftJ = sizeJ - numJ;
  356.     if (sftH > 0) {
  357.       // move keys and children out of H and into I (and parent)
  358.       bkI.keys[sftH - 1] = block.keys[h];
  359.       block.keys[h] = bkH.keys[sizeH - sftH];
  360.       var larger = bkH.keys.slice(sizeH - sftH + 1, sizeH);
  361.       bkI.keys.splice(0, larger.length, ...larger);
  362.       bkH.keys.fill(undefined, sizeH - sftH, sizeH);
  363.       larger = bkH.children.slice(sizeH - sftH + 1, sizeH + 1);
  364.       bkI.children.splice(0, larger.length, ...larger);
  365.       bkH.children.fill(-1, sizeH - sftH + 1, sizeH + 1);
  366.       // update size I
  367.       sizeI += sftH;
  368.     }
  369.     if (sftJ > 0) {
  370.       // shift keys and children from J to I
  371.       var smaller = bkJ.keys.slice(0, sftJ - 1);
  372.       bkI.keys.splice(sizeI, smaller.length, ...smaller);
  373.       smaller = bkJ.children.slice(0, sftJ);
  374.       bkI.children.splice(sizeI, smaller.length, ...smaller);
  375.       var value = bkJ.keys[sftJ - 1];
  376.       // delete keys and children from J
  377.       bkJ.keys = bkJ.keys.copyWithin(0, sftJ, this.bp.bkSz);
  378.       bkJ.keys.fill(undefined, sizeJ - sftJ, this.bp.bkSz);
  379.       bkJ.children = bkJ.children.copyWithin(0, sftJ, this.bp.bkSz + 1);
  380.       bkJ.children.fill(-1, sizeJ - sftJ + 1, this.bp.bkSz + 1);
  381.       // add node I to parent
  382.       block.add(value, bkI.index, true);
  383.     } else {
  384.       // add node I to parent
  385.       block.add(bkI.remove(sizeI - 1), bkI.index, true);
  386.     }
  387.     this.bp.bkSr.writeBlock(bkI.index, bkI);
  388.   }
  389.  
  390.   /** #addRecursive() Use recursion to add a value to the i'th child of block,
  391.    *  or recursively add a value to root if block is undefined. ##perf##
  392.    *  If that node is split by this operation then the return value is the
  393.    *  BTNode that was created when node was split.
  394.    * @param {any} value the element to add
  395.    * @param {BTNode} block the parent of node I, or undefined to start at root
  396.    * @param {number} i the index i in block.children
  397.    * @returns {BTNode} a new node that was created when node was split, or
  398.    *            undefined if node was not split
  399.    */
  400.   #addRecursive(value, block, i) {
  401.     var bkI;  // BTNode
  402.     if (block == undefined) { // root
  403.       bkI = this.bp.bkSr.readBlock(this.bp.rootNdx);
  404.     } else {  // non-root
  405.       bkI = this.bp.bkSr.readBlock(block.children[i]);
  406.     }
  407.     var k = findIt(bkI.keys, value, this.bp.compare);
  408.     if (k < 0) { throw new Error("duplicate value"); }
  409.     if (bkI.children[k] < 0) { // leaf node, just add it
  410.       bkI.add(value, -1, false);
  411.       this.bp.bkSr.writeBlock(bkI.index, bkI);
  412.     } else {
  413.       var newBk = this.#addRecursive(value, bkI, k);
  414.       if (newBk != undefined) {  // child was split, newBk is new child
  415.         value = newBk.remove(0);
  416.         this.bp.bkSr.writeBlock(newBk.index, newBk);
  417.         bkI.add(value, newBk.index, false);
  418.         this.bp.bkSr.writeBlock(bkI.index, bkI);
  419.       }
  420.     }
  421.     if (bkI.isFull() && block != undefined) { // overfull non-root node
  422.       if (block.children[i + 1] >= 0) {  // look right
  423.         var bkJ = this.bp.bkSr.readBlock(block.children[i + 1]);
  424.         var sizeJ = bkJ.size();
  425.         if (sizeJ <= this.bp.hfBS + 1) {  // J can borrow from I
  426.           this.#shiftLtoR(block, i, bkI, bkJ);
  427.         } else if (block.size() < this.bp.bkSz - 1) {  // redist. with new node
  428.           this.#redist(block, i, bkI, bkJ, sizeJ);
  429.         } else {
  430.           return bkI.split();
  431.         }
  432.       } else {
  433.         return bkI.split();
  434.       }
  435.     } else if (bkI.isFull()) {  // overfull root node
  436.       return bkI.split();
  437.     }
  438.     return undefined;
  439.   }
  440.  
  441.   /** add() Add a value to this tree.
  442.    * @param {any} value the element to add
  443.    * @returns {boolean} true if value was added, or false if value was already
  444.    *            in the set or value is undefined
  445.    */
  446.   add(value) {
  447.     if (value == undefined) {
  448.       console.error("BTree.add() undefined value");
  449.       return false;
  450.     }
  451.     var block;  // BTNode
  452.     try {
  453.       block = this.#addRecursive(value, undefined, -1); // ##perf##
  454.     } catch (err) {
  455.       console.error("BTree.add() " + err.message);
  456.       return false;
  457.     }
  458.     if (block != undefined) {   // root was split, make new root
  459.       var newRoot = new BTNode(this.bp);
  460.       value = block.remove(0);
  461.       this.bp.bkSr.writeBlock(block.index, block);
  462.       newRoot.children[0] = this.bp.rootNdx;
  463.       newRoot.keys[0] = value;
  464.       newRoot.children[1] = block.index;
  465.       this.bp.rootNdx = newRoot.index;
  466.       this.bp.bkSr.writeBlock(this.bp.rootNdx, newRoot);
  467.     }
  468.     this.bp.numEl++;
  469.     return true;
  470.   }
  471.  
  472.   /** #merge() Node H will absorb node I.
  473.    * @param {BTNode} block the parent of H and I
  474.    * @param {number} h the index h in block.children
  475.    * @param {BTNode} bkH the left sibling of I
  476.    * @param {BTNode} bkI the right sibling of H
  477.    */
  478.   #merge(block, h, bkH, bkI) {
  479.     if (bkH.index != block.children[h]) { throw new Error("assert: H mismatch"); }
  480.     if (bkI.index != block.children[h + 1]) { throw new Error("assert: I mismatch"); }
  481.     var sizeI = bkI.size();
  482.     var sizeH = bkH.size();
  483.     // copy keys from I to H
  484.     var smaller = bkI.keys.slice(0, sizeI);
  485.     bkH.keys.splice(sizeH + 1, smaller.length, ...smaller);
  486.     smaller = bkI.children.slice(0, sizeI + 1);
  487.     bkH.children.splice(sizeH + 1, smaller.length, ...smaller);
  488.     // add key to H and remove it from block
  489.     bkH.keys[sizeH] = block.keys[h];
  490.     block.keys = block.keys.copyWithin(h, h + 1, this.bp.bkSz);
  491.     block.keys[this.bp.bkSz - 1] = undefined;
  492.     block.children = block.children.copyWithin(h + 1, h + 2, this.bp.bkSz + 1);
  493.     block.children[this.bp.bkSz] = -1;
  494.     this.bp.bkSr.freeBlock(bkI.index);
  495.   }
  496.  
  497.   /** #redist2() Nodes H, I and J will be redistributed. ##perf##
  498.    * @param {BTNode} block the parent of H, I and J
  499.    * @param {number} i the index i in block.children
  500.    * @param {BTNode} bkH the left sibling of I
  501.    * @param {BTNode} bkI the right sibling of H and left sibling of J
  502.    * @param {BTNode} bkJ the right sibling of I
  503.    * @param {number} sizeH size of node H
  504.    * @param {number} sizeJ size of node J
  505.    */
  506.   #redist2(block, i, bkH, bkI, bkJ, sizeH, sizeJ) {
  507.     if (sizeH > sizeJ) {  // H to I
  508.       this.#shiftLtoR(block, i - 1, bkH, bkI);
  509.       var sizeI = bkI.size();
  510.       if (sizeI - sizeJ > 1) { // I to J
  511.         this.#shiftLtoR(block, i, bkI, bkJ);
  512.       } else if (sizeJ - sizeI > 1) {  // J to I
  513.         this.#shiftRtoL(block, i, bkJ, bkI);
  514.       }
  515.     } else {  // J to I
  516.       this.#shiftRtoL(block, i, bkJ, bkI);
  517.       var sizeI = bkI.size();
  518.       if (sizeI - sizeH > 1) {  // I to H
  519.         this.#shiftRtoL(block, i - 1, bkI, bkH);
  520.       } else if (sizeH - sizeI > 1) { // H to I
  521.         this.#shiftLtoR(block, i - 1, bkH, bkI);
  522.       }
  523.     }
  524.   }
  525.  
  526.   /** #merge2() Nodes H and/or J will absorb node I. ##perf##
  527.    * @param {BTNode} block the parent of H, I and J
  528.    * @param {number} i the index i in block.children
  529.    * @param {BTNode} bkH the left sibling of I
  530.    * @param {BTNode} bkI the right sibling of H and left sibling of J
  531.    * @param {BTNode} bkJ the right sibling of I
  532.    * @param {number} sizeH size of node H
  533.    * @param {number} sizeI size of node I
  534.    * @param {number} sizeJ size of node J
  535.    */
  536.   #merge2(block, i, bkH, bkI, bkJ, sizeH, sizeI, sizeJ) {
  537.     var numH = sizeH, numI = sizeI + 1, numJ = sizeJ;
  538.     // calculate the numbers of keys to shift to H and to J
  539.     var shift = Math.abs(numH - numJ);
  540.     if (shift > 0) {
  541.       if (shift > numI) { shift = numI; }
  542.       if (numH < numJ) { numH += shift; }
  543.       else { numJ += shift; }
  544.       numI -= shift;
  545.     }
  546.     if (numI > 0) {
  547.       shift = Math.floor(numI / 2);
  548.       numH += shift;
  549.       numJ += shift;
  550.       if (numI % 2 == 1) {
  551.         if (numH < numJ) { numH++; }
  552.         else { numJ++; }
  553.       }
  554.     }
  555.     var sftH = numH - sizeH, sftJ = numJ - sizeJ;
  556.     if (sftJ > 0) {
  557.       // make space for new keys in J
  558.       bkJ.keys = bkJ.keys.copyWithin(sftJ, 0, sizeJ);
  559.       bkJ.children = bkJ.children.copyWithin(sftJ, 0, sizeJ + 1);
  560.       // move keys and children out of I and into J (and parent)
  561.       bkJ.keys[sftJ - 1] = block.keys[i];
  562.       block.keys[i] = bkI.keys[sizeI - sftJ];
  563.       var larger = bkI.keys.slice(sizeI - sftJ + 1, sizeI);
  564.       bkJ.keys.splice(0, larger.length, ...larger);
  565.       bkI.keys.fill(undefined, sizeI - sftJ, sizeI);
  566.       larger = bkI.children.slice(sizeI - sftJ + 1, sizeI + 1);
  567.       bkJ.children.splice(0, larger.length, ...larger);
  568.       bkI.children.fill(-1, sizeI - sftJ + 1, sizeI + 1);
  569.       if (sftH > 0) {  // H will absorb I
  570.         this.#merge(block, i - 1, bkH, bkI);
  571.       } else {  // remove node I from block
  572.         block.keys = block.keys.copyWithin(i, i + 1, this.bp.bkSz);
  573.         block.keys[this.bp.bkSz - 1] = undefined;
  574.         block.children = block.children.copyWithin(i + 1, i + 2, this.bp.bkSz + 1);
  575.         block.children[this.bp.bkSz] = -1;
  576.         this.bp.bkSr.freeBlock(bkI.index);
  577.       }
  578.     } else {  // H will absorb I
  579.       this.#merge(block, i - 1, bkH, bkI);
  580.     }
  581.   }
  582.  
  583.   /** #shiftRtoL() Shift keys from node J into node I.
  584.    * @param {BTNode} block the parent of J and I
  585.    * @param {number} i the index i in block.children
  586.    * @param {BTNode} bkJ the right sibling of I
  587.    * @param {BTNode} bkI the left sibling of J
  588.    */
  589.   #shiftRtoL(block, i, bkJ, bkI) {
  590.     var sizeI = bkI.size();
  591.     var sizeJ = bkJ.size();
  592.     var shift = Math.floor((sizeI + sizeJ) / 2) - sizeI;  // num. keys to shift from J to I
  593.     // shift keys and children from J to I
  594.     bkI.keys[sizeI] = block.keys[i];
  595.     var smaller = bkJ.keys.slice(0, shift - 1);
  596.     bkI.keys.splice(sizeI + 1, smaller.length, ...smaller);
  597.     smaller = bkJ.children.slice(0, shift);
  598.     bkI.children.splice(sizeI + 1, smaller.length, ...smaller);
  599.     block.keys[i] = bkJ.keys[shift - 1];
  600.     // delete keys and children from J
  601.     bkJ.keys = bkJ.keys.copyWithin(0, shift, this.bp.bkSz);
  602.     bkJ.keys.fill(undefined, sizeJ - shift, this.bp.bkSz);
  603.     bkJ.children = bkJ.children.copyWithin(0, shift, this.bp.bkSz + 1);
  604.     bkJ.children.fill(-1, sizeJ - shift + 1, this.bp.bkSz + 1);
  605.   }
  606.  
  607.   /** #checkUnderflowZero() Check if an underflow has occured in the i'th
  608.    *  child of block and, if so, fix it.
  609.    * @param {BTNode} block block to check
  610.    * @param {number} i the index i in block.children
  611.    */
  612.   #checkUnderflowZero(block, i) {
  613.     var bkI = this.bp.bkSr.readBlock(block.children[i]);  // i'th child of block
  614.     if (bkI.size() < this.bp.hfBS - 1) {  // underflow at I
  615.       var bkJ = this.bp.bkSr.readBlock(block.children[i + 1]); // J right of I
  616.       if (bkJ.size() > this.bp.hfBS) { // I can borrow from J
  617.         this.#shiftRtoL(block, i, bkJ, bkI);
  618.       } else { // I will absorb J
  619.         this.#merge(block, i, bkI, bkJ);
  620.         block.children[i] = bkI.index;
  621.       }
  622.     }
  623.   }
  624.  
  625.   /** #shiftLtoR() Shift keys from node H into node I.
  626.    * @param {BTNode} block the parent of H and I
  627.    * @param {number} h the index h in block.children
  628.    * @param {BTNode} bkH the left sibling of I
  629.    * @param {BTNode} bkI the right sibling of H
  630.    */
  631.   #shiftLtoR(block, h, bkH, bkI) {
  632.     var sizeI = bkI.size();
  633.     var sizeH = bkH.size();
  634.     var shift = Math.floor((sizeI + sizeH) / 2) - sizeI;  // num. keys to shift from H to I
  635.     // make space for new keys in I
  636.     bkI.keys = bkI.keys.copyWithin(shift, 0, sizeI);
  637.     bkI.children = bkI.children.copyWithin(shift, 0, sizeI + 1);
  638.     // move keys and children out of H and into I (and parent)
  639.     bkI.keys[shift - 1] = block.keys[h];
  640.     block.keys[h] = bkH.keys[sizeH - shift];
  641.     var larger = bkH.keys.slice(sizeH - shift + 1, sizeH);
  642.     bkI.keys.splice(0, larger.length, ...larger);
  643.     bkH.keys.fill(undefined, sizeH - shift, sizeH);
  644.     larger = bkH.children.slice(sizeH - shift + 1, sizeH + 1);
  645.     bkI.children.splice(0, larger.length, ...larger);
  646.     bkH.children.fill(-1, sizeH - shift + 1, sizeH + 1);
  647.   }
  648.  
  649.   /** #checkUnderflowNonZero() Check if an underflow has occured in the i'th
  650.    *  child of block and, if so, fix it.
  651.    * @param {BTNode} block block to check
  652.    * @param {number} i the index i in block.children
  653.    */
  654.   #checkUnderflowNonZero(block, i) {
  655.     var bkI = this.bp.bkSr.readBlock(block.children[i]);  // i'th child of block
  656.     if (bkI.size() < this.bp.hfBS - 1) {  // underflow at I
  657.       var bkH = this.bp.bkSr.readBlock(block.children[i - 1]); // H left of I
  658.       if (block.children[i + 1] >= 0) {  // look right ##perf##
  659.         var bkJ = this.bp.bkSr.readBlock(block.children[i + 1]);  // J right of I
  660.         var sizeH = bkH.size(), sizeI = bkI.size(), sizeJ = bkJ.size();
  661.         if (sizeH + sizeI + sizeJ >= 3 * this.bp.hfBS) {
  662.           this.#redist2(block, i, bkH, bkI, bkJ, sizeH, sizeJ);
  663.         } else {
  664.           this.#merge2(block, i, bkH, bkI, bkJ, sizeH, sizeI, sizeJ);
  665.         }
  666.       } else if (bkH.size() > this.bp.hfBS) {  // I can borrow from H
  667.         this.#shiftLtoR(block, i - 1, bkH, bkI);
  668.       } else {  // H will absorb I
  669.         this.#merge(block, i - 1, bkH, bkI);
  670.       }
  671.     }
  672.   }
  673.  
  674.   /** #checkUnderflow() Check if an underflow has occurred in the i'th child
  675.    *  of block and, if so, fix it by borrowing from or merging with a sibling.
  676.    * @param {BTNode} block block to check
  677.    * @param {number} i the index i in block.children
  678.    */
  679.   #checkUnderflow(block, i) {
  680.     if (block.children[i] < 0) { return; }
  681.     if (i == 0) {
  682.       this.#checkUnderflowZero(block, i); // use block's right sibling
  683.     } else {
  684.       this.#checkUnderflowNonZero(block, i);
  685.     }
  686.   }
  687.  
  688.   /** #removeSmallest() Remove the smallest value in the subtree rooted at the
  689.    *  node with index.
  690.    * @param {number} index the index of a subtree
  691.    * @returns {any} the value that was removed
  692.    */
  693.   #removeSmallest(index) {
  694.     var block = this.bp.bkSr.readBlock(index);
  695.     if (block.isLeaf()) { return block.remove(0); }
  696.     var left = this.#removeSmallest(block.children[0]);
  697.     this.#checkUnderflow(block, 0);
  698.     return left;
  699.   }
  700.  
  701.   /** #removeRecursive() Remove the value from the subtree rooted at the node with index.
  702.    * @param {any} value the value to remove
  703.    * @param {number} index the index of the subtree to remove value from
  704.    * @returns {boolean} true if value was removed and false otherwise
  705.    */
  706.   #removeRecursive(value, index) {
  707.     if (index < 0) { return false; }  // didn't find it
  708.     var block = this.bp.bkSr.readBlock(index);
  709.     var i = findIt(block.keys, value, this.bp.compare);
  710.     if (i < 0) { // found it
  711.       i = -(i + 1);
  712.       if (block.isLeaf()) {
  713.         block.remove(i);
  714.       } else {
  715.         block.keys[i] = this.#removeSmallest(block.children[i + 1]);
  716.         this.#checkUnderflow(block, i + 1);
  717.       }
  718.       return true;
  719.     } else if (this.#removeRecursive(value, block.children[i])) {
  720.       this.#checkUnderflow(block, i);
  721.       return true;
  722.     }
  723.     return false;
  724.   }
  725.  
  726.   /** remove() Remove a value from this tree.
  727.    * @param {any} value the value
  728.    * @returns {boolean} true if value was removed and false if value
  729.    *            was not removed (because value was not present)
  730.    */
  731.   remove(value) {
  732.     if (this.#removeRecursive(value, this.bp.rootNdx)) {
  733.       this.bp.numEl--;
  734.       var rootBk = this.bp.bkSr.readBlock(this.bp.rootNdx);
  735.       if (rootBk.size() == 0 && this.bp.numEl > 0) { // root has only one child
  736.         this.bp.rootNdx = rootBk.children[0];
  737.       }
  738.       return true;
  739.     }
  740.     return false;
  741.   }
  742.  
  743.   /** findGE() Find the smallest element in the tree that is greater than or equal
  744.    *  to value. If value is undefined, return the smallest element in the tree.
  745.    * @param {any} value the value
  746.    * @returns {any} the smallest element in the tree that is greater than or equal to
  747.    *            value or undefined if no such element exists. If value is undefined
  748.    *            then the smallest element in the tree
  749.    */
  750.   findGE(value) {
  751.     var last = undefined;
  752.     var cur = this.bp.rootNdx;
  753.     while (cur >= 0) {
  754.       var block = this.bp.bkSr.readBlock(cur);
  755.       var i = findIt(block.keys, value, this.bp.compare);
  756.       if (i < 0) { return block.keys[-(i + 1)]; } // found it
  757.       if (block.keys[i] != undefined) { last = block.keys[i]; }
  758.       cur = block.children[i];
  759.     }
  760.     return last;
  761.   }
  762.  
  763.   /** findLT() Find the largest element in the tree that is less than value.
  764.    *  If value is undefined, return the smallest element in the tree.
  765.    * @param {any} value the value
  766.    * @returns {any} the largest element in the tree that is less than value. If
  767.    *          value is undefined then the smallest element in the tree
  768.    */
  769.   findLT(value) {
  770.     var last = undefined;
  771.     var cur = this.bp.rootNdx;
  772.     while (cur >= 0) {
  773.       var block = this.bp.bkSr.readBlock(cur);
  774.       var i = findIt(block.keys, value, this.bp.compare);
  775.       if (i < 0) { i = -(i + 1); }
  776.       if (i > 0) { last = block.keys[i - 1]; }
  777.       cur = block.children[i];
  778.     }
  779.     return last;
  780.   }
  781.  
  782.   /** comparator()
  783.    * @returns {function} the comparator of this tree's elements
  784.    */
  785.   comparator() {
  786.     return this.bp.compare;
  787.   }
  788.  
  789.   /** iteratorGE() Get an iterator of this tree starting with the smallest element
  790.    *  in the tree that is greater than or equal to value. If value is undefined,
  791.    *  returns iterator starting with the smallest element in the tree.
  792.    * @param {any} value the value
  793.    * @returns {BTIterator} iterator starting with the smallest element in the
  794.    *              tree that is greater than or equal to value. If value is undefined,
  795.    *              iterator starting with the smallest element in the tree.
  796.    */
  797.   iteratorGE(value) {
  798.     return new BTIterator(this.bp, value);
  799.   }
  800.  
  801.   /** @@iterator() Get an iterator of this tree. */
  802.   *[Symbol.iterator]() {
  803.     var iter = new BTIterator(this.bp, undefined);
  804.     while (iter.hasNext()) {
  805.       yield iter.next();
  806.     }
  807.   }
  808.  
  809.   /** size()
  810.    * @returns {number} the number of elements in this tree
  811.    */
  812.   size() {
  813.     return this.bp.numEl;
  814.   }
  815.  
  816.   /** clear() Remove all elements from this tree. */
  817.   clear() {
  818.     this.bp.numEl = 0;
  819.     this.bp.bkSr.clear();
  820.     this.bp.rootNdx = (new BTNode(this.bp)).index;
  821.   }
  822.  
  823.   /** #toString() Converts a tree to a string using recursion.
  824.    * @param {number} index index of block
  825.    * @param {StringBuffer} sBuf string buffer
  826.    */
  827.   #toString(index, sBuf) {
  828.     if (index < 0) { return; }
  829.     var block = this.bp.bkSr.readBlock(index);
  830.     var i = 0;
  831.     while(i < this.bp.bkSz && block.keys[i] != undefined) {
  832.       this.#toString(block.children[i], sBuf);
  833.       sBuf.str += block.keys[i++] + ", ";
  834.     }
  835.     this.#toString(block.children[i], sBuf);
  836.   }
  837.  
  838.   /** toString() Converts this tree to a string.
  839.    * @returns {string} this tree as a string
  840.    */
  841.   toString() {
  842.     if (this.bp.numEl == 0) { return ""; }
  843.     var sBuf = new StringBuffer();
  844.     this.#toString(this.bp.rootNdx, sBuf);
  845.     return sBuf.str.slice(0, sBuf.str.length - 2);
  846.   }
  847.  
  848.   /** toString2()
  849.    * @returns {string} in-order values formatted as array
  850.    */
  851.   toString2() {
  852.     return JSON.stringify([...this]);
  853.   }
  854.  
  855.   /** #toString3() Converts a tree to a string using recursion.
  856.    * @param {number} index index of block
  857.    * @param {number} depth depth of block
  858.    * @param {StringBuffer} sBuf string buffer
  859.    */
  860.   #toString3(index, depth, sBuf) {
  861.     if (index < 0) { return; }
  862.     var block = this.bp.bkSr.readBlock(index);
  863.     var i = 0;
  864.     while(i < this.bp.bkSz && block.keys[i] != undefined) {
  865.       this.#toString3(block.children[i], depth + 1, sBuf);
  866.       sBuf.str += '\n' + " ".repeat(depth + 1) + block.keys[i++];
  867.     }
  868.     this.#toString3(block.children[i], depth + 1, sBuf);
  869.   }
  870.  
  871.   /** toString3() Converts this tree (with depth) to a string.
  872.    * @returns {string} this tree as a string
  873.    */
  874.   toString3() {
  875.     if (this.bp.numEl == 0) { return ""; }
  876.     var sBuf = new StringBuffer();
  877.     this.#toString3(this.bp.rootNdx, 0, sBuf);
  878.     return "tree with depth" + sBuf.str;
  879.   }
  880.  
  881.   /** blocksToString()
  882.    * @returns {string} this tree's blocks as a string
  883.    */
  884.   blocksToString() {
  885.     var str = "blocks";
  886.     for (var i = 0; i < this.bp.bkSr.blocks.length; i++) {
  887.       if (this.bp.bkSr.blocks[i] != undefined) {
  888.         if (i == this.bp.rootNdx) { str += "{root}"; }
  889.         str += this.bp.bkSr.blocks[i].toString();
  890.         if (i + 1 < this.bp.bkSr.blocks.length && this.bp.bkSr.blocks[i + 1] != undefined) {
  891.           str += ",";
  892.         }
  893.       }
  894.     }
  895.     //str += "\nfree" + JSON.stringify([...this.bp.bkSr.free]);
  896.     return str;
  897.   }
  898. }
  899.  
  900. /** compare() Compare values. This will work for string and number types.
  901.  * @param {any} a a value
  902.  * @param {any} b another value
  903.  * @returns {number} -1 if a < b, 0 if a == b, 1 if a > b
  904.  */
  905. function compare(a, b) {
  906.   if (a < b) { return -1; }
  907.   else if (a > b) { return 1; }
  908.   return 0;
  909. }
  910.  
  911. /** assertSets() Assert that sizes and values of tree and set are equal.
  912.  * @param {BTree} aTree a tree
  913.  * @param {Set} aSet a set
  914.  */
  915. function assertSets(aTree, aSet) {
  916.   if (aTree.size() == aSet.size) {
  917.     var i = 0, setVals = Array.from(aSet).sort(compare);
  918.     for (const tVal of aTree) {
  919.       if (compare(tVal, setVals[i]) != 0) {
  920.         throw new Error("assert: value mismatch");
  921.       }
  922.       i++;
  923.     }
  924.   } else {
  925.     throw new Error("assert: size mismatch");
  926.   }
  927. }
  928.  
  929.  
  930. var natoValues = ["alpha", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",
  931.                   "india", "juliet", "kilo", "lima", "mike", "november", "oscar", "papa",
  932.                   "quebec", "romeo", "sierra", "tango", "uniform"];
  933. var wuValues = ["adams", "boston", "chicago", "denver", "easy", "frank", "george", "henry",
  934.                 "ida", "john", "king", "lincoln", "mary", "new york", "ocean", "peter",
  935.                 "queen", "roger", "sugar", "thomas", "union"];
  936. var bTree = new BTree(15, compare);
  937. var valuesSet = new Set();
  938. var midNV = Math.floor(natoValues.length / 2);
  939.  
  940. for (var i = 0; i < natoValues.length; i++) {
  941.   bTree.add(natoValues[i]);
  942.   valuesSet.add(natoValues[i]);
  943. }
  944. console.log("--added " + natoValues.length + " nato phonetics--");
  945. assertSets(bTree, valuesSet);
  946.  
  947. console.log(bTree.toString());
  948.  
  949. console.log("findGE(" + JSON.stringify(wuValues[0]) + ") = " + bTree.findGE(wuValues[0]));
  950. console.log("size = " + bTree.size());
  951.  
  952. for (var i = wuValues.length - 1; i >= 0; i--) {
  953.   bTree.add(wuValues[i]);
  954.   valuesSet.add(wuValues[i]);
  955. }
  956. console.log("--added (in reverse order) " + wuValues.length + " western union phonetics--");
  957. assertSets(bTree, valuesSet);
  958.  
  959. console.log(bTree.toString2());
  960.  
  961. console.log("findGE(" + JSON.stringify(wuValues[0]) + ") = " + bTree.findGE(wuValues[0]));
  962. console.log("findLT(" + JSON.stringify(natoValues[midNV]) + ") = " + bTree.findLT(natoValues[midNV]));
  963. console.log("size = " + bTree.size());
  964.  
  965. for (var i = 0; i < midNV; i++) {
  966.   bTree.remove(natoValues[i]);
  967.   valuesSet.delete(natoValues[i]);
  968. }
  969. console.log("--removed first " + midNV + " nato phonetics--");
  970. assertSets(bTree, valuesSet);
  971.  
  972. console.log(bTree.toString());
  973.  
  974. console.log("findLT(" + JSON.stringify(wuValues[0]) + ") = " + bTree.findLT(wuValues[0]));
  975. console.log("findLT(" + JSON.stringify(natoValues[midNV]) + ") = " + bTree.findLT(natoValues[midNV]));
  976. console.log("findGE(\"victor\") = " + bTree.findGE("victor"));
  977. var str = "";
  978. var iter = bTree.iteratorGE(natoValues[midNV]);
  979. while (iter.hasNext()) {
  980.   str += iter.next()
  981.   if (iter.hasNext()) { str += ", "; }
  982. }
  983. console.log("iteratorGE(" + JSON.stringify(natoValues[midNV]) + ") = " + str);
  984. console.log("size = " + bTree.size());
  985.  
  986. console.log(bTree.toString3());
  987.  
  988. bTree.clear();
  989. valuesSet.clear();
  990. console.log("--clear--");
  991. console.log(bTree.toString2());
  992.  
  993. /** @version 1.0a */
Advertisement
Comments
Add Comment
Please, Sign In to add comment