ouija_bh

B-Tree using open content

Jul 17th, 2025 (edited)
354
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 26.97 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.     var cur = this.bp.rootNdx;
  213.     do {
  214.       var block = this.bp.bkSr.readBlock(cur);
  215.       this.#nodeSk.push(block);
  216.       this.#indexSk.push(0);
  217.       cur = block.children[0];
  218.     } while (cur >= 0);
  219.   }
  220.  
  221.   #advance() {
  222.     var block = this.#nodeSk[this.#nodeSk.length - 1];
  223.     var i = this.#indexSk[this.#indexSk.length - 1];
  224.     if (block.isLeaf()) { // this is a leaf, walk up
  225.       while (this.#nodeSk.length > 0 && i == block.size()) {
  226.         this.#nodeSk.pop();
  227.         this.#indexSk.pop();
  228.         if (this.#nodeSk.length > 0) {
  229.           block = this.#nodeSk[this.#nodeSk.length - 1];
  230.           i = this.#indexSk[this.#indexSk.length - 1];
  231.         }
  232.       }
  233.     } else { // this is an internal node, walk down
  234.       var cur = block.children[i];
  235.       do {
  236.         block = this.bp.bkSr.readBlock(cur);
  237.         this.#nodeSk.push(block);
  238.         this.#indexSk.push(0);
  239.         cur = block.children[0];
  240.       } while (cur >= 0);
  241.     }
  242.   }
  243.  
  244.   /** constructor() Create an iterator starting with the smallest element in the
  245.    *  tree that is greater than or equal to value. If value is undefined, create
  246.    *  an iterator starting with the smallest element in the tree.
  247.    * @param {BTProps} bp properties of the BTree
  248.    * @param {any} value the value
  249.    */
  250.   constructor(bp, value) {
  251.     this.#nodeSk = new Array();
  252.     this.#indexSk = new Array();
  253.     this.bp = bp;
  254.    
  255.     if (this.bp.numEl == 0) { return; }
  256.     if (value == undefined) {
  257.       this.#iterator();
  258.     } else {
  259.       var block, i, cur = this.bp.rootNdx;
  260.       do {
  261.         block = this.bp.bkSr.readBlock(cur);
  262.         i = findIt(block.keys, value, this.bp.compare);
  263.         this.#nodeSk.push(block);
  264.         if (i < 0) {  // found the value, add its index and stop
  265.           this.#indexSk.push(-(i + 1));
  266.           return;
  267.         }
  268.         this.#indexSk.push(i);  // add index of smallest value
  269.         cur = block.children[i];
  270.       } while (cur >= 0);
  271.       if (i == block.size()) { this.#advance(); }
  272.     }
  273.   }
  274.  
  275.   /** hasNext()
  276.    * @returns {boolean}
  277.    */
  278.   hasNext() {
  279.     return this.#nodeSk.length > 0;
  280.   }
  281.  
  282.   /** next()
  283.    * @returns {any}
  284.    */
  285.   next() {
  286.     var block = this.#nodeSk[this.#nodeSk.length - 1];
  287.     var i = this.#indexSk[this.#indexSk.length - 1];
  288.     var value = block.keys[i++];
  289.     this.#indexSk[this.#indexSk.length - 1] = i;
  290.     this.#advance();
  291.     return value;
  292.   }
  293. }
  294.  
  295. /** A simple string buffer. */
  296. class StringBuffer {
  297.   str = "";
  298. }
  299.  
  300. /** A sorted set. credit: Morin SSet.java, BTree.java. */
  301. class BTree {
  302.   /** constructor()
  303.    * @param {number} blockSize maximum number of keys of a node
  304.    * @param {function} comparator function taking (any, any) and returning -1, 0, or 1
  305.    */
  306.   constructor(blockSize, comparator) {
  307.     this.bp = new BTProps();
  308.     blockSize += 1 - (blockSize % 2); // an odd number
  309.     this.bp.bkSz = blockSize;
  310.     this.bp.hfBS = Math.floor(blockSize / 2);
  311.     this.bp.bkSr = new BlockStore();
  312.     this.bp.numEl = 0;
  313.     this.bp.rootNdx = (new BTNode(this.bp)).index;
  314.     this.bp.compare = comparator;
  315.   }
  316.  
  317.   /** #addRecursive() Add a value to the subtree rooted at the node at given index.
  318.    *  If that node is split by this operation then the return value is the BTNode
  319.    *  that was created when node was split.
  320.    * @param {any} value the element to add
  321.    * @param {number} index the index of the node at which to add value
  322.    * @returns {BTNode} a new node that was created when node was split, or
  323.    *            undefined if node was not split
  324.    */
  325.   #addRecursive(value, index) {
  326.     var block = this.bp.bkSr.readBlock(index); // BTNode
  327.     var i = findIt(block.keys, value, this.bp.compare);
  328.     if (i < 0) { throw new Error("duplicate value"); }
  329.     if (block.children[i] < 0) { // leaf node, just add it
  330.       block.add(value, -1);
  331.       this.bp.bkSr.writeBlock(block.index, block);
  332.     } else {
  333.       var newBk = this.#addRecursive(value, block.children[i]);  // BTNode
  334.       if (newBk != undefined) {  // child was split, newBk is new child
  335.         value = newBk.remove(0);
  336.         this.bp.bkSr.writeBlock(newBk.index, newBk);
  337.         block.add(value, newBk.index);
  338.         this.bp.bkSr.writeBlock(block.index, block);
  339.       }
  340.     }
  341.     if (block.isFull()) { return block.split(); }
  342.     return undefined;
  343.   }
  344.  
  345.   /** add() Add a value to this tree.
  346.    * @param {any} value the element to add
  347.    * @returns {boolean} true if value was added, or false if value was already
  348.    *            in the set or value is undefined
  349.    */
  350.   add(value) {
  351.     if (value == undefined) {
  352.       console.error("BTree.add() undefined value");
  353.       return false;
  354.     }
  355.     var block;  // BTNode
  356.     try {
  357.       block = this.#addRecursive(value, this.bp.rootNdx);
  358.     } catch (err) {
  359.       console.error("BTree.add() " + err.message);
  360.       return false;
  361.     }
  362.     if (block != undefined) {   // root was split, make new root
  363.       var newRoot = new BTNode(this.bp);
  364.       value = block.remove(0);
  365.       this.bp.bkSr.writeBlock(block.index, block);
  366.       newRoot.children[0] = this.bp.rootNdx;
  367.       newRoot.keys[0] = value;
  368.       newRoot.children[1] = block.index;
  369.       this.bp.rootNdx = newRoot.index;
  370.       this.bp.bkSr.writeBlock(this.bp.rootNdx, newRoot);
  371.     }
  372.     this.bp.numEl++;
  373.     return true;
  374.   }
  375.  
  376.   /** #merge() Node H will absorb node I.
  377.    * @param {BTNode} block the parent of H and I
  378.    * @param {number} h the index h in block.children
  379.    * @param {BTNode} bkH the left sibling of I
  380.    * @param {BTNode} bkI the right sibling of H
  381.    */
  382.   #merge(block, h, bkH, bkI) {
  383.     if (bkH.index != block.children[h]) { throw new Error("assert: H mismatch"); }
  384.     if (bkI.index != block.children[h + 1]) { throw new Error("assert: I mismatch"); }
  385.     var sizeI = bkI.size();
  386.     var sizeH = bkH.size();
  387.     // copy keys from I to H
  388.     var smaller = bkI.keys.slice(0, sizeI);
  389.     bkH.keys.splice(sizeH + 1, smaller.length, ...smaller);
  390.     smaller = bkI.children.slice(0, sizeI + 1);
  391.     bkH.children.splice(sizeH + 1, smaller.length, ...smaller);
  392.     // add key to H and remove it from block
  393.     bkH.keys[sizeH] = block.keys[h];
  394.     block.keys = block.keys.copyWithin(h, h + 1, this.bp.bkSz);
  395.     block.keys[this.bp.bkSz - 1] = undefined;
  396.     block.children = block.children.copyWithin(h + 1, h + 2, this.bp.bkSz + 1);
  397.     block.children[this.bp.bkSz] = -1;
  398.     this.bp.bkSr.freeBlock(bkI.index);
  399.   }
  400.  
  401.   /** #shiftRtoL() Shift keys from node J into node I.
  402.    * @param {BTNode} block the parent of J and I
  403.    * @param {number} i the index i in block.children
  404.    * @param {BTNode} bkJ the right sibling of I
  405.    * @param {BTNode} bkI the left sibling of J
  406.    */
  407.   #shiftRtoL(block, i, bkJ, bkI) {
  408.     var sizeI = bkI.size();
  409.     var sizeJ = bkJ.size();
  410.     var shift = Math.floor((sizeI + sizeJ) / 2) - sizeI;  // num. keys to shift from J to I
  411.     // shift keys and children from J to I
  412.     bkI.keys[sizeI] = block.keys[i];
  413.     var smaller = bkJ.keys.slice(0, shift - 1);
  414.     bkI.keys.splice(sizeI + 1, smaller.length, ...smaller);
  415.     smaller = bkJ.children.slice(0, shift);
  416.     bkI.children.splice(sizeI + 1, smaller.length, ...smaller);
  417.     block.keys[i] = bkJ.keys[shift - 1];
  418.     // delete keys and children from J
  419.     bkJ.keys = bkJ.keys.copyWithin(0, shift, this.bp.bkSz);
  420.     bkJ.keys.fill(undefined, sizeJ - shift, this.bp.bkSz);
  421.     bkJ.children = bkJ.children.copyWithin(0, shift, this.bp.bkSz + 1);
  422.     bkJ.children.fill(-1, sizeJ - shift + 1, this.bp.bkSz + 1);
  423.   }
  424.  
  425.   /** #checkUnderflowZero() Check if an underflow has occured in the i'th
  426.    *  child of block and, if so, fix it.
  427.    * @param {BTNode} block block to check
  428.    * @param {number} i the index i in block.children
  429.    */
  430.   #checkUnderflowZero(block, i) {
  431.     var bkI = this.bp.bkSr.readBlock(block.children[i]);  // i'th child of block
  432.     if (bkI.size() < this.bp.hfBS - 1) {  // underflow at I
  433.       var bkJ = this.bp.bkSr.readBlock(block.children[i + 1]); // J right of I
  434.       if (bkJ.size() > this.bp.hfBS) { // I can borrow from J
  435.         this.#shiftRtoL(block, i, bkJ, bkI);
  436.       } else { // I will absorb J
  437.         this.#merge(block, i, bkI, bkJ);
  438.         block.children[i] = bkI.index;
  439.       }
  440.     }
  441.   }
  442.  
  443.   /** #shiftLtoR() Shift keys from node H into node I.
  444.    * @param {BTNode} block the parent of H and I
  445.    * @param {number} h the index h in block.children
  446.    * @param {BTNode} bkH the left sibling of I
  447.    * @param {BTNode} bkI the right sibling of H
  448.    */
  449.   #shiftLtoR(block, h, bkH, bkI) {
  450.     var sizeI = bkI.size();
  451.     var sizeH = bkH.size();
  452.     var shift = Math.floor((sizeI + sizeH) / 2) - sizeI;  // num. keys to shift from H to I
  453.     // make space for new keys in I
  454.     bkI.keys = bkI.keys.copyWithin(shift, 0, sizeI);
  455.     bkI.children = bkI.children.copyWithin(shift, 0, sizeI + 1);
  456.     // move keys and children out of H and into I (and parent)
  457.     bkI.keys[shift - 1] = block.keys[h];
  458.     block.keys[h] = bkH.keys[sizeH - shift];
  459.     var larger = bkH.keys.slice(sizeH - shift + 1, sizeH);
  460.     bkI.keys.splice(0, larger.length, ...larger);
  461.     bkH.keys.fill(undefined, sizeH - shift, sizeH);
  462.     larger = bkH.children.slice(sizeH - shift + 1, sizeH + 1);
  463.     bkI.children.splice(0, larger.length, ...larger);
  464.     bkH.children.fill(-1, sizeH - shift + 1, sizeH + 1);
  465.   }
  466.  
  467.   /** #checkUnderflowNonZero() Check if an underflow has occured in the i'th
  468.    *  child of block and, if so, fix it.
  469.    * @param {BTNode} block block to check
  470.    * @param {number} i the index i in block.children
  471.    */
  472.   #checkUnderflowNonZero(block, i) {
  473.     var bkI = this.bp.bkSr.readBlock(block.children[i]);  // i'th child of block
  474.     if (bkI.size() < this.bp.hfBS - 1) {  // underflow at I
  475.       var bkH = this.bp.bkSr.readBlock(block.children[i - 1]); // H left of I
  476.       if (bkH.size() > this.bp.hfBS) {  // I can borrow from H
  477.         this.#shiftLtoR(block, i - 1, bkH, bkI);
  478.       } else {  // H will absorb I
  479.         this.#merge(block, i - 1, bkH, bkI);
  480.       }
  481.     }
  482.   }
  483.  
  484.   /** #checkUnderflow() Check if an underflow has occurred in the i'th child
  485.    *  of block and, if so, fix it by borrowing from or merging with a sibling.
  486.    * @param {BTNode} block block to check
  487.    * @param {number} i the index i in block.children
  488.    */
  489.   #checkUnderflow(block, i) {
  490.     if (block.children[i] < 0) { return; }
  491.     if (i == 0) {
  492.       this.#checkUnderflowZero(block, i); // use block's right sibling
  493.     } else {
  494.       this.#checkUnderflowNonZero(block, i);
  495.     }
  496.   }
  497.  
  498.   /** #removeSmallest() Remove the smallest value in the subtree rooted at the
  499.    *  node with index.
  500.    * @param {number} index the index of a subtree
  501.    * @returns {any} the value that was removed
  502.    */
  503.   #removeSmallest(index) {
  504.     var block = this.bp.bkSr.readBlock(index);
  505.     if (block.isLeaf()) { return block.remove(0); }
  506.     var left = this.#removeSmallest(block.children[0]);
  507.     this.#checkUnderflow(block, 0);
  508.     return left;
  509.   }
  510.  
  511.   /** #removeRecursive() Remove the value from the subtree rooted at the node with index.
  512.    * @param {any} value the value to remove
  513.    * @param {number} index the index of the subtree to remove value from
  514.    * @returns {boolean} true if value was removed and false otherwise
  515.    */
  516.   #removeRecursive(value, index) {
  517.     if (index < 0) { return false; }  // didn't find it
  518.     var block = this.bp.bkSr.readBlock(index);
  519.     var i = findIt(block.keys, value, this.bp.compare);
  520.     if (i < 0) { // found it
  521.       i = -(i + 1);
  522.       if (block.isLeaf()) {
  523.         block.remove(i);
  524.       } else {
  525.         block.keys[i] = this.#removeSmallest(block.children[i + 1]);
  526.         this.#checkUnderflow(block, i + 1);
  527.       }
  528.       return true;
  529.     } else if (this.#removeRecursive(value, block.children[i])) {
  530.       this.#checkUnderflow(block, i);
  531.       return true;
  532.     }
  533.     return false;
  534.   }
  535.  
  536.   /** remove() Remove a value from this tree.
  537.    * @param {any} value the value
  538.    * @returns {boolean} true if value was removed and false if value
  539.    *            was not removed (because value was not present)
  540.    */
  541.   remove(value) {
  542.     if (this.#removeRecursive(value, this.bp.rootNdx)) {
  543.       this.bp.numEl--;
  544.       var rootBk = this.bp.bkSr.readBlock(this.bp.rootNdx);
  545.       if (rootBk.size() == 0 && this.bp.numEl > 0) { // root has only one child
  546.         this.bp.rootNdx = rootBk.children[0];
  547.       }
  548.       return true;
  549.     }
  550.     return false;
  551.   }
  552.  
  553.   /** findGE() Find the smallest element in the tree that is greater than or equal
  554.    *  to value. If value is undefined, return the smallest element in the tree.
  555.    * @param {any} value the value
  556.    * @returns {any} the smallest element in the tree that is greater than or equal to
  557.    *            value or undefined if no such element exists. If value is undefined
  558.    *            then the smallest element in the tree
  559.    */
  560.   findGE(value) {
  561.     var last = undefined;
  562.     var cur = this.bp.rootNdx;
  563.     while (cur >= 0) {
  564.       var block = this.bp.bkSr.readBlock(cur);
  565.       var i = findIt(block.keys, value, this.bp.compare);
  566.       if (i < 0) { return block.keys[-(i + 1)]; } // found it
  567.       if (block.keys[i] != undefined) { last = block.keys[i]; }
  568.       cur = block.children[i];
  569.     }
  570.     return last;
  571.   }
  572.  
  573.   /** findLT() Find the largest element in the tree that is less than value.
  574.    *  If value is undefined, return the smallest element in the tree.
  575.    * @param {any} value the value
  576.    * @returns {any} the largest element in the tree that is less than value. If
  577.    *          value is undefined then the smallest element in the tree
  578.    */
  579.   findLT(value) {
  580.     var last = undefined;
  581.     var cur = this.bp.rootNdx;
  582.     while (cur >= 0) {
  583.       var block = this.bp.bkSr.readBlock(cur);
  584.       var i = findIt(block.keys, value, this.bp.compare);
  585.       if (i < 0) { i = -(i + 1); }
  586.       if (i > 0) { last = block.keys[i - 1]; }
  587.       cur = block.children[i];
  588.     }
  589.     return last;
  590.   }
  591.  
  592.   /** comparator()
  593.    * @returns {function} the comparator of this tree's elements
  594.    */
  595.   comparator() {
  596.     return this.bp.compare;
  597.   }
  598.  
  599.   /** iteratorGE() Get an iterator of this tree starting with the smallest element
  600.    *  in the tree that is greater than or equal to value. If value is undefined,
  601.    *  returns iterator starting with the smallest element in the tree.
  602.    * @param {any} value the value
  603.    * @returns {BTIterator} iterator starting with the smallest element in the
  604.    *              tree that is greater than or equal to value. If value is undefined,
  605.    *              iterator starting with the smallest element in the tree.
  606.    */
  607.   iteratorGE(value) {
  608.     return new BTIterator(this.bp, value);
  609.   }
  610.  
  611.   /** @@iterator() Get an iterator of this tree. */
  612.   *[Symbol.iterator]() {
  613.     var iter = new BTIterator(this.bp, undefined);
  614.     while (iter.hasNext()) {
  615.       yield iter.next();
  616.     }
  617.   }
  618.  
  619.   /** size()
  620.    * @returns {number} the number of elements in this tree
  621.    */
  622.   size() {
  623.     return this.bp.numEl;
  624.   }
  625.  
  626.   /** clear() Remove all elements from this tree. */
  627.   clear() {
  628.     this.bp.numEl = 0;
  629.     this.bp.bkSr.clear();
  630.     this.bp.rootNdx = (new BTNode(this.bp)).index;
  631.   }
  632.  
  633.   /** #toString() Converts a tree to a string using recursion.
  634.    * @param {number} index index of block
  635.    * @param {StringBuffer} sBuf string buffer
  636.    */
  637.   #toString(index, sBuf) {
  638.     if (index < 0) { return; }
  639.     var block = this.bp.bkSr.readBlock(index);
  640.     var i = 0;
  641.     while(i < this.bp.bkSz && block.keys[i] != undefined) {
  642.       this.#toString(block.children[i], sBuf);
  643.       sBuf.str += block.keys[i++] + ", ";
  644.     }
  645.     this.#toString(block.children[i], sBuf);
  646.   }
  647.  
  648.   /** toString() Converts this tree to a string.
  649.    * @returns {string} this tree as a string
  650.    */
  651.   toString() {
  652.     if (this.bp.numEl == 0) { return ""; }
  653.     var sBuf = new StringBuffer();
  654.     this.#toString(this.bp.rootNdx, sBuf);
  655.     return sBuf.str.slice(0, sBuf.str.length - 2);
  656.   }
  657.  
  658.   /** toString2()
  659.    * @returns {string} in-order values formatted as array
  660.    */
  661.   toString2() {
  662.     return JSON.stringify([...this]);
  663.   }
  664.  
  665.   /** #toString3() Converts a tree to a string using recursion.
  666.    * @param {number} index index of block
  667.    * @param {number} depth depth of block
  668.    * @param {StringBuffer} sBuf string buffer
  669.    */
  670.   #toString3(index, depth, sBuf) {
  671.     if (index < 0) { return; }
  672.     var block = this.bp.bkSr.readBlock(index);
  673.     var i = 0;
  674.     while(i < this.bp.bkSz && block.keys[i] != undefined) {
  675.       this.#toString3(block.children[i], depth + 1, sBuf);
  676.       sBuf.str += '\n' + " ".repeat(depth + 1) + block.keys[i++];
  677.     }
  678.     this.#toString3(block.children[i], depth + 1, sBuf);
  679.   }
  680.  
  681.   /** toString3() Converts this tree (with depth) to a string.
  682.    * @returns {string} this tree as a string
  683.    */
  684.   toString3() {
  685.     if (this.bp.numEl == 0) { return ""; }
  686.     var sBuf = new StringBuffer();
  687.     this.#toString3(this.bp.rootNdx, 0, sBuf);
  688.     return "tree with depth" + sBuf.str;
  689.   }
  690.  
  691.   /** blocksToString()
  692.    * @returns {string} this tree's blocks as a string
  693.    */
  694.   blocksToString() {
  695.     var str = "blocks";
  696.     for (var i = 0; i < this.bp.bkSr.blocks.length; i++) {
  697.       if (this.bp.bkSr.blocks[i] != undefined) {
  698.         if (i == this.bp.rootNdx) { str += "{root}"; }
  699.         str += this.bp.bkSr.blocks[i].toString();
  700.         if (i + 1 < this.bp.bkSr.blocks.length && this.bp.bkSr.blocks[i + 1] != undefined) {
  701.           str += ",";
  702.         }
  703.       }
  704.     }
  705.     //str += "\nfree" + JSON.stringify([...this.bp.bkSr.free]);
  706.     return str;
  707.   }
  708. }
  709.  
  710. /** compare() Compare values. This will work for string and number types.
  711.  * @param {any} a a value
  712.  * @param {any} b another value
  713.  * @returns {number} -1 if a < b, 0 if a == b, 1 if a > b
  714.  */
  715. function compare(a, b) {
  716.   if (a < b) { return -1; }
  717.   else if (a > b) { return 1; }
  718.   return 0;
  719. }
  720.  
  721. /** assertSets() Assert that sizes and values of tree and set are equal.
  722.  * @param {BTree} aTree a tree
  723.  * @param {Set} aSet a set
  724.  */
  725. function assertSets(aTree, aSet) {
  726.   if (aTree.size() == aSet.size) {
  727.     var i = 0, setVals = Array.from(aSet).sort(compare);
  728.     for (const tVal of aTree) {
  729.       if (compare(tVal, setVals[i]) != 0) {
  730.         throw new Error("assert: value mismatch");
  731.       }
  732.       i++;
  733.     }
  734.   } else {
  735.     throw new Error("assert: size mismatch");
  736.   }
  737. }
  738.  
  739.  
  740. var natoValues = ["alpha", "bravo", "charlie", "delta", "echo", "foxtrot", "golf", "hotel",
  741.                   "india", "juliet", "kilo", "lima", "mike", "november", "oscar", "papa",
  742.                   "quebec", "romeo", "sierra", "tango", "uniform"];
  743. var wuValues = ["adams", "boston", "chicago", "denver", "easy", "frank", "george", "henry",
  744.                 "ida", "john", "king", "lincoln", "mary", "new york", "ocean", "peter",
  745.                 "queen", "roger", "sugar", "thomas", "union"];
  746. var bTree = new BTree(15, compare);
  747. var valuesSet = new Set();
  748. var midNV = Math.floor(natoValues.length / 2);
  749.  
  750. for (var i = 0; i < natoValues.length; i++) {
  751.   bTree.add(natoValues[i]);
  752.   valuesSet.add(natoValues[i]);
  753. }
  754. console.log("--added " + natoValues.length + " nato phonetics--");
  755. assertSets(bTree, valuesSet);
  756.  
  757. console.log(bTree.toString());
  758.  
  759. console.log("findGE(" + JSON.stringify(wuValues[0]) + ") = " + bTree.findGE(wuValues[0]));
  760. console.log("size = " + bTree.size());
  761.  
  762. for (var i = wuValues.length - 1; i >= 0; i--) {
  763.   bTree.add(wuValues[i]);
  764.   valuesSet.add(wuValues[i]);
  765. }
  766. console.log("--added (in reverse order) " + wuValues.length + " western union phonetics--");
  767. assertSets(bTree, valuesSet);
  768.  
  769. console.log(bTree.toString2());
  770.  
  771. console.log("findGE(" + JSON.stringify(wuValues[0]) + ") = " + bTree.findGE(wuValues[0]));
  772. console.log("findLT(" + JSON.stringify(natoValues[midNV]) + ") = " + bTree.findLT(natoValues[midNV]));
  773. console.log("size = " + bTree.size());
  774.  
  775. for (var i = 0; i < midNV; i++) {
  776.   bTree.remove(natoValues[i]);
  777.   valuesSet.delete(natoValues[i]);
  778. }
  779. console.log("--removed first " + midNV + " nato phonetics--");
  780. assertSets(bTree, valuesSet);
  781.  
  782. console.log(bTree.toString());
  783.  
  784. console.log("findLT(" + JSON.stringify(wuValues[0]) + ") = " + bTree.findLT(wuValues[0]));
  785. console.log("findLT(" + JSON.stringify(natoValues[midNV]) + ") = " + bTree.findLT(natoValues[midNV]));
  786. console.log("findGE(\"victor\") = " + bTree.findGE("victor"));
  787. var str = "";
  788. var iter = bTree.iteratorGE(natoValues[midNV]);
  789. while (iter.hasNext()) {
  790.   str += iter.next()
  791.   if (iter.hasNext()) { str += ", "; }
  792. }
  793. console.log("iteratorGE(" + JSON.stringify(natoValues[midNV]) + ") = " + str);
  794. console.log("size = " + bTree.size());
  795.  
  796. console.log(bTree.toString3());
  797.  
  798. bTree.clear();
  799. valuesSet.clear();
  800. console.log("--clear--");
  801. console.log(bTree.toString2());
  802.  
  803. /** @version 1.0b */
Advertisement
Comments
Add Comment
Please, Sign In to add comment