Advertisement
ouija_bh

B-Tree using open content

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