From 5ff29ee556fa5687621bf98b4a45e8ae56b31868 Mon Sep 17 00:00:00 2001 From: n8allan <3095770+n8allan@users.noreply.github.com> Date: Mon, 11 Mar 2024 17:50:38 -0600 Subject: [PATCH] Fixed issues with path after insert. Optimized insert. Bug w/ rebalance. --- docs/assets/search.js | 2 +- docs/classes/BTree.html | 57 ++++++++++++++++---------------- docs/classes/KeyBound.html | 4 +-- docs/classes/KeyRange.html | 4 +-- docs/classes/Path.html | 4 +-- docs/classes/PathBranch.html | 4 +-- docs/variables/NodeCapacity.html | 2 +- package.json | 2 +- src/b-tree.ts | 47 +++++++++++--------------- test/b-tree.branching.test.ts | 13 ++++++-- 10 files changed, 69 insertions(+), 70 deletions(-) diff --git a/docs/assets/search.js b/docs/assets/search.js index 9620ff5..baad80d 100644 --- a/docs/assets/search.js +++ b/docs/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file +window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file diff --git a/docs/classes/BTree.html b/docs/classes/BTree.html index b51556a..cf5d4fe 100644 --- a/docs/classes/BTree.html +++ b/docs/classes/BTree.html @@ -2,7 +2,7 @@ Allows for efficient storage and retrieval of data in a sorted manner.

Type Parameters

Constructors

Constructors

Properties

Constructors

  • Type Parameters

    • TKey
    • TEntry

    Parameters

    • Optional keyFromEntry: ((entry) => TKey) = ...

      a function to extract the key from an entry. The default assumes the key is the entry itself.

    • Optional compare: ((a, b) => -1 | 0 | 1) = ...

      a comparison function for keys. The default uses < and > operators.

      -
        • (a, b): -1 | 0 | 1
        • Parameters

          Returns -1 | 0 | 1

    Returns BTree<TKey, TEntry>

Properties

_root: ITreeNode
_version: number = 0
compare: ((a, b) => -1 | 0 | 1) = ...

a comparison function for keys. The default uses < and > operators.

+
    • (a, b): -1 | 0 | 1
    • Parameters

      Returns -1 | 0 | 1

Returns BTree<TKey, TEntry>

Properties

_root: ITreeNode
_version: number = 0
compare: ((a, b) => -1 | 0 | 1) = ...

a comparison function for keys. The default uses < and > operators.

Type declaration

    • (a, b): -1 | 0 | 1
    • a comparison function for keys. The default uses < and > operators.

      -

      Parameters

      Returns -1 | 0 | 1

keyFromEntry: ((entry) => TKey) = ...

a function to extract the key from an entry. The default assumes the key is the entry itself.

+

Parameters

Returns -1 | 0 | 1

keyFromEntry: ((entry) => TKey) = ...

a function to extract the key from an entry. The default assumes the key is the entry itself.

Type declaration

    • (entry): TKey
    • a function to extract the key from an entry. The default assumes the key is the entry itself.

      -

      Parameters

      Returns TKey

Methods

Methods

Returns number

Generated using TypeDoc

\ No newline at end of file +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/KeyBound.html b/docs/classes/KeyBound.html index bcf3bf8..2cd5ece 100644 --- a/docs/classes/KeyBound.html +++ b/docs/classes/KeyBound.html @@ -1,4 +1,4 @@ -KeyBound | digitree

Class KeyBound<TKey>

Type Parameters

  • TKey

Constructors

constructor +KeyBound | digitree

Class KeyBound<TKey>

Type Parameters

  • TKey

Constructors

Properties

Constructors

Properties

inclusive: boolean = true
key: TKey

Generated using TypeDoc

\ No newline at end of file +

Constructors

Properties

inclusive: boolean = true
key: TKey

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/KeyRange.html b/docs/classes/KeyRange.html index 789e6d7..aea444a 100644 --- a/docs/classes/KeyRange.html +++ b/docs/classes/KeyRange.html @@ -1,6 +1,6 @@ KeyRange | digitree

Class KeyRange<TKey>

Used for range scans. Omitting first or last implies the end of the tree.

-

Type Parameters

  • TKey

Constructors

Type Parameters

  • TKey

Constructors

Properties

Constructors

Properties

first?: KeyBound<TKey>
isAscending: boolean = true
last?: KeyBound<TKey>

Generated using TypeDoc

\ No newline at end of file +

Constructors

Properties

first?: KeyBound<TKey>
isAscending: boolean = true
last?: KeyBound<TKey>

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/Path.html b/docs/classes/Path.html index 048bae2..64e9781 100644 --- a/docs/classes/Path.html +++ b/docs/classes/Path.html @@ -1,7 +1,7 @@ Path | digitree

Class Path<TKey, TEntry>

Represents a cursor in a BTree. Invalid once mutation has occurred (unless it is the results of a mutation method). Do not change the properties of this object directly. Use the methods of the BTree class to manipulate it.

Member

on - true if the cursor is on an entry, false if it is between entries.

-

Type Parameters

  • TKey
  • TEntry

Constructors

Type Parameters

  • TKey
  • TEntry

Constructors

Properties

Methods

Constructors

Properties

branches: PathBranch<TKey>[]
leafIndex: number
leafNode: LeafNode<TEntry>
on: boolean
version: number

Methods

Generated using TypeDoc

\ No newline at end of file +

Constructors

Properties

branches: PathBranch<TKey>[]
leafIndex: number
leafNode: LeafNode<TEntry>
on: boolean
version: number

Methods

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/PathBranch.html b/docs/classes/PathBranch.html index 53bd086..4e7a6a9 100644 --- a/docs/classes/PathBranch.html +++ b/docs/classes/PathBranch.html @@ -1,5 +1,5 @@ -PathBranch | digitree

Class PathBranch<TKey>

Type Parameters

  • TKey

Constructors

constructor +PathBranch | digitree

Class PathBranch<TKey>

Type Parameters

  • TKey

Constructors

Properties

Methods

Constructors

Properties

index: number
node: BranchNode<TKey>

Methods

Generated using TypeDoc

\ No newline at end of file +

Constructors

Properties

index: number
node: BranchNode<TKey>

Methods

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/variables/NodeCapacity.html b/docs/variables/NodeCapacity.html index 9d7837e..b8e8883 100644 --- a/docs/variables/NodeCapacity.html +++ b/docs/variables/NodeCapacity.html @@ -1,2 +1,2 @@ NodeCapacity | digitree

Variable NodeCapacityConst

NodeCapacity: 64 = 64

Node capacity. Not configurable - not worth the runtime memory, when almost nobody will touch this

-

Generated using TypeDoc

\ No newline at end of file +

Generated using TypeDoc

\ No newline at end of file diff --git a/package.json b/package.json index de9df26..21ca0b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "digitree", - "version": "1.3.0", + "version": "1.3.1", "target": "module", "description": "Lightweight B+Tree implementation in TypeScript", "main": "./dist/index.js", diff --git a/src/b-tree.ts b/src/b-tree.ts index 6315ffb..c69e8df 100644 --- a/src/b-tree.ts +++ b/src/b-tree.ts @@ -459,7 +459,7 @@ export class BTree { } private internalInsertAt(path: Path, entry: TEntry) { - let split = this.leafInsert(path.leafNode, path.leafIndex, entry); + let split = this.leafInsert(path, entry); let branchIndex = path.branches.length - 1; while (split && branchIndex >= 0) { split = this.branchInsert(path, branchIndex, split); @@ -468,6 +468,7 @@ export class BTree { if (split) { const newBranch = new BranchNode([split.key], [this._root, split.right]); this._root = newBranch; + path.branches.unshift(new PathBranch(newBranch, split.indexDelta)); } } @@ -528,7 +529,8 @@ export class BTree { } } - private leafInsert(leaf: LeafNode, index: number, entry: TEntry): Split | undefined { + private leafInsert(path: Path, entry: TEntry): Split | undefined { + const { leafNode: leaf, leafIndex: index } = path; if (leaf.entries.length < NodeCapacity) { // No split needed leaf.entries.splice(index, 0, entry); return undefined; @@ -542,52 +544,41 @@ export class BTree { const newLeaf = new LeafNode(moveEntries); // Insert new entry into appropriate node - if (index <= leaf.entries.length) { + if (index < midIndex) { leaf.entries.splice(index, 0, entry); } else { - newLeaf.entries.splice(index - leaf.entries.length, 0, entry); + path.leafNode = newLeaf; + path.leafIndex -= leaf.entries.length; + newLeaf.entries.splice(path.leafIndex, 0, entry); } - return new Split(this.keyFromEntry(moveEntries[0]), newLeaf); + return new Split(this.keyFromEntry(moveEntries[0]), newLeaf, index < midIndex ? 0 : 1); } private branchInsert(path: Path, branchIndex: number, split: Split): Split | undefined { const pathBranch = path.branches[branchIndex]; const { index, node } = pathBranch; - if (node.nodes.length < NodeCapacity) { // no split needed - node.partitions.splice(index, 0, split.key); - node.nodes.splice(index + 1, 0, split.right); + pathBranch.index += split.indexDelta; + node.partitions.splice(index, 0, split.key); + node.nodes.splice(index + 1, 0, split.right); + if (node.nodes.length <= NodeCapacity) { // no split needed return undefined; } // Full. Split needed - const midIndex = (node.nodes.length + 1) >>> 1; + const midIndex = node.nodes.length >>> 1; const movePartitions = node.partitions.splice(midIndex); - node.partitions.pop(); // Remove the extra partition + const newPartition = node.partitions.pop()!; // Extra partition promoted to parent const moveNodes = node.nodes.splice(midIndex); // New node const newBranch = new BranchNode(movePartitions, moveNodes); - // Insert into appropriate node - if (index < node.nodes.length) { - node.partitions.splice(index, 0, split.key); - node.nodes.splice(index + 1, 0, split.right); - } else { - pathBranch.index -= node.nodes.length; - newBranch.partitions.splice(pathBranch.index, 0, split.key); - newBranch.nodes.splice(pathBranch.index + 1, 0, split.right); + if (pathBranch.index >= midIndex) { // If new entry in new node, slide the index + pathBranch.index -= midIndex; } - return new Split(this.firstKeyOfNode(newBranch), newBranch); - } - - private firstKeyOfNode(node: ITreeNode): TKey { - if (node instanceof LeafNode) { - return this.keyFromEntry((node as LeafNode).entries[0]!); - } else { - return this.firstKeyOfNode((node as BranchNode).nodes[0]); - } + return new Split(newPartition, newBranch, pathBranch.index < midIndex ? 0 : 1); } private rebalanceLeaf(path: Path, depth: number): ITreeNode | undefined { @@ -690,7 +681,6 @@ export class BTree { leftSib.partitions.push(pKey); leftSib.partitions.push(...branch.partitions); leftSib.nodes.push(...branch.nodes); - pNode.partitions.splice(pIndex - 1, 1); pNode.nodes.splice(pIndex, 1); pathBranch.node = leftSib; pathBranch.index += leftSib.nodes.length; @@ -718,6 +708,7 @@ class Split { constructor( public key: TKey, public right: ITreeNode, + public indexDelta: number, ) { } } diff --git a/test/b-tree.branching.test.ts b/test/b-tree.branching.test.ts index b5dfe1a..afa4209 100644 --- a/test/b-tree.branching.test.ts +++ b/test/b-tree.branching.test.ts @@ -163,9 +163,13 @@ describe('Branching BTree', () => { function addRange(starting: number, count: number) { const s = Math.sign(count); for (let i = 0; i !== count; i += s) { - if (!tree.insert(i + starting).on) { // Expects here is slow + const path = tree.insert(i + starting); + if (!path.on) { // Expects here is slow throw new Error("Failed to insert " + (i + starting)); } + if (tree.at(path) !== i + starting) { + throw new Error(`Path not maintained: Expected ${i + starting} but got ${tree.at(path)}`); + } } } @@ -173,9 +177,14 @@ describe('Branching BTree', () => { const range = [...Array(end - start + 1).keys()]; while (range.length) { const index = Math.floor(Math.random() * range.length); - if (!tree.insert(range.splice(index, 1)[0]).on) { + const value = range.splice(index, 1)[0]; + const path = tree.insert(value); + if (!path.on) { throw new Error("Failed to insert " + index); } + if (tree.at(path) !== value) { + throw new Error(`Path not maintained: Expected ${value} but got ${tree.at(path)}`); + } } }