diff --git a/package.json b/package.json index 8ee3af3..680b283 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "data-structure-typed", - "version": "1.39.0", + "version": "1.39.1", "description": "Data Structures of Javascript & TypeScript. Binary Tree, BST, Graph, Heap, Priority Queue, Linked List, Queue, Deque, Stack, AVL Tree, Tree Multiset, Trie, Directed Graph, Undirected Graph, Singly Linked List, Doubly Linked List, Max Heap, Max Priority Queue, Min Heap, Min Priority Queue.", "main": "dist/cjs/index.js", "module": "dist/mjs/index.js", diff --git a/src/data-structures/binary-tree/avl-tree.ts b/src/data-structures/binary-tree/avl-tree.ts index f9bcdfd..47eb26f 100644 --- a/src/data-structures/binary-tree/avl-tree.ts +++ b/src/data-structures/binary-tree/avl-tree.ts @@ -7,7 +7,7 @@ */ import {BST, BSTNode} from './bst'; import type {AVLTreeNodeNested, AVLTreeOptions, BinaryTreeDeletedResult, BinaryTreeNodeKey} from '../../types'; -import {MapCallback} from '../../types'; +import {OneParamCallback} from '../../types'; import {IBinaryTree} from '../../interfaces'; export class AVLTreeNode = AVLTreeNodeNested> extends BSTNode { @@ -21,7 +21,8 @@ export class AVLTreeNode = AVLTreeNodeNeste export class AVLTree = AVLTreeNode>> extends BST - implements IBinaryTree { + implements IBinaryTree +{ /** * This is a constructor function for an AVL tree data structure in TypeScript. * @param {AVLTreeOptions} [options] - The `options` parameter is an optional object that can be passed to the @@ -73,7 +74,7 @@ export class AVLTree = AVLTreeNode` objects. */ - override delete>( + override delete>( identifier: ReturnType, callback: C = this._defaultCallbackByKey as C ): BinaryTreeDeletedResult[] { @@ -160,7 +161,7 @@ export class AVLTree = AVLTreeNode = BinaryTree * @template N - The type of the binary tree's nodes. */ export class BinaryTree = BinaryTreeNode>> - implements IBinaryTree { + implements IBinaryTree +{ /** * Creates a new instance of BinaryTree. * @param {BinaryTreeOptions} [options] - The options for the binary tree. @@ -285,10 +278,6 @@ export class BinaryTree = BinaryTreeNode return keysOrNodes.length === this.addMany(keysOrNodes, data).length; } - delete>(identifier: ReturnType | N): BinaryTreeDeletedResult[]; - - delete>(identifier: ReturnType | N, callback: C): BinaryTreeDeletedResult[]; - /** * The `delete` function removes a node from a binary search tree and returns the deleted node along * with the parent node that needs to be balanced. @@ -303,13 +292,13 @@ export class BinaryTree = BinaryTreeNode * included in the result. The `callback` parameter has a default value of * `this._defaultCallbackByKey`, which */ - delete>( - identifier: ReturnType | N, + delete>( + identifier: ReturnType | null, callback: C = this._defaultCallbackByKey as C ): BinaryTreeDeletedResult[] { const bstDeletedResult: BinaryTreeDeletedResult[] = []; if (!this.root) return bstDeletedResult; - if (identifier instanceof BinaryTreeNode) callback = (node => node) as C; + if ((identifier as any) instanceof BinaryTreeNode) callback = (node => node) as C; const curr = this.get(identifier, callback); if (!curr) return bstDeletedResult; @@ -320,7 +309,8 @@ export class BinaryTree = BinaryTreeNode if (!curr.left) { if (!parent) { - if (curr.right !== undefined) this._setRoot(curr.right); + // Handle the case when there's only one root node + this._setRoot(null); } else { const {familyPosition: fp} = curr; if (fp === FamilyPosition.LEFT || fp === FamilyPosition.ROOT_LEFT) { @@ -405,7 +395,7 @@ export class BinaryTree = BinaryTreeNode return -1; } - const stack: { node: N; depth: number }[] = [{node: beginRoot, depth: 0}]; + const stack: {node: N; depth: number}[] = [{node: beginRoot, depth: 0}]; let maxHeight = 0; while (stack.length > 0) { @@ -489,29 +479,6 @@ export class BinaryTree = BinaryTreeNode return this.getMinHeight(beginRoot) + 1 >= this.getHeight(beginRoot); } - getNodes>(identifier: ReturnType | N): N[]; - - getNodes>(identifier: ReturnType | N, callback: C): N[]; - - getNodes>(identifier: ReturnType | N, onlyOne: boolean): N[]; - - getNodes>(identifier: ReturnType | N, callback: C, onlyOne: boolean): N[]; - - getNodes>( - identifier: ReturnType | N, - callback: C, - onlyOne: boolean, - beginRoot: N | null - ): N[]; - - getNodes>( - identifier: ReturnType | N, - callback: C, - onlyOne: boolean, - beginRoot: N | null, - iterationType: IterationType - ): N[]; - /** * The function `getNodes` returns an array of nodes that match a given node property, using either * recursive or iterative traversal. @@ -533,15 +500,15 @@ export class BinaryTree = BinaryTreeNode * traverse the binary tree. It can have two possible values: * @returns The function `getNodes` returns an array of nodes (`N[]`). */ - getNodes>( - identifier: ReturnType | N, + getNodes>( + identifier: ReturnType | null, callback: C = this._defaultCallbackByKey as C, onlyOne = false, beginRoot: N | null = this.root, iterationType = this.iterationType ): N[] { if (!beginRoot) return []; - if (identifier instanceof BinaryTreeNode) callback = (node => node) as C; + if ((identifier as any) instanceof BinaryTreeNode) callback = (node => node) as C; const ans: N[] = []; if (iterationType === IterationType.RECURSIVE) { @@ -574,14 +541,6 @@ export class BinaryTree = BinaryTreeNode return ans; } - has>(identifier: ReturnType | N): boolean; - - has>(identifier: ReturnType | N, callback: C): boolean; - - has>(identifier: ReturnType | N, beginRoot: N | null): boolean; - - has>(identifier: ReturnType | N, callback: C, beginRoot: N | null): boolean; - /** * The function checks if a binary tree has a node with a given property or key. * @param {BinaryTreeNodeKey | N} identifier - The `identifier` parameter is the key or value of @@ -599,32 +558,17 @@ export class BinaryTree = BinaryTreeNode * performed when searching for nodes in the binary tree. It can have one of the following values: * @returns a boolean value. */ - has>( - identifier: ReturnType | N, + has>( + identifier: ReturnType | null, callback: C = this._defaultCallbackByKey as C, beginRoot = this.root, iterationType = this.iterationType ): boolean { - if (identifier instanceof BinaryTreeNode) callback = (node => node) as C; + if ((identifier as any) instanceof BinaryTreeNode) callback = (node => node) as C; // TODO may support finding node by value equal return this.getNodes(identifier, callback, true, beginRoot, iterationType).length > 0; } - get>(identifier: ReturnType | N): N | null; - - get>(identifier: ReturnType | N, callback: C): N | null; - - get>(identifier: ReturnType | N, beginRoot: N | null): N | null; - - get>(identifier: ReturnType | N, callback: C, beginRoot: N | null): N | null; - - get>( - identifier: ReturnType | N, - callback: C, - beginRoot: N | null, - iterationType: IterationType - ): N | null; - /** * The function `get` returns the first node in a binary tree that matches the given property or key. * @param {BinaryTreeNodeKey | N} identifier - The `identifier` parameter is the key or value of @@ -640,13 +584,13 @@ export class BinaryTree = BinaryTreeNode * performed when searching for a node in the binary tree. It can have one of the following values: * @returns either the found node (of type N) or null if no node is found. */ - get>( - identifier: ReturnType | N, + get>( + identifier: ReturnType | null, callback: C = this._defaultCallbackByKey as C, beginRoot = this.root, iterationType = this.iterationType ): N | null { - if (identifier instanceof BinaryTreeNode) callback = (node => node) as C; + if ((identifier as any) instanceof BinaryTreeNode) callback = (node => node) as C; // TODO may support finding node by value equal return this.getNodes(identifier, callback, true, beginRoot, iterationType)[0] ?? null; } @@ -750,7 +694,7 @@ export class BinaryTree = BinaryTreeNode * possible values: * @returns The function `isSubtreeBST` returns a boolean value. */ - isSubtreeBST(beginRoot: N, iterationType = this.iterationType): boolean { + isSubtreeBST(beginRoot: N | null, iterationType = this.iterationType): boolean { // TODO there is a bug if (!beginRoot) return true; @@ -805,16 +749,16 @@ export class BinaryTree = BinaryTreeNode * start from the root of the tree. * @param iterationType - The `iterationType` parameter determines the type of traversal to be * performed on the binary tree. It can have two possible values: - * @returns The function `subTreeTraverse` returns an array of `MapCallbackReturn`. + * @returns The function `subTreeTraverse` returns an array of `ReturnType>`. */ - subTreeTraverse>( + subTreeTraverse>( callback: C = this._defaultCallbackByKey as C, beginRoot: BinaryTreeNodeKey | N | null = this.root, iterationType = this.iterationType ): ReturnType[] { if (typeof beginRoot === 'number') beginRoot = this.get(beginRoot); - const ans: MapCallbackReturn[] = []; + const ans: ReturnType>[] = []; if (!beginRoot) return ans; if (iterationType === IterationType.RECURSIVE) { @@ -852,16 +796,16 @@ export class BinaryTree = BinaryTreeNode * is `null`, an empty array will be returned. * @param {IterationType} iterationType - The `iterationType` parameter determines the type of * iteration used in the depth-first search algorithm. It can have two possible values: - * @returns The function `dfs` returns an array of `MapCallbackReturn` values. + * @returns The function `dfs` returns an array of `ReturnType>` values. */ - dfs>( + dfs>( callback: C = this._defaultCallbackByKey as C, pattern: DFSOrderPattern = 'in', beginRoot: N | null = this.root, iterationType: IterationType = IterationType.ITERATIVE ): ReturnType[] { if (!beginRoot) return []; - const ans: MapCallbackReturn[] = []; + const ans: ReturnType>[] = []; if (iterationType === IterationType.RECURSIVE) { const _traverse = (node: N) => { switch (pattern) { @@ -888,7 +832,7 @@ export class BinaryTree = BinaryTreeNode _traverse(beginRoot); } else { // 0: visit, 1: print - const stack: { opt: 0 | 1; node: N | null | undefined }[] = [{opt: 0, node: beginRoot}]; + const stack: {opt: 0 | 1; node: N | null | undefined}[] = [{opt: 0, node: beginRoot}]; while (stack.length > 0) { const cur = stack.pop(); @@ -930,22 +874,22 @@ export class BinaryTree = BinaryTreeNode * function on each node. * @param callback - The `callback` parameter is a function that will be called for each node in the * breadth-first search. It takes a node of type `N` as its argument and returns a value of type - * `BFSCallbackReturn`. The default value for this parameter is `this._defaultCallbackByKey + * `ReturnType>`. The default value for this parameter is `this._defaultCallbackByKey * @param {N | null} beginRoot - The `beginRoot` parameter is the starting node for the breadth-first * search. It determines from which node the search will begin. If `beginRoot` is `null`, the search * will not be performed and an empty array will be returned. * @param iterationType - The `iterationType` parameter determines the type of iteration to be used * in the breadth-first search (BFS) algorithm. It can have two possible values: - * @returns The function `bfs` returns an array of `BFSCallbackReturn[]`. + * @returns The function `bfs` returns an array of `ReturnType>[]`. */ - bfs = BFSCallback>( + bfs>( callback: C = this._defaultCallbackByKey as C, beginRoot: N | null = this.root, iterationType = this.iterationType ): ReturnType[] { if (!beginRoot) return []; - const ans: BFSCallbackReturn[] = []; + const ans: ReturnType>[] = []; if (iterationType === IterationType.RECURSIVE) { const queue = new Queue([beginRoot]); @@ -995,7 +939,7 @@ export class BinaryTree = BinaryTreeNode * level in a binary tree. Each inner array contains the return type of the provided callback * function `C` applied to the nodes at that level. */ - listLevels = BFSCallback>( + listLevels>( callback: C = this._defaultCallbackByKey as C, beginRoot: N | null = this.root, iterationType = this.iterationType @@ -1054,7 +998,7 @@ export class BinaryTree = BinaryTreeNode * The `morris` function performs a depth-first traversal of a binary tree using the Morris traversal * algorithm and returns an array of values obtained by applying a callback function to each node. * @param callback - The `callback` parameter is a function that will be called on each node in the - * tree. It takes a node of type `N` as input and returns a value of type `MapCallbackReturn`. The + * tree. It takes a node of type `N` as input and returns a value of type `ReturnType>`. The * default value for this parameter is `this._defaultCallbackByKey`. * @param {DFSOrderPattern} [pattern=in] - The `pattern` parameter in the `morris` function * determines the order in which the nodes of a binary tree are traversed. It can have one of the @@ -1062,15 +1006,15 @@ export class BinaryTree = BinaryTreeNode * @param {N | null} beginRoot - The `beginRoot` parameter is the starting node for the Morris * traversal. It specifies the root node of the tree from which the traversal should begin. If * `beginRoot` is `null`, an empty array will be returned. - * @returns The `morris` function returns an array of `MapCallbackReturn` values. + * @returns The `morris` function returns an array of `ReturnType>` values. */ - morris>( + morris>( callback: C = this._defaultCallbackByKey as C, pattern: DFSOrderPattern = 'in', beginRoot: N | null = this.root ): ReturnType[] { if (beginRoot === null) return []; - const ans: MapCallbackReturn[] = []; + const ans: ReturnType>[] = []; let cur: N | null | undefined = beginRoot; const _reverseEdge = (node: N | null | undefined) => { @@ -1177,7 +1121,7 @@ export class BinaryTree = BinaryTreeNode * the tree's structure should be restored to its original state to maintain the tree's integrity. * This is because the purpose of the Morris algorithm is to save space rather than permanently alter the tree's shape. */ - protected _defaultCallbackByKey: DefaultMapCallback = node => node.key; + protected _defaultCallbackByKey: OneParamCallback = node => node.key; /** * The function `_addTo` adds a new node to a binary tree if there is an available position. diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index 9e73700..8b74002 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -5,14 +5,7 @@ * @copyright Copyright (c) 2022 Tyler Zeng * @license MIT License */ -import type { - BinaryTreeNodeKey, - BSTComparator, - BSTNodeNested, - BSTOptions, - MapCallback, - MapCallbackReturn -} from '../../types'; +import type {BinaryTreeNodeKey, BSTComparator, BSTNodeNested, BSTOptions, OneParamCallback} from '../../types'; import {CP, IterationType} from '../../types'; import {BinaryTree, BinaryTreeNode} from './binary-tree'; import {IBinaryTree} from '../../interfaces'; @@ -26,7 +19,8 @@ export class BSTNode = BSTNodeNested> extend export class BST = BSTNode>> extends BinaryTree - implements IBinaryTree { + implements IBinaryTree +{ /** * The constructor function initializes a binary search tree object with an optional comparator * function. @@ -227,7 +221,7 @@ export class BST = BSTNode> * callback. * @param {ReturnType | N} identifier - The `nodeProperty` parameter is used to specify the * property of the binary tree node that you want to search for. It can be either a specific key - * value (`BinaryTreeNodeKey`) or a custom callback function (`MapCallback`) that determines + * value (`BinaryTreeNodeKey`) or a custom callback function (`OneParamCallback`) that determines * whether a node matches the desired property. * @param callback - The `callback` parameter is a function that is used to determine whether a node * matches the desired property. It takes a node as input and returns a boolean value indicating @@ -240,8 +234,8 @@ export class BST = BSTNode> * @returns either the first node that matches the given nodeProperty and callback, or null if no * matching node is found. */ - override get>( - identifier: ReturnType | N, + override get>( + identifier: ReturnType | null, callback: C = this._defaultCallbackByKey as C, beginRoot = this.root, iterationType = this.iterationType @@ -291,8 +285,8 @@ export class BST = BSTNode> * traverse the binary tree. It can have one of the following values: * @returns an array of nodes (N[]). */ - override getNodes>( - identifier: ReturnType | N, + override getNodes>( + identifier: ReturnType | null, callback: C = this._defaultCallbackByKey as C, onlyOne = false, beginRoot: N | null = this.root, @@ -363,16 +357,16 @@ export class BST = BSTNode> * (`BinaryTreeNodeKey`), or `null` to * @param iterationType - The `iterationType` parameter determines whether the traversal should be * done recursively or iteratively. It can have two possible values: - * @returns The function `lesserOrGreaterTraverse` returns an array of `MapCallbackReturn`. + * @returns The function `lesserOrGreaterTraverse` returns an array of `ReturnType>`. */ - lesserOrGreaterTraverse>( + lesserOrGreaterTraverse>( callback: C = this._defaultCallbackByKey as C, lesserOrGreater: CP = CP.lt, targetNode: BinaryTreeNodeKey | N | null = this.root, iterationType = this.iterationType ): ReturnType[] { if (typeof targetNode === 'number') targetNode = this.get(targetNode); - const ans: MapCallbackReturn[] = []; + const ans: ReturnType>[] = []; if (!targetNode) return ans; const targetKey = targetNode.key; if (!this.root) return ans; diff --git a/src/data-structures/binary-tree/rb-tree.ts b/src/data-structures/binary-tree/rb-tree.ts index 45c363b..c4ad717 100644 --- a/src/data-structures/binary-tree/rb-tree.ts +++ b/src/data-structures/binary-tree/rb-tree.ts @@ -21,7 +21,8 @@ export class RBTreeNode = RBTreeNodeNested = RBTreeNode>> extends BST - implements IBinaryTree { + implements IBinaryTree +{ constructor(options?: RBTreeOptions) { super(options); } diff --git a/src/data-structures/binary-tree/tree-multiset.ts b/src/data-structures/binary-tree/tree-multiset.ts index f07d298..0dab37e 100644 --- a/src/data-structures/binary-tree/tree-multiset.ts +++ b/src/data-structures/binary-tree/tree-multiset.ts @@ -6,7 +6,7 @@ * @license MIT License */ import type {BinaryTreeNodeKey, TreeMultisetNodeNested, TreeMultisetOptions} from '../../types'; -import {BinaryTreeDeletedResult, CP, FamilyPosition, IterationType, MapCallback} from '../../types'; +import {BinaryTreeDeletedResult, CP, FamilyPosition, IterationType, OneParamCallback} from '../../types'; import {IBinaryTree} from '../../interfaces'; import {AVLTree, AVLTreeNode} from './avl-tree'; @@ -37,7 +37,8 @@ export class TreeMultisetNode< */ export class TreeMultiset = TreeMultisetNode>> extends AVLTree - implements IBinaryTree { + implements IBinaryTree +{ /** * The constructor function for a TreeMultiset class in TypeScript, which extends another class and sets an option to * merge duplicated values. @@ -274,7 +275,7 @@ export class TreeMultiset = TreeMultis * decremented by 1 and * @returns The method `delete` returns an array of `BinaryTreeDeletedResult` objects. */ - override delete>( + override delete>( identifier: ReturnType, callback: C = this._defaultCallbackByKey as C, ignoreCount = false diff --git a/src/data-structures/graph/abstract-graph.ts b/src/data-structures/graph/abstract-graph.ts index adc52db..d7d5b08 100644 --- a/src/data-structures/graph/abstract-graph.ts +++ b/src/data-structures/graph/abstract-graph.ts @@ -105,7 +105,8 @@ export abstract class AbstractEdge { export abstract class AbstractGraph< V extends AbstractVertex = AbstractVertex, E extends AbstractEdge = AbstractEdge -> implements IGraph { +> implements IGraph +{ private _vertices: Map = new Map(); get vertices(): Map { @@ -196,7 +197,7 @@ export abstract class AbstractGraph< * @returns a boolean value. It returns true if at least one vertex was successfully removed, and false if no vertices * were removed. */ - removeAllVertices(vertices: V[] | VertexKey[]): boolean { + removeManyVertices(vertices: V[] | VertexKey[]): boolean { const removed: boolean[] = []; for (const v of vertices) { removed.push(this.deleteVertex(v)); @@ -553,14 +554,14 @@ export abstract class AbstractGraph< } getMinDist && - distMap.forEach((d, v) => { - if (v !== srcVertex) { - if (d < minDist) { - minDist = d; - if (genPaths) minDest = v; + distMap.forEach((d, v) => { + if (v !== srcVertex) { + if (d < minDist) { + minDist = d; + if (genPaths) minDest = v; + } } - } - }); + }); genPaths && getPaths(minDest); @@ -622,7 +623,7 @@ export abstract class AbstractGraph< if (vertexOrKey instanceof AbstractVertex) distMap.set(vertexOrKey, Infinity); } - const heap = new PriorityQueue<{ key: number; val: V }>({comparator: (a, b) => a.key - b.key}); + const heap = new PriorityQueue<{key: number; val: V}>({comparator: (a, b) => a.key - b.key}); heap.add({key: 0, val: srcVertex}); distMap.set(srcVertex, 0); @@ -851,7 +852,7 @@ export abstract class AbstractGraph< * `predecessor` property is a 2D array of vertices (or `null`) representing the predecessor vertices in the shortest * path between vertices in the */ - floyd(): { costs: number[][]; predecessor: (V | null)[][] } { + floyd(): {costs: number[][]; predecessor: (V | null)[][]} { const idAndVertices = [...this._vertices]; const n = idAndVertices.length; diff --git a/src/data-structures/graph/directed-graph.ts b/src/data-structures/graph/directed-graph.ts index cc29bf3..175e6bc 100644 --- a/src/data-structures/graph/directed-graph.ts +++ b/src/data-structures/graph/directed-graph.ts @@ -64,7 +64,8 @@ export class DirectedEdge extends AbstractEdge { export class DirectedGraph = DirectedVertex, E extends DirectedEdge = DirectedEdge> extends AbstractGraph - implements IGraph { + implements IGraph +{ /** * The constructor function initializes an instance of a class. */ diff --git a/src/data-structures/graph/map-graph.ts b/src/data-structures/graph/map-graph.ts index acfa6fa..8d7d71e 100644 --- a/src/data-structures/graph/map-graph.ts +++ b/src/data-structures/graph/map-graph.ts @@ -109,9 +109,9 @@ export class MapGraph = MapVertex, E extends MapEd */ override createVertex( key: VertexKey, - val?: V['val'], lat: number = this.origin[0], - long: number = this.origin[1] + long: number = this.origin[1], + val?: V['val'] ): V { return new MapVertex(key, lat, long, val) as V; } diff --git a/src/data-structures/graph/undirected-graph.ts b/src/data-structures/graph/undirected-graph.ts index 8bdf1c2..1380542 100644 --- a/src/data-structures/graph/undirected-graph.ts +++ b/src/data-structures/graph/undirected-graph.ts @@ -51,11 +51,12 @@ export class UndirectedEdge extends AbstractEdge { } export class UndirectedGraph< - V extends UndirectedVertex = UndirectedVertex, - E extends UndirectedEdge = UndirectedEdge -> + V extends UndirectedVertex = UndirectedVertex, + E extends UndirectedEdge = UndirectedEdge + > extends AbstractGraph - implements IGraph { + implements IGraph +{ /** * The constructor initializes a new Map object to store edges. */ diff --git a/src/data-structures/hash/hash-map.ts b/src/data-structures/hash/hash-map.ts index 5231237..f3a5213 100644 --- a/src/data-structures/hash/hash-map.ts +++ b/src/data-structures/hash/hash-map.ts @@ -157,7 +157,7 @@ export class HashMap { } } - * entries(): IterableIterator<[K, V]> { + *entries(): IterableIterator<[K, V]> { for (const bucket of this.table) { if (bucket) { for (const [key, value] of bucket) { diff --git a/src/data-structures/hash/tree-map.ts b/src/data-structures/hash/tree-map.ts index a6d743d..fe86360 100644 --- a/src/data-structures/hash/tree-map.ts +++ b/src/data-structures/hash/tree-map.ts @@ -1,2 +1 @@ -export class TreeMap { -} +export class TreeMap {} diff --git a/src/data-structures/hash/tree-set.ts b/src/data-structures/hash/tree-set.ts index 65f14db..591aeda 100644 --- a/src/data-structures/hash/tree-set.ts +++ b/src/data-structures/hash/tree-set.ts @@ -1,2 +1 @@ -export class TreeSet { -} +export class TreeSet {} diff --git a/src/data-structures/heap/heap.ts b/src/data-structures/heap/heap.ts index b4a197b..4f3fbe6 100644 --- a/src/data-structures/heap/heap.ts +++ b/src/data-structures/heap/heap.ts @@ -11,7 +11,7 @@ export class Heap { protected nodes: E[] = []; protected readonly comparator: Comparator; - constructor(options: { comparator: Comparator; nodes?: E[] }) { + constructor(options: {comparator: Comparator; nodes?: E[]}) { this.comparator = options.comparator; if (options.nodes && options.nodes.length > 0) { this.nodes = options.nodes; @@ -39,7 +39,7 @@ export class Heap { * @returns A new Heap instance. * @param options */ - static heapify(options: { nodes: E[]; comparator: Comparator }): Heap { + static heapify(options: {nodes: E[]; comparator: Comparator}): Heap { return new Heap(options); } diff --git a/src/data-structures/heap/max-heap.ts b/src/data-structures/heap/max-heap.ts index 139ef64..be2c9b1 100644 --- a/src/data-structures/heap/max-heap.ts +++ b/src/data-structures/heap/max-heap.ts @@ -11,7 +11,7 @@ import type {Comparator} from '../../types'; export class MaxHeap extends Heap { constructor( - options: { comparator: Comparator; nodes?: E[] } = { + options: {comparator: Comparator; nodes?: E[]} = { comparator: (a: E, b: E) => { if (!(typeof a === 'number' && typeof b === 'number')) { throw new Error('The a, b params of compare function must be number'); diff --git a/src/data-structures/heap/min-heap.ts b/src/data-structures/heap/min-heap.ts index 5057017..dc86f87 100644 --- a/src/data-structures/heap/min-heap.ts +++ b/src/data-structures/heap/min-heap.ts @@ -11,7 +11,7 @@ import type {Comparator} from '../../types'; export class MinHeap extends Heap { constructor( - options: { comparator: Comparator; nodes?: E[] } = { + options: {comparator: Comparator; nodes?: E[]} = { comparator: (a: E, b: E) => { if (!(typeof a === 'number' && typeof b === 'number')) { throw new Error('The a, b params of compare function must be number'); diff --git a/src/data-structures/linked-list/singly-linked-list.ts b/src/data-structures/linked-list/singly-linked-list.ts index 18233d8..c4e1da9 100644 --- a/src/data-structures/linked-list/singly-linked-list.ts +++ b/src/data-structures/linked-list/singly-linked-list.ts @@ -485,7 +485,7 @@ export class SinglyLinkedList { return count; } - * [Symbol.iterator]() { + *[Symbol.iterator]() { let current = this.head; while (current) { diff --git a/src/data-structures/matrix/matrix.ts b/src/data-structures/matrix/matrix.ts index 7e8ae4b..8f27617 100644 --- a/src/data-structures/matrix/matrix.ts +++ b/src/data-structures/matrix/matrix.ts @@ -14,7 +14,7 @@ export class MatrixNTI2D { * given initial value or 0 if not provided. * @param options - An object containing the following properties: */ - constructor(options: { row: number; col: number; initialVal?: V }) { + constructor(options: {row: number; col: number; initialVal?: V}) { const {row, col, initialVal} = options; this._matrix = new Array(row).fill(undefined).map(() => new Array(col).fill(initialVal || 0)); } diff --git a/src/data-structures/matrix/matrix2d.ts b/src/data-structures/matrix/matrix2d.ts index f6cfd72..ab9bfef 100644 --- a/src/data-structures/matrix/matrix2d.ts +++ b/src/data-structures/matrix/matrix2d.ts @@ -5,7 +5,7 @@ * @copyright Copyright (c) 2022 Tyler Zeng * @license MIT License */ -import Vector2D from './vector2d'; +import {Vector2D} from './vector2d'; export class Matrix2D { private readonly _matrix: number[][]; @@ -209,5 +209,3 @@ export class Matrix2D { return new Vector2D(this._matrix[0][0], this._matrix[1][0]); } } - -export default Matrix2D; diff --git a/src/data-structures/matrix/vector2d.ts b/src/data-structures/matrix/vector2d.ts index e8e4074..1b2ff44 100644 --- a/src/data-structures/matrix/vector2d.ts +++ b/src/data-structures/matrix/vector2d.ts @@ -10,8 +10,7 @@ export class Vector2D { public x: number = 0, public y: number = 0, public w: number = 1 // needed for matrix multiplication - ) { - } + ) {} /** * The function checks if the x and y values of a point are both zero. @@ -313,5 +312,3 @@ export class Vector2D { this.y = 0; } } - -export default Vector2D; diff --git a/src/data-structures/priority-queue/max-priority-queue.ts b/src/data-structures/priority-queue/max-priority-queue.ts index 409c99f..dbb0793 100644 --- a/src/data-structures/priority-queue/max-priority-queue.ts +++ b/src/data-structures/priority-queue/max-priority-queue.ts @@ -10,7 +10,7 @@ import type {Comparator} from '../../types'; export class MaxPriorityQueue extends PriorityQueue { constructor( - options: { comparator: Comparator; nodes?: E[] } = { + options: {comparator: Comparator; nodes?: E[]} = { comparator: (a: E, b: E) => { if (!(typeof a === 'number' && typeof b === 'number')) { throw new Error('The a, b params of compare function must be number'); diff --git a/src/data-structures/priority-queue/min-priority-queue.ts b/src/data-structures/priority-queue/min-priority-queue.ts index da8ab64..8b8386f 100644 --- a/src/data-structures/priority-queue/min-priority-queue.ts +++ b/src/data-structures/priority-queue/min-priority-queue.ts @@ -10,7 +10,7 @@ import type {Comparator} from '../../types'; export class MinPriorityQueue extends PriorityQueue { constructor( - options: { comparator: Comparator; nodes?: E[] } = { + options: {comparator: Comparator; nodes?: E[]} = { comparator: (a: E, b: E) => { if (!(typeof a === 'number' && typeof b === 'number')) { throw new Error('The a, b params of compare function must be number'); diff --git a/src/data-structures/priority-queue/priority-queue.ts b/src/data-structures/priority-queue/priority-queue.ts index 60deb98..edfbaf2 100644 --- a/src/data-structures/priority-queue/priority-queue.ts +++ b/src/data-structures/priority-queue/priority-queue.ts @@ -10,7 +10,7 @@ import {Heap} from '../heap'; import {Comparator} from '../../types'; export class PriorityQueue extends Heap { - constructor(options: { comparator: Comparator; nodes?: E[] }) { + constructor(options: {comparator: Comparator; nodes?: E[]}) { super(options); } } diff --git a/src/data-structures/queue/deque.ts b/src/data-structures/queue/deque.ts index 9faabda..3290ab1 100644 --- a/src/data-structures/queue/deque.ts +++ b/src/data-structures/queue/deque.ts @@ -9,8 +9,7 @@ import {DoublyLinkedList} from '../linked-list'; // O(n) time complexity of obtaining the value // O(1) time complexity of adding at the beginning and the end -export class Deque extends DoublyLinkedList { -} +export class Deque extends DoublyLinkedList {} // O(1) time complexity of obtaining the value // O(n) time complexity of adding at the beginning and the end @@ -20,9 +19,9 @@ export class ObjectDeque { if (capacity !== undefined) this._capacity = capacity; } - private _nodes: { [key: number]: E } = {}; + private _nodes: {[key: number]: E} = {}; - get nodes(): { [p: number]: E } { + get nodes(): {[p: number]: E} { return this._nodes; } @@ -157,7 +156,7 @@ export class ObjectDeque { return this._size <= 0; } - protected _seNodes(value: { [p: number]: E }) { + protected _seNodes(value: {[p: number]: E}) { this._nodes = value; } diff --git a/src/data-structures/queue/queue.ts b/src/data-structures/queue/queue.ts index d11dd92..4549b31 100644 --- a/src/data-structures/queue/queue.ts +++ b/src/data-structures/queue/queue.ts @@ -183,7 +183,7 @@ export class Queue { return new Queue(this.nodes.slice(this.offset)); } - * [Symbol.iterator]() { + *[Symbol.iterator]() { for (const item of this.nodes) { yield item; } diff --git a/src/interfaces/binary-tree.ts b/src/interfaces/binary-tree.ts index 61afbfd..1ff1f53 100644 --- a/src/interfaces/binary-tree.ts +++ b/src/interfaces/binary-tree.ts @@ -1,10 +1,10 @@ import {BinaryTreeNode} from '../data-structures'; -import {BinaryTreeDeletedResult, BinaryTreeNodeKey, BinaryTreeNodeNested, MapCallback} from '../types'; +import {BinaryTreeDeletedResult, BinaryTreeNodeKey, BinaryTreeNodeNested, OneParamCallback} from '../types'; export interface IBinaryTree = BinaryTreeNodeNested> { createNode(key: BinaryTreeNodeKey, val?: N['val']): N; add(keyOrNode: BinaryTreeNodeKey | N | null, val?: N['val']): N | null | undefined; - delete>(identifier: ReturnType | N, callback: C): BinaryTreeDeletedResult[]; + delete>(identifier: ReturnType | null, callback: C): BinaryTreeDeletedResult[]; } diff --git a/src/types/data-structures/binary-tree/binary-tree.ts b/src/types/data-structures/binary-tree/binary-tree.ts index 61b63bb..e037ede 100644 --- a/src/types/data-structures/binary-tree/binary-tree.ts +++ b/src/types/data-structures/binary-tree/binary-tree.ts @@ -24,10 +24,6 @@ export enum FamilyPosition { export type BinaryTreeNodeKey = number; -export type BFSCallback = (node: N, level?: number) => D; - -export type BFSCallbackReturn = ReturnType>; - export type BinaryTreeDeletedResult = { deleted: N | null | undefined; needBalanced: N | null }; export type BinaryTreeNodeNested = BinaryTreeNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> diff --git a/src/types/data-structures/matrix/navigator.ts b/src/types/data-structures/matrix/navigator.ts index 34eddd9..9d8b9a9 100644 --- a/src/types/data-structures/matrix/navigator.ts +++ b/src/types/data-structures/matrix/navigator.ts @@ -1,6 +1,6 @@ export type Direction = 'up' | 'right' | 'down' | 'left'; -export type Turning = { [key in Direction]: Direction }; +export type Turning = {[key in Direction]: Direction}; export type NavigatorParams = { matrix: T[][]; diff --git a/src/types/helpers.ts b/src/types/helpers.ts index 0d80e9e..acd9a20 100644 --- a/src/types/helpers.ts +++ b/src/types/helpers.ts @@ -1,14 +1,8 @@ -import {BinaryTreeNodeKey} from './data-structures'; - export type Comparator = (a: T, b: T) => number; export type DFSOrderPattern = 'pre' | 'in' | 'post'; -export type MapCallback = (node: N) => D; - -export type DefaultMapCallback = (node: N) => D; - -export type MapCallbackReturn = ReturnType>; +export type OneParamCallback = (node: N) => D; export enum CP { lt = 'lt', diff --git a/src/types/utils/utils.ts b/src/types/utils/utils.ts index 1f3a505..f4d26c4 100644 --- a/src/types/utils/utils.ts +++ b/src/types/utils/utils.ts @@ -1,5 +1,5 @@ export type ToThunkFn = () => ReturnType; -export type Thunk = () => ReturnType & { __THUNK__: symbol }; +export type Thunk = () => ReturnType & {__THUNK__: symbol}; export type TrlFn = (...args: any[]) => any; export type TrlAsyncFn = (...args: any[]) => any; diff --git a/src/types/utils/validate-type.ts b/src/types/utils/validate-type.ts index 3ebf451..ac9ff28 100644 --- a/src/types/utils/validate-type.ts +++ b/src/types/utils/validate-type.ts @@ -1,6 +1,6 @@ -export type KeyValueObject = { [key: string]: any }; +export type KeyValueObject = {[key: string]: any}; -export type KeyValueObjectWithKey = { [key: string]: any; key: string | number | symbol }; +export type KeyValueObjectWithKey = {[key: string]: any; key: string | number | symbol}; export type NonNumberNonObjectButDefined = string | boolean | symbol | null; diff --git a/test/integration/bst.test.ts b/test/integration/bst.test.ts index 35a6061..0a33db7 100644 --- a/test/integration/bst.test.ts +++ b/test/integration/bst.test.ts @@ -183,7 +183,7 @@ describe('Individual package BST operations test', () => { }); it('should perform various operations on a Binary Search Tree with object values', () => { - const objBST = new BST<{ key: number; keyA: number }>(); + const objBST = new BST<{key: number; keyA: number}>(); expect(objBST).toBeInstanceOf(BST); objBST.add(11, {key: 11, keyA: 11}); objBST.add(3, {key: 3, keyA: 3}); diff --git a/test/unit/data-structures/binary-tree/avl-tree.test.ts b/test/unit/data-structures/binary-tree/avl-tree.test.ts index 5c99c38..e7e7d00 100644 --- a/test/unit/data-structures/binary-tree/avl-tree.test.ts +++ b/test/unit/data-structures/binary-tree/avl-tree.test.ts @@ -1,4 +1,4 @@ -import {AVLTree, AVLTreeNode, CP} from '../../../../src'; +import {AVLTree, AVLTreeNode, CP, IterationType} from '../../../../src'; describe('AVL Tree Test', () => { it('should perform various operations on a AVL Tree', () => { @@ -109,8 +109,117 @@ describe('AVL Tree Test', () => { }); }); +describe('AVL Tree Test recursively', () => { + it('should perform various operations on a AVL Tree', () => { + const arr = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]; + const tree = new AVLTree({iterationType: IterationType.RECURSIVE}); + + for (const i of arr) tree.add(i, i); + + const node6 = tree.get(6); + + expect(node6 && tree.getHeight(node6)).toBe(3); + expect(node6 && tree.getDepth(node6)).toBe(1); + + const getNodeById = tree.get(10); + expect(getNodeById?.key).toBe(10); + + const getMinNodeByRoot = tree.getLeftMost(); + expect(getMinNodeByRoot?.key).toBe(1); + + const node15 = tree.get(15); + const getMinNodeBySpecificNode = node15 && tree.getLeftMost(node15); + expect(getMinNodeBySpecificNode?.key).toBe(12); + + let subTreeSum = 0; + node15 && tree.subTreeTraverse(node => (subTreeSum += node.key), node15); + expect(subTreeSum).toBe(70); + + let lesserSum = 0; + tree.lesserOrGreaterTraverse(node => (lesserSum += node.key), CP.lt, 10); + expect(lesserSum).toBe(45); + + // node15 has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class. + expect(node15?.val).toBe(15); + + const dfs = tree.dfs(node => node, 'in'); + expect(dfs[0].key).toBe(1); + expect(dfs[dfs.length - 1].key).toBe(16); + + tree.perfectlyBalance(); + const bfs = tree.bfs(node => node); + expect(tree.isPerfectlyBalanced()).toBe(true); + expect(bfs[0].key).toBe(8); + expect(bfs[bfs.length - 1].key).toBe(16); + + expect(tree.delete(11)[0].deleted?.key).toBe(11); + expect(tree.isAVLBalanced()).toBe(true); + expect(node15 && tree.getHeight(node15)).toBe(2); + + expect(tree.delete(1)[0].deleted?.key).toBe(1); + expect(tree.isAVLBalanced()).toBe(true); + expect(tree.getHeight()).toBe(4); + + expect(tree.delete(4)[0].deleted?.key).toBe(4); + expect(tree.isAVLBalanced()).toBe(true); + expect(tree.getHeight()).toBe(4); + + expect(tree.delete(10)[0].deleted?.key).toBe(10); + expect(tree.isAVLBalanced()).toBe(true); + expect(tree.getHeight()).toBe(3); + + expect(tree.delete(15)[0].deleted?.key).toBe(15); + expect(tree.isAVLBalanced()).toBe(true); + + expect(tree.getHeight()).toBe(3); + + expect(tree.delete(5)[0].deleted?.key).toBe(5); + expect(tree.isAVLBalanced()).toBe(true); + expect(tree.getHeight()).toBe(3); + + expect(tree.delete(13)[0].deleted?.key).toBe(13); + expect(tree.isAVLBalanced()).toBe(true); + expect(tree.getHeight()).toBe(3); + + expect(tree.delete(3)[0].deleted?.key).toBe(3); + expect(tree.isAVLBalanced()).toBe(true); + expect(tree.getHeight()).toBe(3); + + expect(tree.delete(8)[0].deleted?.key).toBe(8); + expect(tree.isAVLBalanced()).toBe(true); + expect(tree.getHeight()).toBe(3); + + expect(tree.delete(6)[0].deleted?.key).toBe(6); + expect(tree.delete(6).length).toBe(0); + expect(tree.isAVLBalanced()).toBe(true); + expect(tree.getHeight()).toBe(2); + + expect(tree.delete(7)[0].deleted?.key).toBe(7); + expect(tree.isAVLBalanced()).toBe(true); + expect(tree.getHeight()).toBe(2); + + expect(tree.delete(9)[0].deleted?.key).toBe(9); + expect(tree.isAVLBalanced()).toBe(true); + expect(tree.getHeight()).toBe(2); + expect(tree.delete(14)[0].deleted?.key).toBe(14); + expect(tree.isAVLBalanced()).toBe(true); + expect(tree.getHeight()).toBe(1); + + expect(tree.isAVLBalanced()).toBe(true); + const lastBFSIds = tree.bfs(); + expect(lastBFSIds[0]).toBe(12); + expect(lastBFSIds[1]).toBe(2); + expect(lastBFSIds[2]).toBe(16); + + const lastBFSNodes = tree.bfs(node => node); + expect(lastBFSNodes[0].key).toBe(12); + expect(lastBFSNodes[1].key).toBe(2); + expect(lastBFSNodes[2].key).toBe(16); + }); +}); + describe('AVLTree APIs test', () => { - const avl = new AVLTree<{ id: number; text: string }>(); + const avl = new AVLTree<{id: number; text: string}>(); beforeEach(() => { avl.clear(); }); @@ -127,3 +236,34 @@ describe('AVLTree APIs test', () => { expect(bfsRes[0]).toBe(2); }); }); + +describe('AVLTree', () => { + it('should balance the tree using _balanceLR when nodes are added', () => { + const avlTree = new AVLTree(); + avlTree.add(10, 'A'); + avlTree.add(5, 'B'); + avlTree.add(15, 'C'); + avlTree.add(3, 'D'); + avlTree.add(7, 'E'); + + // Adding nodes to trigger _balanceLR + avlTree.add(12, 'F'); + + // You can add more specific assertions to check the tree's balance and structure. + }); + + it('should balance the tree using _balanceLR when nodes are deleted', () => { + const avlTree = new AVLTree(); + avlTree.add(10, 'A'); + avlTree.add(5, 'B'); + avlTree.add(15, 'C'); + avlTree.add(3, 'D'); + avlTree.add(7, 'E'); + avlTree.add(12, 'F'); + + // Deleting nodes to trigger _balanceLR + avlTree.delete(3); + + // You can add more specific assertions to check the tree's balance and structure. + }); +}); diff --git a/test/unit/data-structures/binary-tree/binary-tree.test.ts b/test/unit/data-structures/binary-tree/binary-tree.test.ts index 3398166..e15a7ed 100644 --- a/test/unit/data-structures/binary-tree/binary-tree.test.ts +++ b/test/unit/data-structures/binary-tree/binary-tree.test.ts @@ -71,81 +71,112 @@ describe('BinaryTreeNode', () => { }); describe('BinaryTree', () => { - let binaryTree: BinaryTree; + let tree: BinaryTree; beforeEach(() => { - binaryTree = new BinaryTree(); + tree = new BinaryTree(); }); afterEach(() => { - binaryTree.clear(); + tree.clear(); }); - test('should add a node', () => { - const node = binaryTree.add(1); + it('should add a node', () => { + const node = tree.add(1); expect(node).not.toBeNull(); - expect(binaryTree.size).toBe(1); + expect(tree.size).toBe(1); }); - test('should delete a node', () => { - const node = binaryTree.add(1); - expect(binaryTree.size).toBe(1); + it('should delete a node', () => { + const node = tree.add(1); + expect(tree.size).toBe(1); if (node) { - const result = binaryTree.delete(node); + const result = tree.delete(node, node => node); expect(result).toHaveLength(1); - expect(binaryTree.size).toBe(0); + expect(tree.size).toBe(0); } }); - test('should add and find nodes', () => { - binaryTree.add(1); - binaryTree.add(2); - binaryTree.add(3); + it('should add and find nodes', () => { + tree.add(1); + tree.add(2); + tree.add(3); - expect(binaryTree.has(1)).toBe(true); - expect(binaryTree.has(2)).toBe(true); - expect(binaryTree.has(3)).toBe(true); - expect(binaryTree.has(4)).toBe(false); + expect(tree.has(1)).toBe(true); + expect(tree.has(2)).toBe(true); + expect(tree.has(3)).toBe(true); + expect(tree.has(4)).toBe(false); + const node4 = tree.get(4); + expect(tree.has(node4, node => node)).toBe(false); }); - test('should getDepth return correct depth', () => { - binaryTree.add(1); - expect(binaryTree.getDepth(1)).toBe(0); - binaryTree.add(2); - expect(binaryTree.getDepth(2)).toBe(1); - binaryTree.add(3); - expect(binaryTree.getDepth(3, 1)).toBe(1); - binaryTree.add(4); - expect(binaryTree.getDepth(4, 1)).toBe(2); - expect(binaryTree.getDepth(4)).toBe(2); - expect(binaryTree.getDepth(4, 2)).toBe(1); + it('should getDepth return correct depth', () => { + tree.add(1); + expect(tree.getDepth(1)).toBe(0); + tree.add(2); + expect(tree.getDepth(2)).toBe(1); + tree.add(3); + expect(tree.getDepth(3, 1)).toBe(1); + tree.add(4); + expect(tree.getDepth(4, 1)).toBe(2); + expect(tree.getDepth(4)).toBe(2); + expect(tree.getDepth(4, 2)).toBe(1); }); - test('should traverse in-order', () => { - binaryTree.add(4); - binaryTree.add(2); - binaryTree.add(6); - binaryTree.add(1); - binaryTree.add(3); - binaryTree.add(5); - binaryTree.add(7); + it('should traverse in-order', () => { + tree.add(4); + tree.add(2); + tree.add(6); + tree.add(1); + tree.add(3); + tree.add(5); + tree.add(7); - const inOrder = binaryTree.dfs(node => node.key); + const inOrder = tree.dfs(node => node.key); expect(inOrder).toEqual([1, 2, 3, 4, 5, 6, 7]); }); - test('should clear the tree', () => { - binaryTree.add(1); - binaryTree.add(2); + it('should getLeftMost', () => { + tree.addMany([4, 2, 6, 1, 3, 5, 7]); - expect(binaryTree.size).toBe(2); + const leftMost = tree.getLeftMost(tree.root, IterationType.RECURSIVE); + expect(leftMost?.key).toEqual(1); + const rightMost = tree.getRightMost(tree.root, IterationType.RECURSIVE); + expect(rightMost?.key).toEqual(7); + }); - binaryTree.clear(); + it('should isSubtreeBST', () => { + tree.addMany([ + new BinaryTreeNode(4, 4), + new BinaryTreeNode(2, 2), + new BinaryTreeNode(6, 6), + new BinaryTreeNode(1, 1), + new BinaryTreeNode(3, 3), + new BinaryTreeNode(5, 5), + new BinaryTreeNode(7, 7), + new BinaryTreeNode(4, 4) + ]); - expect(binaryTree.size).toBe(0); - expect(binaryTree.root).toBeNull(); + expect(tree.isSubtreeBST(tree.get(4), IterationType.RECURSIVE)).toBe(true); + }); + + it('should subTreeTraverse', () => { + tree.addMany([4, 2, 6, 1, 3, 5, 7]); + expect(tree.subTreeTraverse(node => node.key, tree.get(6), IterationType.RECURSIVE)).toEqual([6, 5, 7]); + }); + + it('should clear the tree', () => { + tree.add(1); + tree.add(2); + + expect(tree.size).toBe(2); + + tree.clear(); + + expect(tree.size).toBe(0); + expect(tree.root).toBeNull(); }); }); @@ -200,7 +231,7 @@ describe('BinaryTree Morris Traversal', () => { }); describe('BinaryTree APIs test', () => { - const avl = new AVLTree<{ id: number; text: string }>(); + const avl = new AVLTree<{id: number; text: string}>(); beforeEach(() => { avl.clear(); }); @@ -236,4 +267,195 @@ describe('BinaryTree traversals', () => { const levels = tree.listLevels(node => node.key); expect(levels).toEqual([[35], [20, 40], [15, 29, 50], [16, 28, 30, 45, 55]]); isDebug && console.log(levels); + + expect(tree.listLevels(node => node.key, tree.root, IterationType.RECURSIVE)).toEqual([ + [35], + [20, 40], + [15, 29, 50], + [16, 28, 30, 45, 55] + ]); + isDebug && console.log(levels); +}); + +describe('BinaryTree', () => { + let tree: BinaryTree; + + beforeEach(() => { + tree = new BinaryTree(); + }); + + afterEach(() => { + tree.clear(); + }); + + it('should create an empty BinaryTree', () => { + expect(tree.size).toBe(0); + expect(tree.isEmpty()).toBe(true); + expect(tree.root).toBe(null); + }); + + it('should add nodes to the tree', () => { + tree.add(5, 'A'); + tree.add(3, 'B'); + tree.add(7, 'C'); + + expect(tree.size).toBe(3); + expect(tree.isEmpty()).toBe(false); + expect(tree.root?.key).toBe(5); + }); + + it('should clear the BinaryTree', () => { + tree.add(5, 'A'); + tree.add(3, 'B'); + tree.add(7, 'C'); + + tree.clear(); + + expect(tree.size).toBe(0); + expect(tree.isEmpty()).toBe(true); + expect(tree.root).toBe(null); + }); + + it('should get nodes by key', () => { + tree.add(5, 'A'); + tree.add(3, 'B'); + tree.add(7, 'C'); + + const nodeA = tree.get(5); + const nodeB = tree.get(3); + + expect(nodeA?.key).toBe(5); + expect(nodeA?.val).toBe('A'); + expect(nodeB?.key).toBe(3); + expect(nodeB?.val).toBe('B'); + }); + + it('should return null when getting a non-existent node', () => { + tree.add(5, 'A'); + + const node = tree.get(3); + + expect(node).toBe(null); + }); + + it('should get the depth of a node', () => { + tree.add(5, 'A'); + tree.add(3, 'B'); + tree.add(7, 'C'); + + expect(tree.getDepth(7)).toBe(1); + expect(tree.getDepth(3)).toBe(1); + }); + + it('should get the height of the tree', () => { + tree.add(5, 'A'); + tree.add(3, 'B'); + tree.add(7, 'C'); + + expect(tree.getHeight()).toBe(1); + expect(tree.getHeight(undefined, IterationType.RECURSIVE)).toBe(1); + expect(tree.getMinHeight(undefined, IterationType.RECURSIVE)).toBe(1); + }); + + it('should check if the tree is a binary search tree', () => { + tree.add(5, 'A'); + tree.add(3, 'B'); + tree.add(7, 'C'); + + expect(tree.isBST()).toBe(true); + }); + + it('should perform a depth-first traversal', () => { + tree.add(5, 'A'); + tree.add(3, 'B'); + tree.add(7, 'C'); + + const result = tree.dfs(); + expect(result).toEqual([3, 5, 7]); + // Add assertions for the result of depth-first traversal + }); + + it('should perform a breadth-first traversal', () => { + tree.add(5, 'A'); + tree.add(3, 'B'); + tree.add(7, 'C'); + + const result = tree.bfs(node => node.key); + expect(result).toEqual([5, 3, 7]); + // Add assertions for the result of breadth-first traversal + }); + + it('should list levels of the tree', () => { + tree.add(5, 'A'); + tree.add(3, 'B'); + tree.add(7, 'C'); + + const levels = tree.listLevels(); + expect(levels).toEqual([[5], [3, 7]]); + // Add assertions for the levels of the tree + }); + + it('should delete nodes from the tree', () => { + tree.add(5, 'A'); + tree.add(3, 'B'); + tree.add(7, 'C'); + + tree.delete(3); + + expect(tree.size).toBe(2); + expect(tree.get(3)).toBe(null); + }); + + it('should check if the tree is perfectly balanced', () => { + tree.add(5, 'A'); + tree.add(3, 'B'); + tree.add(7, 'C'); + + expect(tree.isPerfectlyBalanced()).toBe(true); + }); + + it('should get nodes by a custom callback', () => { + tree.add(5, 'A'); + tree.add(3, 'B'); + tree.add(7, 'C'); + + const nodes = tree.getNodes('B', (node: BinaryTreeNode) => node.val); + + expect(nodes.length).toBe(1); + expect(nodes[0].key).toBe(3); + + const nodesRec = tree.getNodes( + 'B', + (node: BinaryTreeNode) => node.val, + false, + tree.root, + IterationType.RECURSIVE + ); + + expect(nodesRec.length).toBe(1); + expect(nodesRec[0].key).toBe(3); + }); + + it('should perform Morris traversal', () => { + tree.add(5, 'A'); + tree.add(3, 'B'); + tree.add(7, 'C'); + + const result = tree.morris(); + expect(result).toEqual([3, 5, 7]); + // Add assertions for the result of Morris traversal + }); + + it('should perform delete all', () => { + tree.add(5, 'A'); + tree.add(3, 'B'); + tree.add(7, 'C'); + + tree.delete(5); + tree.delete(7); + tree.delete(3); + + expect(tree.root).toBe(null); + expect(tree.getHeight()).toBe(-1); + }); }); diff --git a/test/unit/data-structures/binary-tree/bst.test.ts b/test/unit/data-structures/binary-tree/bst.test.ts index 1991ad0..b4d4a44 100644 --- a/test/unit/data-structures/binary-tree/bst.test.ts +++ b/test/unit/data-structures/binary-tree/bst.test.ts @@ -1,4 +1,4 @@ -import {BST, BSTNode, CP} from '../../../../src'; +import {BST, BSTNode, CP, IterationType} from '../../../../src'; import {isDebugTest} from '../../../config'; const isDebug = isDebugTest; @@ -189,7 +189,7 @@ describe('BST operations test', () => { }); it('should perform various operations on a Binary Search Tree with object values', () => { - const objBST = new BST<{ key: number; keyA: number }>(); + const objBST = new BST<{key: number; keyA: number}>(); expect(objBST).toBeInstanceOf(BST); objBST.add(11, {key: 11, keyA: 11}); objBST.add(3, {key: 3, keyA: 3}); @@ -260,7 +260,7 @@ describe('BST operations test', () => { objBST.perfectlyBalance(); expect(objBST.isPerfectlyBalanced()).toBe(true); - const bfsNodesAfterBalanced: BSTNode<{ key: number; keyA: number }>[] = []; + const bfsNodesAfterBalanced: BSTNode<{key: number; keyA: number}>[] = []; objBST.bfs(node => bfsNodesAfterBalanced.push(node)); expect(bfsNodesAfterBalanced[0].key).toBe(8); expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16); @@ -385,7 +385,397 @@ describe('BST operations test', () => { expect(bfsIDs[1]).toBe(12); expect(bfsIDs[2]).toBe(16); - const bfsNodes: BSTNode<{ key: number; keyA: number }>[] = []; + const bfsNodes: BSTNode<{key: number; keyA: number}>[] = []; + objBST.bfs(node => bfsNodes.push(node)); + expect(bfsNodes[0].key).toBe(2); + expect(bfsNodes[1].key).toBe(12); + expect(bfsNodes[2].key).toBe(16); + }); +}); + +describe('BST operations test recursively', () => { + it('should perform various operations on a Binary Search Tree with numeric values', () => { + const bst = new BST({iterationType: IterationType.RECURSIVE}); + expect(bst).toBeInstanceOf(BST); + bst.add(11, 11); + bst.add(3, 3); + const idsAndValues = [15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]; + bst.addMany(idsAndValues, idsAndValues, false); + expect(bst.root).toBeInstanceOf(BSTNode); + + if (bst.root) expect(bst.root.key).toBe(11); + + expect(bst.size).toBe(16); + + expect(bst.has(6)).toBe(true); + + const node6 = bst.get(6); + expect(node6 && bst.getHeight(6)).toBe(2); + expect(node6 && bst.getDepth(6)).toBe(3); + + const nodeId10 = bst.get(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = bst.get(9, node => node.val); + expect(nodeVal9?.key).toBe(9); + + const leftMost = bst.getLeftMost(); + expect(leftMost?.key).toBe(1); + + const node15 = bst.get(15); + const minNodeBySpecificNode = node15 && bst.getLeftMost(node15); + expect(minNodeBySpecificNode?.key).toBe(12); + + let subTreeSum = 0; + node15 && bst.subTreeTraverse(node => (subTreeSum += node.key), 15); + expect(subTreeSum).toBe(70); + + let lesserSum = 0; + bst.lesserOrGreaterTraverse(node => (lesserSum += node.key), CP.lt, 10); + expect(lesserSum).toBe(45); + + expect(node15).toBeInstanceOf(BSTNode); + + const node11 = bst.get(11); + expect(node11).toBeInstanceOf(BSTNode); + + const dfsInorderNodes = bst.dfs(node => node, 'in'); + expect(dfsInorderNodes[0].key).toBe(1); + expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16); + + bst.perfectlyBalance(); + expect(bst.isPerfectlyBalanced()).toBe(true); + + const bfsNodesAfterBalanced: BSTNode[] = []; + bst.bfs(node => bfsNodesAfterBalanced.push(node)); + expect(bfsNodesAfterBalanced[0].key).toBe(8); + expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16); + + const removed11 = bst.delete(11); + expect(removed11).toBeInstanceOf(Array); + expect(removed11[0]).toBeDefined(); + expect(removed11[0].deleted).toBeDefined(); + + if (removed11[0].deleted) expect(removed11[0].deleted.key).toBe(11); + + expect(bst.isAVLBalanced()).toBe(true); + + expect(bst.getHeight(15)).toBe(1); + + const removed1 = bst.delete(1); + expect(removed1).toBeInstanceOf(Array); + expect(removed1[0]).toBeDefined(); + expect(removed1[0].deleted).toBeDefined(); + if (removed1[0].deleted) expect(removed1[0].deleted.key).toBe(1); + + expect(bst.isAVLBalanced()).toBe(true); + + expect(bst.getHeight()).toBe(4); + + const removed4 = bst.delete(4); + expect(removed4).toBeInstanceOf(Array); + expect(removed4[0]).toBeDefined(); + expect(removed4[0].deleted).toBeDefined(); + if (removed4[0].deleted) expect(removed4[0].deleted.key).toBe(4); + expect(bst.isAVLBalanced()).toBe(true); + expect(bst.getHeight()).toBe(4); + + const removed10 = bst.delete(10); + expect(removed10).toBeInstanceOf(Array); + expect(removed10[0]).toBeDefined(); + expect(removed10[0].deleted).toBeDefined(); + if (removed10[0].deleted) expect(removed10[0].deleted.key).toBe(10); + expect(bst.isAVLBalanced()).toBe(false); + expect(bst.getHeight()).toBe(4); + + const removed15 = bst.delete(15); + expect(removed15).toBeInstanceOf(Array); + expect(removed15[0]).toBeDefined(); + expect(removed15[0].deleted).toBeDefined(); + if (removed15[0].deleted) expect(removed15[0].deleted.key).toBe(15); + + expect(bst.isAVLBalanced()).toBe(true); + expect(bst.getHeight()).toBe(3); + + const removed5 = bst.delete(5); + expect(removed5).toBeInstanceOf(Array); + expect(removed5[0]).toBeDefined(); + expect(removed5[0].deleted).toBeDefined(); + if (removed5[0].deleted) expect(removed5[0].deleted.key).toBe(5); + + expect(bst.isAVLBalanced()).toBe(true); + expect(bst.getHeight()).toBe(3); + + const removed13 = bst.delete(13); + expect(removed13).toBeInstanceOf(Array); + expect(removed13[0]).toBeDefined(); + expect(removed13[0].deleted).toBeDefined(); + if (removed13[0].deleted) expect(removed13[0].deleted.key).toBe(13); + expect(bst.isAVLBalanced()).toBe(true); + expect(bst.getHeight()).toBe(3); + + const removed3 = bst.delete(3); + expect(removed3).toBeInstanceOf(Array); + expect(removed3[0]).toBeDefined(); + expect(removed3[0].deleted).toBeDefined(); + if (removed3[0].deleted) expect(removed3[0].deleted.key).toBe(3); + expect(bst.isAVLBalanced()).toBe(false); + expect(bst.getHeight()).toBe(3); + + const removed8 = bst.delete(8); + expect(removed8).toBeInstanceOf(Array); + expect(removed8[0]).toBeDefined(); + expect(removed8[0].deleted).toBeDefined(); + if (removed8[0].deleted) expect(removed8[0].deleted.key).toBe(8); + expect(bst.isAVLBalanced()).toBe(true); + expect(bst.getHeight()).toBe(3); + + const removed6 = bst.delete(6); + expect(removed6).toBeInstanceOf(Array); + expect(removed6[0]).toBeDefined(); + expect(removed6[0].deleted).toBeDefined(); + if (removed6[0].deleted) expect(removed6[0].deleted.key).toBe(6); + expect(bst.delete(6).length).toBe(0); + expect(bst.isAVLBalanced()).toBe(false); + expect(bst.getHeight()).toBe(3); + + const removed7 = bst.delete(7); + expect(removed7).toBeInstanceOf(Array); + expect(removed7[0]).toBeDefined(); + expect(removed7[0].deleted).toBeDefined(); + if (removed7[0].deleted) expect(removed7[0].deleted.key).toBe(7); + expect(bst.isAVLBalanced()).toBe(false); + expect(bst.getHeight()).toBe(3); + + const removed9 = bst.delete(9); + expect(removed9).toBeInstanceOf(Array); + expect(removed9[0]).toBeDefined(); + expect(removed9[0].deleted).toBeDefined(); + if (removed9[0].deleted) expect(removed9[0].deleted.key).toBe(9); + expect(bst.isAVLBalanced()).toBe(false); + expect(bst.getHeight()).toBe(3); + + const removed14 = bst.delete(14); + expect(removed14).toBeInstanceOf(Array); + expect(removed14[0]).toBeDefined(); + expect(removed14[0].deleted).toBeDefined(); + if (removed14[0].deleted) expect(removed14[0].deleted.key).toBe(14); + expect(bst.isAVLBalanced()).toBe(false); + expect(bst.getHeight()).toBe(2); + + expect(bst.isAVLBalanced()).toBe(false); + + const bfsIDs: number[] = []; + bst.bfs(node => bfsIDs.push(node.key)); + expect(bfsIDs[0]).toBe(2); + expect(bfsIDs[1]).toBe(12); + expect(bfsIDs[2]).toBe(16); + + const bfsNodes: BSTNode[] = []; + bst.bfs(node => bfsNodes.push(node)); + expect(bfsNodes[0].key).toBe(2); + expect(bfsNodes[1].key).toBe(12); + expect(bfsNodes[2].key).toBe(16); + }); + + it('should perform various operations on a Binary Search Tree with object values', () => { + const objBST = new BST<{key: number; keyA: number}>(); + expect(objBST).toBeInstanceOf(BST); + objBST.add(11, {key: 11, keyA: 11}); + objBST.add(3, {key: 3, keyA: 3}); + const values = [ + {key: 15, keyA: 15}, + {key: 1, keyA: 1}, + {key: 8, keyA: 8}, + {key: 13, keyA: 13}, + {key: 16, keyA: 16}, + {key: 2, keyA: 2}, + {key: 6, keyA: 6}, + {key: 9, keyA: 9}, + {key: 12, keyA: 12}, + {key: 14, keyA: 14}, + {key: 4, keyA: 4}, + {key: 7, keyA: 7}, + {key: 10, keyA: 10}, + {key: 5, keyA: 5} + ]; + + objBST.addMany( + values.map(item => item.key), + values, + false + ); + + expect(objBST.root).toBeInstanceOf(BSTNode); + + if (objBST.root) expect(objBST.root.key).toBe(11); + + expect(objBST.has(6)).toBe(true); + + const node6 = objBST.get(6); + expect(node6 && objBST.getHeight(node6)).toBe(2); + expect(node6 && objBST.getDepth(node6)).toBe(3); + + const nodeId10 = objBST.get(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = objBST.get(9); + expect(nodeVal9?.key).toBe(9); + + const leftMost = objBST.getLeftMost(); + expect(leftMost?.key).toBe(1); + + const node15 = objBST.get(15); + expect(node15?.val).toEqual({key: 15, keyA: 15}); + const minNodeBySpecificNode = node15 && objBST.getLeftMost(node15); + expect(minNodeBySpecificNode?.key).toBe(12); + + let subTreeSum = 0; + node15 && objBST.subTreeTraverse(node => (subTreeSum += node.key), node15); + expect(subTreeSum).toBe(70); + + let lesserSum = 0; + objBST.lesserOrGreaterTraverse(node => (lesserSum += node.key), CP.lt, 10); + expect(lesserSum).toBe(45); + + expect(node15).toBeInstanceOf(BSTNode); + + const node11 = objBST.get(11); + expect(node11).toBeInstanceOf(BSTNode); + + const dfsInorderNodes = objBST.dfs(node => node, 'in'); + expect(dfsInorderNodes[0].key).toBe(1); + expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16); + + objBST.perfectlyBalance(); + expect(objBST.isPerfectlyBalanced()).toBe(true); + + const bfsNodesAfterBalanced: BSTNode<{key: number; keyA: number}>[] = []; + objBST.bfs(node => bfsNodesAfterBalanced.push(node)); + expect(bfsNodesAfterBalanced[0].key).toBe(8); + expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16); + + const removed11 = objBST.delete(11); + expect(removed11).toBeInstanceOf(Array); + expect(removed11[0]).toBeDefined(); + expect(removed11[0].deleted).toBeDefined(); + + if (removed11[0].deleted) expect(removed11[0].deleted.key).toBe(11); + + expect(objBST.isAVLBalanced()).toBe(true); + + expect(node15 && objBST.getHeight(node15)).toBe(2); + + const removed1 = objBST.delete(1); + expect(removed1).toBeInstanceOf(Array); + expect(removed1[0]).toBeDefined(); + expect(removed1[0].deleted).toBeDefined(); + if (removed1[0].deleted) expect(removed1[0].deleted.key).toBe(1); + + expect(objBST.isAVLBalanced()).toBe(true); + + expect(objBST.getHeight()).toBe(4); + + const removed4 = objBST.delete(4); + expect(removed4).toBeInstanceOf(Array); + expect(removed4[0]).toBeDefined(); + expect(removed4[0].deleted).toBeDefined(); + if (removed4[0].deleted) expect(removed4[0].deleted.key).toBe(4); + expect(objBST.isAVLBalanced()).toBe(true); + expect(objBST.getHeight()).toBe(4); + + const removed10 = objBST.delete(10); + expect(removed10).toBeInstanceOf(Array); + expect(removed10[0]).toBeDefined(); + expect(removed10[0].deleted).toBeDefined(); + if (removed10[0].deleted) expect(removed10[0].deleted.key).toBe(10); + expect(objBST.isAVLBalanced()).toBe(false); + expect(objBST.getHeight()).toBe(4); + + const removed15 = objBST.delete(15); + expect(removed15).toBeInstanceOf(Array); + expect(removed15[0]).toBeDefined(); + expect(removed15[0].deleted).toBeDefined(); + if (removed15[0].deleted) expect(removed15[0].deleted.key).toBe(15); + + expect(objBST.isAVLBalanced()).toBe(true); + expect(objBST.getHeight()).toBe(3); + + const removed5 = objBST.delete(5); + expect(removed5).toBeInstanceOf(Array); + expect(removed5[0]).toBeDefined(); + expect(removed5[0].deleted).toBeDefined(); + if (removed5[0].deleted) expect(removed5[0].deleted.key).toBe(5); + + expect(objBST.isAVLBalanced()).toBe(true); + expect(objBST.getHeight()).toBe(3); + + const removed13 = objBST.delete(13); + expect(removed13).toBeInstanceOf(Array); + expect(removed13[0]).toBeDefined(); + expect(removed13[0].deleted).toBeDefined(); + if (removed13[0].deleted) expect(removed13[0].deleted.key).toBe(13); + expect(objBST.isAVLBalanced()).toBe(true); + expect(objBST.getHeight()).toBe(3); + + const removed3 = objBST.delete(3); + expect(removed3).toBeInstanceOf(Array); + expect(removed3[0]).toBeDefined(); + expect(removed3[0].deleted).toBeDefined(); + if (removed3[0].deleted) expect(removed3[0].deleted.key).toBe(3); + expect(objBST.isAVLBalanced()).toBe(false); + expect(objBST.getHeight()).toBe(3); + + const removed8 = objBST.delete(8); + expect(removed8).toBeInstanceOf(Array); + expect(removed8[0]).toBeDefined(); + expect(removed8[0].deleted).toBeDefined(); + if (removed8[0].deleted) expect(removed8[0].deleted.key).toBe(8); + expect(objBST.isAVLBalanced()).toBe(true); + expect(objBST.getHeight()).toBe(3); + + const removed6 = objBST.delete(6); + expect(removed6).toBeInstanceOf(Array); + expect(removed6[0]).toBeDefined(); + expect(removed6[0].deleted).toBeDefined(); + if (removed6[0].deleted) expect(removed6[0].deleted.key).toBe(6); + expect(objBST.delete(6).length).toBe(0); + expect(objBST.isAVLBalanced()).toBe(false); + expect(objBST.getHeight()).toBe(3); + + const removed7 = objBST.delete(7); + expect(removed7).toBeInstanceOf(Array); + expect(removed7[0]).toBeDefined(); + expect(removed7[0].deleted).toBeDefined(); + if (removed7[0].deleted) expect(removed7[0].deleted.key).toBe(7); + expect(objBST.isAVLBalanced()).toBe(false); + expect(objBST.getHeight()).toBe(3); + + const removed9 = objBST.delete(9); + expect(removed9).toBeInstanceOf(Array); + expect(removed9[0]).toBeDefined(); + expect(removed9[0].deleted).toBeDefined(); + if (removed9[0].deleted) expect(removed9[0].deleted.key).toBe(9); + expect(objBST.isAVLBalanced()).toBe(false); + expect(objBST.getHeight()).toBe(3); + + const removed14 = objBST.delete(14); + expect(removed14).toBeInstanceOf(Array); + expect(removed14[0]).toBeDefined(); + expect(removed14[0].deleted).toBeDefined(); + if (removed14[0].deleted) expect(removed14[0].deleted.key).toBe(14); + expect(objBST.isAVLBalanced()).toBe(false); + expect(objBST.getHeight()).toBe(2); + + expect(objBST.isAVLBalanced()).toBe(false); + + const bfsIDs: number[] = []; + objBST.bfs(node => bfsIDs.push(node.key)); + expect(bfsIDs[0]).toBe(2); + expect(bfsIDs[1]).toBe(12); + expect(bfsIDs[2]).toBe(16); + + const bfsNodes: BSTNode<{key: number; keyA: number}>[] = []; objBST.bfs(node => bfsNodes.push(node)); expect(bfsNodes[0].key).toBe(2); expect(bfsNodes[1].key).toBe(12); diff --git a/test/unit/data-structures/binary-tree/overall.test.ts b/test/unit/data-structures/binary-tree/overall.test.ts index 8c1921c..520c2bc 100644 --- a/test/unit/data-structures/binary-tree/overall.test.ts +++ b/test/unit/data-structures/binary-tree/overall.test.ts @@ -29,7 +29,7 @@ describe('Overall BinaryTree Test', () => { bfsIDs[0] === 11; // true expect(bfsIDs[0]).toBe(11); - const objBST = new BST<{ key: number; keyA: number }>(); + const objBST = new BST<{key: number; keyA: number}>(); objBST.add(11, {key: 11, keyA: 11}); objBST.add(3, {key: 3, keyA: 3}); diff --git a/test/unit/data-structures/binary-tree/rb-tree.test.ts b/test/unit/data-structures/binary-tree/rb-tree.test.ts index 884e126..afd016b 100644 --- a/test/unit/data-structures/binary-tree/rb-tree.test.ts +++ b/test/unit/data-structures/binary-tree/rb-tree.test.ts @@ -1,14 +1,80 @@ -// import {RBTree, RBTreeNode} from '../../../../src'; +import {RBTree, RBTreeNode} from '../../../../src'; + +describe('RBTreeNode', () => { + it('should create an instance of RBTreeNode', () => { + const node = new RBTreeNode(1); + expect(node).toBeInstanceOf(RBTreeNode); + }); + + it('should set and get the ID correctly', () => { + const node = new RBTreeNode(1); + expect(node.key).toBe(1); + + node.key = 2; + expect(node.key).toBe(2); + }); + + it('should set and get the value correctly', () => { + const node: RBTreeNode = new RBTreeNode(1, 42); + expect(node.val).toBe(42); + + node.val = 55; + expect(node.val).toBe(55); + }); + + it('should set and get the left child correctly', () => { + const node1 = new RBTreeNode(1); + const node2 = new RBTreeNode(2); + + node1.left = node2; + + expect(node1.left).toBe(node2); + expect(node2.parent).toBe(node1); + }); + + it('should set and get the right child correctly', () => { + const node1 = new RBTreeNode(1); + const node2 = new RBTreeNode(2); + + node1.right = node2; + + expect(node1.right).toBe(node2); + expect(node2.parent).toBe(node1); + }); + + it('should set and get the parent correctly', () => { + const node1 = new RBTreeNode(1); + const node2 = new RBTreeNode(2); + + node1.left = node2; + + expect(node2.parent).toBe(node1); + expect(node1.left).toBe(node2); + }); + + it('should determine family position correctly', () => { + const root = new RBTreeNode(1); + const leftChild = new RBTreeNode(2); + const rightChild = new RBTreeNode(3); + + root.left = leftChild; + root.right = rightChild; + + expect(leftChild.familyPosition).toBe('LEFT'); + expect(rightChild.familyPosition).toBe('RIGHT'); + expect(root.familyPosition).toBe('ROOT'); + }); +}); describe('Red-Black Tree Tests', () => { - // let tree: RBTree>; - // - // beforeEach(() => { - // tree = new RBTree>(); - // }); + let tree: RBTree>; + + beforeEach(() => { + tree = new RBTree>(); + }); test('Insertion and In-order Traverse', () => { - // tree.add(5); + tree.add(5); // tree.add(3); // tree.add(7); // tree.add(2); diff --git a/test/unit/data-structures/binary-tree/tree-multiset.test.ts b/test/unit/data-structures/binary-tree/tree-multiset.test.ts index 361b90d..ab90ae2 100644 --- a/test/unit/data-structures/binary-tree/tree-multiset.test.ts +++ b/test/unit/data-structures/binary-tree/tree-multiset.test.ts @@ -1,7 +1,8 @@ -import {CP, TreeMultiset, TreeMultisetNode} from '../../../../src'; +import {CP, IterationType, TreeMultiset, TreeMultisetNode} from '../../../../src'; import {isDebugTest} from '../../../config'; const isDebug = isDebugTest; + describe('TreeMultiset operations test', () => { it('should perform various operations on a Binary Search Tree with numeric values', () => { const treeMultiset = new TreeMultiset(); @@ -206,7 +207,7 @@ describe('TreeMultiset operations test', () => { }); it('should perform various operations on a Binary Search Tree with object values', () => { - const objTreeMultiset = new TreeMultiset<{ key: number; keyA: number }>(); + const objTreeMultiset = new TreeMultiset<{key: number; keyA: number}>(); expect(objTreeMultiset).toBeInstanceOf(TreeMultiset); objTreeMultiset.add(11, {key: 11, keyA: 11}); objTreeMultiset.add(3, {key: 3, keyA: 3}); @@ -239,196 +240,250 @@ describe('TreeMultiset operations test', () => { expect(objTreeMultiset.count).toBe(16); expect(objTreeMultiset.has(6)).toBe(true); + }); +}); - // const node6 = objTreeMultiset.get(6); - // expect(node6 && objTreeMultiset.getHeight(node6)).toBe(2); - // expect(node6 && objTreeMultiset.getDepth(node6)).toBe(3); - // - // const nodeId10 = objTreeMultiset.get(10, 'key'); - // expect(nodeId10?.key).toBe(10); - // - // const nodeVal9 = objTreeMultiset.get(9, 'key'); - // expect(nodeVal9?.key).toBe(9); - // - // const nodesByCount1 = objTreeMultiset.getNodesByCount(1); - // expect(nodesByCount1.length).toBe(16); - // - // const leftMost = objTreeMultiset.getLeftMost(); - // expect(leftMost?.key).toBe(1); - // - // const node15 = objTreeMultiset.get(15); - // expect(node15?.val).toEqual({key: 15, keyA: 15}); - // const minNodeBySpecificNode = node15 && objTreeMultiset.getLeftMost(node15); - // expect(minNodeBySpecificNode?.key).toBe(12); - // - // const subTreeSum = node15 && objTreeMultiset.subTreeSum(node15); - // expect(subTreeSum).toBe(70); - // - // const lesserSum = objTreeMultiset.lesserSum(10); - // expect(lesserSum).toBe(45); - // - // expect(node15).toBeInstanceOf(TreeMultisetNode); - // if (node15 instanceof TreeMultisetNode) { - // const subTreeAdd = objTreeMultiset.subTreeAddCount(node15, 1); - // expect(subTreeAdd).toBeDefined(); - // } - // - // const node11 = objTreeMultiset.get(11); - // expect(node11).toBeInstanceOf(TreeMultisetNode); - // if (node11 instanceof TreeMultisetNode) { - // const allGreaterNodesAdded = objTreeMultiset.allGreaterNodesAddCount(node11, 2); - // expect(allGreaterNodesAdded).toBeDefined(); - // } - // - // const dfsInorderNodes = objTreeMultiset.dfs(node => node, 'in'); - // expect(dfsInorderNodes[0].key).toBe(1); - // expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16); - // - // objTreeMultiset.perfectlyBalance(); - // expect(objTreeMultiset.isPerfectlyBalanced()).toBe(true); - // - // const bfsNodesAfterBalanced = objTreeMultiset.bfs('node'); - // expect(bfsNodesAfterBalanced[0].key).toBe(8); - // expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16); - // - // const removed11 = objTreeMultiset.delete(11, true); - // expect(removed11).toBeInstanceOf(Array); - // expect(removed11[0]).toBeDefined(); - // expect(removed11[0].deleted).toBeDefined(); - // - // if (removed11[0].deleted) expect(removed11[0].deleted.key).toBe(11); - // - // expect(objTreeMultiset.isAVLBalanced()).toBe(true); - // - // expect(node15 && objTreeMultiset.getHeight(node15)).toBe(2); - // - // const removed1 = objTreeMultiset.delete(1, true); - // expect(removed1).toBeInstanceOf(Array); - // expect(removed1[0]).toBeDefined(); - // expect(removed1[0].deleted).toBeDefined(); - // if (removed1[0].deleted) expect(removed1[0].deleted.key).toBe(1); - // - // expect(objTreeMultiset.isAVLBalanced()).toBe(true); - // - // expect(objTreeMultiset.getHeight()).toBe(4); - // - // const removed4 = objTreeMultiset.delete(4, true); - // expect(removed4).toBeInstanceOf(Array); - // expect(removed4[0]).toBeDefined(); - // expect(removed4[0].deleted).toBeDefined(); - // if (removed4[0].deleted) expect(removed4[0].deleted.key).toBe(4); - // expect(objTreeMultiset.isAVLBalanced()).toBe(true); - // expect(objTreeMultiset.getHeight()).toBe(4); - // - // const removed10 = objTreeMultiset.delete(10, true); - // expect(removed10).toBeInstanceOf(Array); - // expect(removed10[0]).toBeDefined(); - // expect(removed10[0].deleted).toBeDefined(); - // if (removed10[0].deleted) expect(removed10[0].deleted.key).toBe(10); - // expect(objTreeMultiset.isAVLBalanced()).toBe(false); - // expect(objTreeMultiset.getHeight()).toBe(4); - // - // const removed15 = objTreeMultiset.delete(15, true); - // expect(removed15).toBeInstanceOf(Array); - // expect(removed15[0]).toBeDefined(); - // expect(removed15[0].deleted).toBeDefined(); - // if (removed15[0].deleted) expect(removed15[0].deleted.key).toBe(15); - // - // expect(objTreeMultiset.isAVLBalanced()).toBe(true); - // expect(objTreeMultiset.getHeight()).toBe(3); - // - // const removed5 = objTreeMultiset.delete(5, true); - // expect(removed5).toBeInstanceOf(Array); - // expect(removed5[0]).toBeDefined(); - // expect(removed5[0].deleted).toBeDefined(); - // if (removed5[0].deleted) expect(removed5[0].deleted.key).toBe(5); - // - // expect(objTreeMultiset.isAVLBalanced()).toBe(true); - // expect(objTreeMultiset.getHeight()).toBe(3); - // - // const removed13 = objTreeMultiset.delete(13, true); - // expect(removed13).toBeInstanceOf(Array); - // expect(removed13[0]).toBeDefined(); - // expect(removed13[0].deleted).toBeDefined(); - // if (removed13[0].deleted) expect(removed13[0].deleted.key).toBe(13); - // expect(objTreeMultiset.isAVLBalanced()).toBe(true); - // expect(objTreeMultiset.getHeight()).toBe(3); - // - // const removed3 = objTreeMultiset.delete(3, true); - // expect(removed3).toBeInstanceOf(Array); - // expect(removed3[0]).toBeDefined(); - // expect(removed3[0].deleted).toBeDefined(); - // if (removed3[0].deleted) expect(removed3[0].deleted.key).toBe(3); - // expect(objTreeMultiset.isAVLBalanced()).toBe(false); - // expect(objTreeMultiset.getHeight()).toBe(3); - // - // const removed8 = objTreeMultiset.delete(8, true); - // expect(removed8).toBeInstanceOf(Array); - // expect(removed8[0]).toBeDefined(); - // expect(removed8[0].deleted).toBeDefined(); - // if (removed8[0].deleted) expect(removed8[0].deleted.key).toBe(8); - // expect(objTreeMultiset.isAVLBalanced()).toBe(true); - // expect(objTreeMultiset.getHeight()).toBe(3); - // - // const removed6 = objTreeMultiset.delete(6, true); - // expect(removed6).toBeInstanceOf(Array); - // expect(removed6[0]).toBeDefined(); - // expect(removed6[0].deleted).toBeDefined(); - // if (removed6[0].deleted) expect(removed6[0].deleted.key).toBe(6); - // expect(objTreeMultiset.delete(6, true).length).toBe(0); - // expect(objTreeMultiset.isAVLBalanced()).toBe(false); - // expect(objTreeMultiset.getHeight()).toBe(3); - // - // const removed7 = objTreeMultiset.delete(7, true); - // expect(removed7).toBeInstanceOf(Array); - // expect(removed7[0]).toBeDefined(); - // expect(removed7[0].deleted).toBeDefined(); - // if (removed7[0].deleted) expect(removed7[0].deleted.key).toBe(7); - // expect(objTreeMultiset.isAVLBalanced()).toBe(false); - // expect(objTreeMultiset.getHeight()).toBe(3); - // - // const removed9 = objTreeMultiset.delete(9, true); - // expect(removed9).toBeInstanceOf(Array); - // expect(removed9[0]).toBeDefined(); - // expect(removed9[0].deleted).toBeDefined(); - // if (removed9[0].deleted) expect(removed9[0].deleted.key).toBe(9); - // expect(objTreeMultiset.isAVLBalanced()).toBe(false); - // expect(objTreeMultiset.getHeight()).toBe(3); - // - // const removed14 = objTreeMultiset.delete(14, true); - // expect(removed14).toBeInstanceOf(Array); - // expect(removed14[0]).toBeDefined(); - // expect(removed14[0].deleted).toBeDefined(); - // if (removed14[0].deleted) expect(removed14[0].deleted.key).toBe(14); - // expect(objTreeMultiset.isAVLBalanced()).toBe(false); - // expect(objTreeMultiset.getHeight()).toBe(2); - // - // - // expect(objTreeMultiset.isAVLBalanced()).toBe(false); - // - // const bfsIDs = objTreeMultiset.bfs(); - // expect(bfsIDs[0]).toBe(2); - // expect(bfsIDs[1]).toBe(12); - // expect(bfsIDs[2]).toBe(16); - // - // const bfsNodes = objTreeMultiset.bfs('node'); - // expect(bfsNodes[0].key).toBe(2); - // expect(bfsNodes[1].key).toBe(12); - // expect(bfsNodes[2].key).toBe(16); - // - // expect(objTreeMultiset.count).toBe(5); +describe('TreeMultiset operations test recursively', () => { + it('should perform various operations on a Binary Search Tree with numeric values', () => { + const treeMultiset = new TreeMultiset({iterationType: IterationType.RECURSIVE}); + + expect(treeMultiset instanceof TreeMultiset); + treeMultiset.add(11, 11); + treeMultiset.add(3, 3); + const idAndValues = [11, 3, 15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5]; + treeMultiset.addMany(idAndValues, idAndValues); + expect(treeMultiset.root instanceof TreeMultisetNode); + + if (treeMultiset.root) expect(treeMultiset.root.key == 11); + + expect(treeMultiset.size).toBe(16); + expect(treeMultiset.count).toBe(18); + + expect(treeMultiset.has(6)); + + expect(treeMultiset.getHeight(6)).toBe(3); + expect(treeMultiset.getDepth(6)).toBe(1); + const nodeId10 = treeMultiset.get(10); + expect(nodeId10?.key).toBe(10); + + const nodeVal9 = treeMultiset.get(9, node => node.val); + expect(nodeVal9?.key).toBe(9); + + const nodesByCount1 = treeMultiset.getNodes(1, node => node.count); + expect(nodesByCount1.length).toBe(14); + + const nodesByCount2 = treeMultiset.getNodes(2, node => node.count); + expect(nodesByCount2.length).toBe(2); + const leftMost = treeMultiset.getLeftMost(); + expect(leftMost?.key).toBe(1); + + const node15 = treeMultiset.get(15); + const minNodeBySpecificNode = node15 && treeMultiset.getLeftMost(node15); + expect(minNodeBySpecificNode?.key).toBe(12); + + let subTreeSum = 0; + node15 && treeMultiset.subTreeTraverse((node: TreeMultisetNode) => (subTreeSum += node.key), 15); + expect(subTreeSum).toBe(70); + let lesserSum = 0; + treeMultiset.lesserOrGreaterTraverse((node: TreeMultisetNode) => (lesserSum += node.key), CP.lt, 10); + expect(lesserSum).toBe(45); + + expect(node15 instanceof TreeMultisetNode); + if (node15 instanceof TreeMultisetNode) { + const subTreeAdd = treeMultiset.subTreeTraverse((node: TreeMultisetNode) => (node.count += 1), 15); + expect(subTreeAdd); + } + const node11 = treeMultiset.get(11); + expect(node11 instanceof TreeMultisetNode); + if (node11 instanceof TreeMultisetNode) { + const allGreaterNodesAdded = treeMultiset.lesserOrGreaterTraverse(node => (node.count += 2), CP.gt, 11); + expect(allGreaterNodesAdded); + } + + const dfsInorderNodes = treeMultiset.dfs(node => node, 'in'); + expect(dfsInorderNodes[0].key).toBe(1); + expect(dfsInorderNodes[dfsInorderNodes.length - 1].key).toBe(16); + expect(treeMultiset.isPerfectlyBalanced()).toBe(false); + + treeMultiset.perfectlyBalance(); + + expect(treeMultiset.isPerfectlyBalanced()).toBe(true); + expect(treeMultiset.isAVLBalanced()).toBe(true); + + const bfsNodesAfterBalanced = treeMultiset.bfs(node => node); + expect(bfsNodesAfterBalanced[0].key).toBe(8); + expect(bfsNodesAfterBalanced[bfsNodesAfterBalanced.length - 1].key).toBe(16); + + const removed11 = treeMultiset.delete(11, undefined, true); + expect(removed11 instanceof Array); + expect(removed11[0]); + expect(removed11[0].deleted); + + if (removed11[0].deleted) expect(removed11[0].deleted.key).toBe(11); + + expect(treeMultiset.isAVLBalanced()).toBe(true); + + expect(treeMultiset.getHeight(15)).toBe(1); + + const removed1 = treeMultiset.delete(1, undefined, true); + expect(removed1 instanceof Array); + expect(removed1[0]); + expect(removed1[0].deleted); + if (removed1[0].deleted) expect(removed1[0].deleted.key).toBe(1); + + expect(treeMultiset.isAVLBalanced()).toBe(true); + + expect(treeMultiset.getHeight()).toBe(4); + + const removed4 = treeMultiset.delete(4, undefined, true); + expect(removed4 instanceof Array); + expect(removed4[0]); + expect(removed4[0].deleted); + if (removed4[0].deleted) expect(removed4[0].deleted.key).toBe(4); + + expect(treeMultiset.isAVLBalanced()).toBe(true); + expect(treeMultiset.getHeight()).toBe(4); + + const removed10 = treeMultiset.delete(10, undefined, true); + expect(removed10 instanceof Array); + expect(removed10[0]); + expect(removed10[0].deleted); + if (removed10[0].deleted) expect(removed10[0].deleted.key).toBe(10); + expect(treeMultiset.isAVLBalanced()).toBe(true); + + expect(treeMultiset.getHeight()).toBe(3); + + const removed15 = treeMultiset.delete(15, undefined, true); + expect(removed15 instanceof Array); + expect(removed15[0]); + expect(removed15[0].deleted); + if (removed15[0].deleted) expect(removed15[0].deleted.key).toBe(15); + + expect(treeMultiset.isAVLBalanced()).toBe(true); + expect(treeMultiset.getHeight()).toBe(3); + + const removed5 = treeMultiset.delete(5, undefined, true); + expect(removed5 instanceof Array); + expect(removed5[0]); + expect(removed5[0].deleted); + if (removed5[0].deleted) expect(removed5[0].deleted.key).toBe(5); + + expect(treeMultiset.isAVLBalanced()).toBe(true); + expect(treeMultiset.getHeight()).toBe(3); + + const removed13 = treeMultiset.delete(13, undefined, true); + expect(removed13 instanceof Array); + expect(removed13[0]); + expect(removed13[0].deleted); + if (removed13[0].deleted) expect(removed13[0].deleted.key).toBe(13); + expect(treeMultiset.isAVLBalanced()).toBe(true); + expect(treeMultiset.getHeight()).toBe(3); + + const removed3 = treeMultiset.delete(3, undefined, true); + expect(removed3 instanceof Array); + expect(removed3[0]); + expect(removed3[0].deleted); + if (removed3[0].deleted) expect(removed3[0].deleted.key).toBe(3); + expect(treeMultiset.isAVLBalanced()).toBe(true); + expect(treeMultiset.getHeight()).toBe(3); + + const removed8 = treeMultiset.delete(8, undefined, true); + expect(removed8 instanceof Array); + expect(removed8[0]); + expect(removed8[0].deleted); + if (removed8[0].deleted) expect(removed8[0].deleted.key).toBe(8); + expect(treeMultiset.isAVLBalanced()).toBe(true); + expect(treeMultiset.getHeight()).toBe(3); + + const removed6 = treeMultiset.delete(6, undefined, true); + expect(removed6 instanceof Array); + expect(removed6[0]); + expect(removed6[0].deleted); + if (removed6[0].deleted) expect(removed6[0].deleted.key).toBe(6); + expect(treeMultiset.delete(6, undefined, true).length).toBe(0); + expect(treeMultiset.isAVLBalanced()).toBe(true); + + expect(treeMultiset.getHeight()).toBe(2); + + const removed7 = treeMultiset.delete(7, undefined, true); + expect(removed7 instanceof Array); + expect(removed7[0]); + expect(removed7[0].deleted); + if (removed7[0].deleted) expect(removed7[0].deleted.key).toBe(7); + expect(treeMultiset.isAVLBalanced()).toBe(true); + expect(treeMultiset.getHeight()).toBe(2); + + const removed9 = treeMultiset.delete(9, undefined, true); + expect(removed9 instanceof Array); + expect(removed9[0]); + expect(removed9[0].deleted); + if (removed9[0].deleted) expect(removed9[0].deleted.key).toBe(9); + expect(treeMultiset.isAVLBalanced()).toBe(true); + expect(treeMultiset.getHeight()).toBe(2); + + const removed14 = treeMultiset.delete(14, undefined, true); + expect(removed14 instanceof Array); + expect(removed14[0]); + expect(removed14[0].deleted); + if (removed14[0].deleted) expect(removed14[0].deleted.key).toBe(14); + expect(treeMultiset.isAVLBalanced()).toBe(true); + expect(treeMultiset.getHeight()).toBe(1); + + expect(treeMultiset.isAVLBalanced()).toBe(true); + + const bfsIDs = treeMultiset.bfs(node => node.key); + + expect(bfsIDs[0]).toBe(12); + expect(bfsIDs[1]).toBe(2); + expect(bfsIDs[2]).toBe(16); + + const bfsNodes = treeMultiset.bfs(node => node); + + expect(bfsNodes[0].key).toBe(12); + expect(bfsNodes[1].key).toBe(2); + expect(bfsNodes[2].key).toBe(16); + + expect(treeMultiset.count).toBe(9); + }); + + it('should perform various operations on a Binary Search Tree with object values', () => { + const objTreeMultiset = new TreeMultiset<{key: number; keyA: number}>(); + expect(objTreeMultiset).toBeInstanceOf(TreeMultiset); + objTreeMultiset.add(11, {key: 11, keyA: 11}); + objTreeMultiset.add(3, {key: 3, keyA: 3}); + const values = [ + {key: 15, keyA: 15}, + {key: 1, keyA: 1}, + {key: 8, keyA: 8}, + {key: 13, keyA: 13}, + {key: 16, keyA: 16}, + {key: 2, keyA: 2}, + {key: 6, keyA: 6}, + {key: 9, keyA: 9}, + {key: 12, keyA: 12}, + {key: 14, keyA: 14}, + {key: 4, keyA: 4}, + {key: 7, keyA: 7}, + {key: 10, keyA: 10}, + {key: 5, keyA: 5} + ]; + + objTreeMultiset.addMany( + values.map(item => item.key), + values + ); + + expect(objTreeMultiset.root).toBeInstanceOf(TreeMultisetNode); + + if (objTreeMultiset.root) expect(objTreeMultiset.root.key).toBe(11); + + expect(objTreeMultiset.count).toBe(16); + + expect(objTreeMultiset.has(6)).toBe(true); }); }); describe('TreeMultiset Performance test', function () { - // const treeMS = new TreeMultiset>(); - // const inputSize = [100]; // Adjust input sizes as needed - // - // // Define a function to calculate the expected O(n log n) time - // function expectedTime(n: number): number { - // return n * Math.log(n); - // } - const treeMS = new TreeMultiset>(); const inputSize = 100000; // Adjust input sizes as needed diff --git a/test/unit/data-structures/graph/directed-graph.test.ts b/test/unit/data-structures/graph/directed-graph.test.ts index fcd8443..91cb054 100644 --- a/test/unit/data-structures/graph/directed-graph.test.ts +++ b/test/unit/data-structures/graph/directed-graph.test.ts @@ -22,22 +22,27 @@ describe('DirectedGraph Operation Test', () => { const vertex1 = new DirectedVertex('A'); const vertex2 = new DirectedVertex('B'); const edge = new DirectedEdge('A', 'B'); + edge.src = edge.src; + edge.dest = edge.dest; graph.addVertex(vertex1); graph.addVertex(vertex2); graph.addEdge(edge); + expect(graph.outEdgeMap.size).toBe(1); + expect(graph.inEdgeMap.size).toBe(1); expect(graph.hasEdge('A', 'B')).toBe(true); expect(graph.hasEdge('B', 'A')).toBe(false); }); it('should delete edges', () => { const vertex1 = new DirectedVertex('A'); - const vertex2 = new DirectedVertex('B'); + // const vertex2 = new DirectedVertex('B'); + graph.createVertex('B'); const edge = new DirectedEdge('A', 'B'); graph.addVertex(vertex1); - graph.addVertex(vertex2); + graph.addVertex('B'); graph.addEdge(edge); expect(graph.deleteEdge(edge)).toBe(edge); @@ -49,16 +54,41 @@ describe('DirectedGraph Operation Test', () => { const vertexB = new DirectedVertex('B'); const vertexC = new DirectedVertex('C'); const edgeAB = new DirectedEdge('A', 'B'); - const edgeBC = new DirectedEdge('B', 'C'); + graph.createEdge('B', 'C'); graph.addVertex(vertexA); graph.addVertex(vertexB); graph.addVertex(vertexC); graph.addEdge(edgeAB); - graph.addEdge(edgeBC); + graph.addEdge('B', 'C'); + + expect(graph.getEdgeSrc(edgeAB)).toBe(vertexA); const topologicalOrder = graph.topologicalSort(); if (topologicalOrder) expect(topologicalOrder).toEqual(['A', 'B', 'C']); + + graph.deleteEdgesBetween('A', 'B'); + + const topologicalOrder1 = graph.topologicalSort(); + if (topologicalOrder1) expect(topologicalOrder1).toEqual(['B', 'C', 'A']); + + expect(graph.incomingEdgesOf(vertexC)?.length).toBe(1); + expect(graph.degreeOf(vertexA)).toBe(0); + expect(graph.inDegreeOf(vertexC)).toBe(1); + expect(graph.outDegreeOf(vertexC)).toBe(0); + expect(graph.edgesOf(vertexC)?.length).toBe(1); + + expect(graph.tarjan(true, true, true, true)?.dfnMap.size).toBe(3); + expect(graph.bellmanFord(vertexC, true, true, true)?.paths.length).toBe(3); + expect(graph.getMinPathBetween('B', 'C', true)?.length).toBe(2); + expect(graph.setEdgeWeight('B', 'C', 100)).toBe(true); + expect(graph.getMinCostBetween('B', 'C', true)).toBe(100); + expect(graph.getMinCostBetween('B', 'C')).toBe(1); + expect(graph.getAllPathsBetween('B', 'C')?.length).toBe(1); + expect(graph.deleteVertex(vertexB)).toBe(true); + expect(graph.getAllPathsBetween('B', 'C')?.length).toBe(0); + + expect(graph.removeManyVertices([vertexB, vertexC])).toBe(true); }); }); diff --git a/test/unit/data-structures/graph/map-graph.test.ts b/test/unit/data-structures/graph/map-graph.test.ts index 078a1fc..0c88ddd 100644 --- a/test/unit/data-structures/graph/map-graph.test.ts +++ b/test/unit/data-structures/graph/map-graph.test.ts @@ -1,4 +1,4 @@ -import {MapGraph, MapVertex} from '../../../../src'; +import {MapEdge, MapGraph, MapVertex} from '../../../../src'; describe('MapGraph Operation Test', () => { it('dijkstra shortest path', () => { @@ -43,3 +43,84 @@ describe('MapGraph Operation Test', () => { expect(surinToSaanenGoatFarmViaDij?.minDist).toBe(25.2); }); }); + +describe('MapGraph', () => { + let mapGraph: MapGraph; + + beforeEach(() => { + // Create a new MapGraph instance before each test + mapGraph = new MapGraph([0, 0], [100, 100]); + }); + + // Test adding vertices to the graph + it('should add vertices to the graph', () => { + const locationA = new MapVertex('A', 10, 20, 'Location A'); + const locationB = new MapVertex('B', 30, 40, 'Location B'); + + mapGraph.addVertex(locationA); + mapGraph.addVertex(locationB); + + expect(mapGraph.hasVertex('A')).toBe(true); + expect(mapGraph.hasVertex('B')).toBe(true); + }); + + // Test adding edges to the graph + it('should add edges to the graph', () => { + const locationA = new MapVertex('A', 10, 20, 'Location A'); + const locationB = new MapVertex('B', 30, 40, 'Location B'); + const edgeAB = new MapEdge('A', 'B', 50, 'Edge from A to B'); + + mapGraph.addVertex(locationA); + mapGraph.addVertex(locationB); + mapGraph.addEdge(edgeAB); + + expect(mapGraph.hasEdge('A', 'B')).toBe(true); + }); + + // Test getting neighbors of a vertex + it('should return the neighbors of a vertex', () => { + const locationA = new MapVertex('A', 10, 20, 'Location A'); + locationA.lat = locationA.lat; + locationA.long = locationA.long; + const locationB = mapGraph.createVertex('B', 30, 40, 'Location B'); + + const locationC = new MapVertex('C', 50, 60, 'Location C'); + const edgeAB = new MapEdge('A', 'B', 50, 'Edge from A to B'); + const edgeBC = new MapEdge('B', 'C', 60, 'Edge from B to C'); + + mapGraph.origin = mapGraph.origin; + mapGraph.bottomRight = mapGraph.bottomRight; + + mapGraph.addVertex(locationA); + mapGraph.addVertex(locationB); + mapGraph.addVertex(locationC); + mapGraph.addEdge(edgeAB); + mapGraph.addEdge(edgeBC); + + const neighborsOfA = mapGraph.getNeighbors('A'); + const neighborsOfB = mapGraph.getNeighbors('B'); + + expect(neighborsOfA).toEqual([locationB]); + expect(neighborsOfB).toEqual([locationC]); + }); + + // Test finding the shortest path between locations + it('should find the shortest path between two locations', () => { + const locationA = new MapVertex('A', 10, 20, 'Location A'); + const locationB = new MapVertex('B', 30, 40, 'Location B'); + const locationC = new MapVertex('C', 50, 60, 'Location C'); + const edgeAB = new MapEdge('A', 'B', 50, 'Edge from A to B'); + const edgeBC = new MapEdge('B', 'C', 60, 'Edge from B to C'); + + mapGraph.addVertex(locationA); + mapGraph.addVertex(locationB); + mapGraph.addVertex(locationC); + mapGraph.addEdge(edgeAB); + mapGraph.addEdge(edgeBC); + + const shortestPath = mapGraph.dijkstra('A', 'C'); + + expect(shortestPath?.minPath.length).toEqual(0); + expect(shortestPath?.distMap.size).toBe(3); + }); +}); diff --git a/test/unit/data-structures/graph/undirected-graph.test.ts b/test/unit/data-structures/graph/undirected-graph.test.ts index 9d81ee1..c331fd6 100644 --- a/test/unit/data-structures/graph/undirected-graph.test.ts +++ b/test/unit/data-structures/graph/undirected-graph.test.ts @@ -57,3 +57,85 @@ describe('UndirectedGraph Operation Test', () => { expect(Array.from(dijkstraResult?.seen ?? []).map(vertex => vertex.key)).toEqual(['A', 'B', 'D']); }); }); + +describe('UndirectedGraph', () => { + let undirectedGraph: UndirectedGraph, UndirectedEdge>; + + beforeEach(() => { + // Create a new UndirectedGraph instance before each test + undirectedGraph = new UndirectedGraph, UndirectedEdge>(); + }); + + // Test adding vertices to the graph + it('should add vertices to the graph', () => { + const vertexA = new UndirectedVertex('A', 'Location A'); + const vertexB = new UndirectedVertex('B', 'Location B'); + + undirectedGraph.addVertex(vertexA); + undirectedGraph.addVertex(vertexB); + + expect(undirectedGraph.hasVertex('A')).toBe(true); + expect(undirectedGraph.hasVertex('B')).toBe(true); + }); + + // Test adding edges to the graph + it('should add edges to the graph', () => { + const vertexA = new UndirectedVertex('A', 'Location A'); + const vertexB = new UndirectedVertex('B', 'Location B'); + const edgeAB = new UndirectedEdge('A', 'B', 1, 'Edge between A and B'); + + undirectedGraph.addVertex(vertexA); + undirectedGraph.addVertex(vertexB); + undirectedGraph.addEdge(edgeAB); + + expect(undirectedGraph.hasEdge('A', 'B')).toBe(true); + }); + + // Test getting neighbors of a vertex + it('should return the neighbors of a vertex', () => { + const vertexA = new UndirectedVertex('A', 'Location A'); + const vertexB = new UndirectedVertex('B', 'Location B'); + const vertexC = new UndirectedVertex('C', 'Location C'); + const edgeAB = new UndirectedEdge('A', 'B', 1, 'Edge between A and B'); + const edgeBC = new UndirectedEdge('B', 'C', 2, 'Edge between B and C'); + + undirectedGraph.addVertex(vertexA); + undirectedGraph.addVertex(vertexB); + undirectedGraph.addVertex(vertexC); + undirectedGraph.addEdge(edgeAB); + undirectedGraph.addEdge(edgeBC); + + const neighborsOfA = undirectedGraph.getNeighbors('A'); + const neighborsOfB = undirectedGraph.getNeighbors('B'); + + expect(neighborsOfA).toEqual([vertexB]); + expect(neighborsOfB).toEqual([vertexA, vertexC]); + }); + + // Test degree of a vertex + it('should return the degree of a vertex', () => { + const vertexA = new UndirectedVertex('A', 'Location A'); + const vertexB = new UndirectedVertex('B', 'Location B'); + const vertexC = new UndirectedVertex('C', 'Location C'); + const edgeAB = new UndirectedEdge('A', 'B', 3, 'Edge between A and B'); + const edgeBC = new UndirectedEdge('B', 'C', 4, 'Edge between B and C'); + + edgeAB.vertices = edgeAB.vertices; + expect(undirectedGraph.edges.size).toBe(0); + undirectedGraph.addVertex(vertexA); + undirectedGraph.addVertex(vertexB); + undirectedGraph.addVertex(vertexC); + undirectedGraph.addEdge(edgeAB); + undirectedGraph.addEdge(edgeBC); + + const degreeOfA = undirectedGraph.degreeOf('A'); + const degreeOfB = undirectedGraph.degreeOf('B'); + const degreeOfC = undirectedGraph.degreeOf('C'); + expect(undirectedGraph.edgeSet().length).toBe(2); + expect(undirectedGraph.getEndsOfEdge(edgeBC)?.length).toBe(2); + + expect(degreeOfA).toBe(1); + expect(degreeOfB).toBe(2); + expect(degreeOfC).toBe(1); + }); +}); diff --git a/test/unit/data-structures/heap/heap.test.ts b/test/unit/data-structures/heap/heap.test.ts index d827b91..19ed4dc 100644 --- a/test/unit/data-structures/heap/heap.test.ts +++ b/test/unit/data-structures/heap/heap.test.ts @@ -22,7 +22,7 @@ describe('Heap Operation Test', () => { }); it('should object heap work well', function () { - const minHeap = new MinHeap<{ a: string; key: number }>({comparator: (a, b) => a.key - b.key}); + const minHeap = new MinHeap<{a: string; key: number}>({comparator: (a, b) => a.key - b.key}); minHeap.add({key: 1, a: 'a1'}); minHeap.add({key: 6, a: 'a6'}); minHeap.add({key: 2, a: 'a2'}); @@ -37,7 +37,7 @@ describe('Heap Operation Test', () => { i++; } - const maxHeap = new MaxHeap<{ key: number; a: string }>({comparator: (a, b) => b.key - a.key}); + const maxHeap = new MaxHeap<{key: number; a: string}>({comparator: (a, b) => b.key - a.key}); maxHeap.add({key: 1, a: 'a1'}); maxHeap.add({key: 6, a: 'a6'}); maxHeap.add({key: 5, a: 'a5'}); diff --git a/test/unit/data-structures/linked-list/doubly-linked-list.test.ts b/test/unit/data-structures/linked-list/doubly-linked-list.test.ts index 0838337..eab2ad5 100644 --- a/test/unit/data-structures/linked-list/doubly-linked-list.test.ts +++ b/test/unit/data-structures/linked-list/doubly-linked-list.test.ts @@ -3,7 +3,7 @@ import {bigO, magnitude} from '../../../utils'; describe('DoublyLinkedList Operation Test', () => { let list: DoublyLinkedList; - let objectList: DoublyLinkedList<{ keyA: number }>; + let objectList: DoublyLinkedList<{keyA: number}>; beforeEach(() => { list = new DoublyLinkedList(); diff --git a/test/unit/data-structures/linked-list/singly-linked-list.test.ts b/test/unit/data-structures/linked-list/singly-linked-list.test.ts index 092b2e6..108f9ec 100644 --- a/test/unit/data-structures/linked-list/singly-linked-list.test.ts +++ b/test/unit/data-structures/linked-list/singly-linked-list.test.ts @@ -3,10 +3,10 @@ import {bigO, magnitude} from '../../../utils'; describe('SinglyLinkedList Operation Test', () => { let list: SinglyLinkedList; - let objectList: SinglyLinkedList<{ keyA: number }>; + let objectList: SinglyLinkedList<{keyA: number}>; beforeEach(() => { list = new SinglyLinkedList(); - objectList = new SinglyLinkedList<{ keyA: number }>(); + objectList = new SinglyLinkedList<{keyA: number}>(); }); describe('push', () => { @@ -393,7 +393,7 @@ describe('SinglyLinkedList Performance Test', () => { } // expect(performance.now() - startPopTime).toBeLessThan(bigO.LINEAR); - expect(performance.now() - startPopTime).toBeLessThan(bigO.LINEAR * 300); + expect(performance.now() - startPopTime).toBeLessThan(bigO.LINEAR * 400); }); }); describe('SinglyLinkedList', () => { diff --git a/test/unit/data-structures/matrix/matrix2d.test.ts b/test/unit/data-structures/matrix/matrix2d.test.ts index 2657f9f..a6213ea 100644 --- a/test/unit/data-structures/matrix/matrix2d.test.ts +++ b/test/unit/data-structures/matrix/matrix2d.test.ts @@ -1,5 +1,7 @@ import {Matrix2D, Vector2D} from '../../../../src'; +import {isDebugTest} from '../../../config'; +const isDebug = isDebugTest; describe('Matrix2D', () => { it('should initialize with default identity matrix', () => { const matrix = new Matrix2D(); @@ -136,3 +138,208 @@ describe('Matrix2D', () => { expect(result.m).toEqual(expectedMatrix); }); }); + +describe('Vector2D', () => { + it('should create a vector with default values', () => { + const vector = new Vector2D(); + expect(vector.x).toBe(0); + expect(vector.y).toBe(0); + expect(vector.w).toBe(1); + }); + + it('should correctly calculate vector length', () => { + const vector = new Vector2D(3, 4); + expect(vector.length).toBe(5); + }); + + it('should correctly add two vectors', () => { + const vector1 = new Vector2D(2, 3); + const vector2 = new Vector2D(1, 2); + const result = Vector2D.add(vector1, vector2); + expect(result.x).toBe(3); + expect(result.y).toBe(5); + }); + + // Add more test cases for Vector2D methods +}); + +describe('Matrix2D', () => { + it('should create an identity matrix by default', () => { + const matrix = new Matrix2D(); + expect(matrix.m).toEqual(Matrix2D.identity); + }); + + it('should correctly add two matrices', () => { + const matrix1 = new Matrix2D([ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9] + ]); + const matrix2 = new Matrix2D([ + [9, 8, 7], + [6, 5, 4], + [3, 2, 1] + ]); + const result = Matrix2D.add(matrix1, matrix2); + expect(result.m).toEqual([ + [10, 10, 10], + [10, 10, 10], + [10, 10, 10] + ]); + }); + + // Add more test cases for Matrix2D methods +}); + +describe('Matrix2D', () => { + it('should create a matrix with default identity values', () => { + const matrix = new Matrix2D(); + expect(matrix.m).toEqual(Matrix2D.identity); + }); + + it('should create a matrix from a Vector2D', () => { + const vector = new Vector2D(2, 3); + const matrix = new Matrix2D(vector); + expect(matrix.m).toEqual([ + [2, 0, 0], + [3, 1, 0], + [1, 0, 1] + ]); + }); + + it('should correctly add two matrices', () => { + const matrix1 = new Matrix2D([ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9] + ]); + const matrix2 = new Matrix2D([ + [9, 8, 7], + [6, 5, 4], + [3, 2, 1] + ]); + const result = Matrix2D.add(matrix1, matrix2); + expect(result.m).toEqual([ + [10, 10, 10], + [10, 10, 10], + [10, 10, 10] + ]); + }); + + it('should correctly subtract two matrices', () => { + const matrix1 = new Matrix2D([ + [5, 4, 3], + [2, 1, 0], + [0, 1, 2] + ]); + const matrix2 = new Matrix2D([ + [4, 3, 2], + [1, 0, 1], + [2, 1, 0] + ]); + const result = Matrix2D.subtract(matrix1, matrix2); + expect(result.m).toEqual([ + [1, 1, 1], + [1, 1, -1], + [-2, 0, 2] + ]); + }); + + it('should correctly multiply two matrices', () => { + const matrix1 = new Matrix2D([ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9] + ]); + const matrix2 = new Matrix2D([ + [9, 8, 7], + [6, 5, 4], + [3, 2, 1] + ]); + const result = Matrix2D.multiply(matrix1, matrix2); + expect(result.m).toEqual([ + [30, 24, 18], + [84, 69, 54], + [138, 114, 90] + ]); + }); + + it('should correctly multiply a matrix by a value', () => { + const matrix = new Matrix2D([ + [2, 3, 4], + [5, 6, 7], + [8, 9, 10] + ]); + const value = 2; + const result = Matrix2D.multiplyByValue(matrix, value); + expect(result.m).toEqual([ + [4, 6, 8], + [10, 12, 14], + [16, 18, 20] + ]); + }); + + it('should correctly multiply a matrix by a vector', () => { + const matrix = new Matrix2D([ + [2, 3, 4], + [5, 6, 7], + [8, 9, 10] + ]); + const vector = new Vector2D(2, 3); + const result = Matrix2D.multiplyByVector(matrix, vector); + isDebug && console.log(JSON.stringify(result)); + expect(result).toEqual({x: 17, y: 35, w: 1}); + }); + + it('should correctly create a view matrix', () => { + const width = 800; + const height = 600; + const viewMatrix = Matrix2D.view(width, height); + expect(viewMatrix.m).toEqual([ + [1, 0, 400], + [0, -1, 300], + [0, 0, 1] + ]); + }); + + it('should correctly scale a matrix', () => { + const factor = 3; + const scaledMatrix = Matrix2D.scale(factor); + expect(scaledMatrix.m).toEqual([ + [3, 0, 0], + [0, 3, 0], + [0, 0, 3] + ]); + }); + + it('should correctly rotate a matrix', () => { + const radians = Math.PI / 4; // 45 degrees + const rotationMatrix = Matrix2D.rotate(radians); + console.log(JSON.stringify(rotationMatrix.m)); + expect(rotationMatrix.m).toEqual([ + [0.7071067811865476, -0.7071067811865475, 0], + [0.7071067811865475, 0.7071067811865476, 0], + [0, 0, 1] + ]); + }); + + it('should correctly translate a matrix', () => { + const translationVector = new Vector2D(2, 3); + const translationMatrix = Matrix2D.translate(translationVector); + expect(translationMatrix.m).toEqual([ + [1, 0, 2], + [0, 1, 3], + [0, 0, 1] + ]); + }); + + it('should correctly convert a matrix to a vector', () => { + const matrix = new Matrix2D([ + [2, 3, 4], + [5, 6, 7], + [8, 9, 10] + ]); + const vector = matrix.toVector(); + expect(vector).toEqual(new Vector2D(2, 5)); + }); +}); diff --git a/test/unit/data-structures/matrix/navigator.test.ts b/test/unit/data-structures/matrix/navigator.test.ts index f8d3eda..69029a0 100644 --- a/test/unit/data-structures/matrix/navigator.test.ts +++ b/test/unit/data-structures/matrix/navigator.test.ts @@ -1,5 +1,7 @@ import {Character, Navigator, NavigatorParams, Turning} from '../../../../src'; +import {isDebugTest} from '../../../config'; +const isDebug = isDebugTest; const exampleMatrix: number[][] = [ [0, 0, 0, 0], [0, 1, 1, 0], @@ -17,7 +19,7 @@ const exampleTurning: Turning = { // Create a sample move callback function const exampleOnMove = () => { expect(true).toBeTruthy(); - // console.log(`Moved to position (${cur[0]}, ${cur[1]})`); + // isDebug && console.log(`Moved to position (${cur[0]}, ${cur[1]})`); }; // Create an initial parameter object for the example @@ -77,3 +79,166 @@ describe('Navigator class', () => { expect(navigator.check('left')).toBe(false); // Open path }); }); + +describe('Navigator', () => { + it('should correctly navigate the grid', () => { + // Define a sample grid and initial parameters + const matrix: number[][] = [ + [0, 0, 1], + [0, 1, 0], + [0, 0, 0] + ]; + + const turning: Turning = { + up: 'right', + right: 'down', + down: 'left', + left: 'up' + }; + + const init: NavigatorParams['init'] = { + cur: [0, 0], // Initial position + charDir: 'right', // Initial character direction + VISITED: 2 // Value to mark visited cells + }; + + // Initialize the navigator + const navigator = new Navigator({ + matrix, + turning, + init, + onMove: cur => cur + }); + + // Define a function to track visited cells + const visitedCells: number[][] = []; + const onMove = (cur: number[]) => { + visitedCells.push([...cur]); + }; + + // Attach the onMove function + navigator.onMove = onMove; + + // Start the navigation + navigator.start(); + + // The character should have navigated the grid correctly + expect(visitedCells).toEqual([ + [0, 1], + [0, 2], + [1, 2], + [2, 2], + [2, 1], + [2, 0], + [1, 0], + [1, 1] + ]); + }); + + it('should not move if there are no valid moves', () => { + // Define a sample grid with no valid moves + const matrix: number[][] = [ + [1, 1], + [1, 1] + ]; + + const turning: Turning = { + up: 'right', + right: 'down', + down: 'left', + left: 'up' + }; + + const init: NavigatorParams['init'] = { + cur: [0, 0], // Initial position + charDir: 'right', // Initial character direction + VISITED: 2 // Value to mark visited cells + }; + + // Initialize the navigator + const navigator = new Navigator({ + matrix, + turning, + init, + onMove: cur => cur + }); + + // Define a function to track visited cells + const visitedCells: number[][] = []; + const onMove = (cur: number[]) => { + visitedCells.push([...cur]); + }; + + // Attach the onMove function + navigator.onMove = onMove; + + // Start the navigation + navigator.start(); + + // The character should not move + isDebug && console.log(visitedCells); + expect(visitedCells).toEqual([ + [0, 1], + [1, 1], + [1, 0] + ]); + }); + + it('should handle edge cases and turns correctly', () => { + // Define a sample grid with turns and edge cases + const matrix: number[][] = [ + [1, 0, 0, 0], + [1, 0, 1, 0], + [0, 0, 1, 1] + ]; + + const turning: Turning = { + up: 'right', + right: 'down', + down: 'left', + left: 'up' + }; + + const init: NavigatorParams['init'] = { + cur: [0, 0], // Initial position + charDir: 'right', // Initial character direction + VISITED: 2 // Value to mark visited cells + }; + + // Initialize the navigator + const navigator = new Navigator({ + matrix, + turning, + init, + onMove: cur => cur + }); + + // Define a function to track visited cells + const visitedCells: number[][] = []; + const onMove = (cur: number[]) => { + visitedCells.push([...cur]); + }; + + // Attach the onMove function + navigator.onMove = onMove; + + // Start the navigation + navigator.start(); + + // The character should have navigated the grid, handled turns, and edge cases + isDebug && console.log(visitedCells); + expect(visitedCells).toEqual([ + [0, 1], + [0, 2], + [0, 3], + [1, 3], + [2, 3], + [2, 2], + [2, 1], + [2, 0], + [1, 0], + [1, 1], + [1, 2] + ]); + }); +}); diff --git a/test/unit/data-structures/matrix/vector2d.test.ts b/test/unit/data-structures/matrix/vector2d.test.ts new file mode 100644 index 0000000..93d1f8d --- /dev/null +++ b/test/unit/data-structures/matrix/vector2d.test.ts @@ -0,0 +1,171 @@ +import {Vector2D} from '../../../../src'; + +describe('Vector2D', () => { + it('should create a vector with default values', () => { + const vector = new Vector2D(); + expect(vector.x).toBe(0); + expect(vector.y).toBe(0); + expect(vector.w).toBe(1); + }); + + it('should correctly check if a vector is zero', () => { + const nonZeroVector = new Vector2D(3, 4); + const zeroVector = new Vector2D(0, 0); + expect(nonZeroVector.isZero).toBe(false); + expect(zeroVector.isZero).toBe(true); + }); + + it('should correctly calculate vector length', () => { + const vector = new Vector2D(3, 4); + expect(vector.length).toBe(5); + }); + + it('should correctly calculate squared vector length', () => { + const vector = new Vector2D(3, 4); + expect(vector.lengthSq).toBe(25); + }); + + it('should correctly round vector components', () => { + const vector = new Vector2D(3.6, 4.3); + const roundedVector = vector.rounded; + expect(roundedVector.x).toBe(4); + expect(roundedVector.y).toBe(4); + }); + + it('should correctly add two vectors', () => { + const vector1 = new Vector2D(2, 3); + const vector2 = new Vector2D(1, 2); + const result = Vector2D.add(vector1, vector2); + expect(result.x).toBe(3); + expect(result.y).toBe(5); + }); + + it('should correctly subtract two vectors', () => { + const vector1 = new Vector2D(4, 5); + const vector2 = new Vector2D(1, 2); + const result = Vector2D.subtract(vector1, vector2); + expect(result.x).toBe(3); + expect(result.y).toBe(3); + }); + + it('should correctly subtract value from a vector', () => { + const vector = new Vector2D(5, 7); + const result = Vector2D.subtractValue(vector, 3); + expect(result.x).toBe(2); + expect(result.y).toBe(4); + }); + + it('should correctly multiply a vector by a value', () => { + const vector = new Vector2D(2, 3); + const result = Vector2D.multiply(vector, 4); + expect(result.x).toBe(8); + expect(result.y).toBe(12); + }); + + it('should correctly divide a vector by a value', () => { + const vector = new Vector2D(6, 8); + const result = Vector2D.divide(vector, 2); + expect(result.x).toBe(3); + expect(result.y).toBe(4); + }); + + it('should correctly check if two vectors are equal', () => { + const vector1 = new Vector2D(3, 4); + const vector2 = new Vector2D(3, 4); + const vector3 = new Vector2D(4, 5); + expect(Vector2D.equals(vector1, vector2)).toBe(true); + expect(Vector2D.equals(vector1, vector3)).toBe(false); + }); + + it('should correctly check if two vectors are equal within a rounding factor', () => { + const vector1 = new Vector2D(3, 4); + const vector2 = new Vector2D(2.9, 3.9); + const vector3 = new Vector2D(4, 5); + expect(Vector2D.equalsRounded(vector1, vector2, 0.2)).toBe(true); + expect(Vector2D.equalsRounded(vector1, vector3, 0.2)).toBe(false); + }); + + it('should correctly normalize a vector', () => { + const vector = new Vector2D(3, 4); + const normalized = Vector2D.normalize(vector); + expect(normalized.x).toBeCloseTo(0.6); + expect(normalized.y).toBeCloseTo(0.8); + }); + + it('should correctly truncate a vector', () => { + const vector = new Vector2D(3, 4); + const truncated = Vector2D.truncate(vector, 4); + expect(truncated.length).toBeLessThanOrEqual(4); + }); + + it('should correctly get the perpendicular vector', () => { + const vector = new Vector2D(3, 4); + const perpendicular = Vector2D.perp(vector); + expect(Vector2D.dot(vector, perpendicular)).toBe(0); + }); + + it('should correctly reverse the vector', () => { + const vector = new Vector2D(3, 4); + const reversed = Vector2D.reverse(vector); + expect(reversed.x).toBe(-3); + expect(reversed.y).toBe(-4); + }); + + it('should correctly get the absolute vector', () => { + const vector = new Vector2D(-3, 4); + const absVector = Vector2D.abs(vector); + expect(absVector.x).toBe(3); + expect(absVector.y).toBe(4); + }); + + it('should correctly calculate the dot product of two vectors', () => { + const vector1 = new Vector2D(2, 3); + const vector2 = new Vector2D(4, 5); + const dotProduct = Vector2D.dot(vector1, vector2); + expect(dotProduct).toBe(23); + }); + + it('should correctly calculate the distance between two vectors', () => { + const vector1 = new Vector2D(1, 1); + const vector2 = new Vector2D(4, 5); + const distance = Vector2D.distance(vector1, vector2); + expect(distance).toBeCloseTo(5); + }); + + it('should correctly calculate the squared distance between two vectors', () => { + const vector1 = new Vector2D(1, 1); + const vector2 = new Vector2D(4, 5); + const distanceSq = Vector2D.distanceSq(vector1, vector2); + expect(distanceSq).toBe(25); + }); + + it('should correctly determine the sign of the cross product of two vectors', () => { + const vector1 = new Vector2D(2, 3); + const vector2 = new Vector2D(4, 5); + const sign = Vector2D.sign(vector1, vector2); + expect(sign).toBe(-1); // Assuming specific vector values, the result may vary + }); + + it('should correctly calculate the angle between a vector and the negative y-axis', () => { + const vector = new Vector2D(3, 4); + const angle = Vector2D.angle(vector); + expect(angle).toBeCloseTo(2.498091544796509, 3); + }); + + it('should create a random vector within the specified range', () => { + const maxX = 10; + const maxY = 10; + const randomVector = Vector2D.random(maxX, maxY); + expect(randomVector.x).toBeGreaterThanOrEqual(-maxX / 2); + expect(randomVector.x).toBeLessThanOrEqual(maxX / 2); + expect(randomVector.y).toBeGreaterThanOrEqual(-maxY / 2); + expect(randomVector.y).toBeLessThanOrEqual(maxY / 2); + }); + + it('should zero the vector components', () => { + const vector = new Vector2D(3, 4); + vector.zero(); + expect(vector.x).toBe(0); + expect(vector.y).toBe(0); + }); +}); diff --git a/test/unit/data-structures/priority-queue/max-priority-queue.test.ts b/test/unit/data-structures/priority-queue/max-priority-queue.test.ts index 8b2f287..26fd837 100644 --- a/test/unit/data-structures/priority-queue/max-priority-queue.test.ts +++ b/test/unit/data-structures/priority-queue/max-priority-queue.test.ts @@ -17,7 +17,7 @@ describe('MaxPriorityQueue Operation Test', () => { }); it('should add elements and maintain heap property in a object MaxPriorityQueue', () => { - const priorityQueue = new MaxPriorityQueue<{ keyA: number }>({comparator: (a, b) => b.keyA - a.keyA}); + const priorityQueue = new MaxPriorityQueue<{keyA: number}>({comparator: (a, b) => b.keyA - a.keyA}); priorityQueue.refill([{keyA: 5}, {keyA: 3}, {keyA: 1}]); priorityQueue.add({keyA: 7}); @@ -64,7 +64,7 @@ describe('MaxPriorityQueue Operation Test', () => { it('should correctly heapify an object array', () => { const nodes = [{keyA: 5}, {keyA: 3}, {keyA: 7}, {keyA: 1}]; - const maxPQ = MaxPriorityQueue.heapify<{ keyA: number }>({nodes: nodes, comparator: (a, b) => b.keyA - a.keyA}); + const maxPQ = MaxPriorityQueue.heapify<{keyA: number}>({nodes: nodes, comparator: (a, b) => b.keyA - a.keyA}); expect(maxPQ.poll()?.keyA).toBe(7); expect(maxPQ.poll()?.keyA).toBe(5); diff --git a/test/unit/data-structures/queue/deque.test.ts b/test/unit/data-structures/queue/deque.test.ts index ca41a61..bc89ebf 100644 --- a/test/unit/data-structures/queue/deque.test.ts +++ b/test/unit/data-structures/queue/deque.test.ts @@ -1,6 +1,8 @@ import {ArrayDeque, Deque, ObjectDeque} from '../../../../src'; import {bigO} from '../../../utils'; +import {isDebugTest} from '../../../config'; +const isDebug = isDebugTest; describe('Deque Tests', () => { // Test cases for the Deque class (DoublyLinkedList-based) describe('Deque (DoublyLinkedList-based)', () => { @@ -141,7 +143,268 @@ describe('Deque Performance Test', () => { for (let i = 0; i < dataSize; i++) { queue.pop(); } - console.log(`Queue Deque Test: ${performance.now() - startTime} ms`); + isDebug && console.log(`Queue Deque Test: ${performance.now() - startTime} ms`); expect(performance.now() - startTime).toBeLessThan(bigO.LINEAR * 100); }); }); + +describe('Deque', () => { + let deque: Deque; + + beforeEach(() => { + deque = new Deque(); + }); + + test('should initialize an empty deque', () => { + expect(deque.size).toBe(0); + expect(deque.isEmpty()).toBe(true); + }); + + test('should add elements to the front and back', () => { + deque.addFirst(1); + deque.addLast(2); + + expect(deque.size).toBe(2); + expect(deque.peekFirst()).toBe(1); + expect(deque.peekLast()).toBe(2); + }); + + test('should remove elements from the front and back', () => { + deque.addFirst(1); + deque.addLast(2); + + const firstElement = deque.pollFirst(); + const lastElement = deque.pollLast(); + + expect(deque.size).toBe(0); + expect(firstElement).toBe(1); + expect(lastElement).toBe(2); + }); + + test('should get elements by index', () => { + deque.addLast(1); + deque.addLast(2); + deque.addLast(3); + + expect(deque.getAt(0)).toBe(1); + expect(deque.getAt(1)).toBe(2); + expect(deque.getAt(2)).toBe(3); + }); + + test('should return null for out-of-bounds index', () => { + expect(deque.getAt(0)).toBe(undefined); + expect(deque.getAt(1)).toBe(undefined); + expect(deque.getAt(-1)).toBe(undefined); + }); + + test('should check if the deque is empty', () => { + expect(deque.isEmpty()).toBe(true); + + deque.addLast(1); + expect(deque.isEmpty()).toBe(false); + + deque.pollFirst(); + expect(deque.isEmpty()).toBe(true); + }); +}); + +describe('ArrayDeque', () => { + let deque: ArrayDeque; + + beforeEach(() => { + deque = new ArrayDeque(); + }); + + test('should initialize an empty deque', () => { + expect(deque.size).toBe(0); + expect(deque.isEmpty()).toBe(true); + }); + + test('should add elements to the front and back', () => { + deque.addFirst(1); + deque.addLast(2); + + expect(deque.size).toBe(2); + expect(deque.peekFirst()).toBe(1); + expect(deque.peekLast()).toBe(2); + }); + + test('should remove elements from the front and back', () => { + deque.addFirst(1); + deque.addLast(2); + + const firstElement = deque.pollFirst(); + const lastElement = deque.pollLast(); + + expect(deque.size).toBe(0); + expect(firstElement).toBe(1); + expect(lastElement).toBe(2); + }); + + test('should get elements by index', () => { + deque.addLast(1); + deque.addLast(2); + deque.addLast(3); + + expect(deque.get(0)).toBe(1); + expect(deque.get(1)).toBe(2); + expect(deque.get(2)).toBe(3); + }); + + test('should return null for out-of-bounds index', () => { + expect(deque.get(0)).toBe(null); + expect(deque.get(1)).toBe(null); + expect(deque.get(-1)).toBe(null); + }); + + test('should check if the deque is empty', () => { + expect(deque.isEmpty()).toBe(true); + + deque.addLast(1); + expect(deque.isEmpty()).toBe(false); + + deque.pollFirst(); + expect(deque.isEmpty()).toBe(true); + }); + + test('should set elements at a specific index', () => { + deque.addLast(1); + deque.addLast(2); + deque.addLast(3); + + deque.set(1, 4); + + expect(deque.get(0)).toBe(1); + expect(deque.get(1)).toBe(4); + expect(deque.get(2)).toBe(3); + }); + + test('should insert elements at a specific index', () => { + deque.addLast(1); + deque.addLast(2); + deque.addLast(3); + + deque.insert(1, 4); + + expect(deque.size).toBe(4); + expect(deque.get(0)).toBe(1); + expect(deque.get(1)).toBe(4); + expect(deque.get(2)).toBe(2); + expect(deque.get(3)).toBe(3); + }); + + test('should delete elements at a specific index', () => { + deque.addLast(1); + deque.addLast(2); + deque.addLast(3); + + const deletedElement = deque.delete(1); + + expect(deque.size).toBe(2); + expect(deletedElement[0]).toBe(2); + expect(deque.get(0)).toBe(1); + expect(deque.get(1)).toBe(3); + }); +}); + +describe('ObjectDeque', () => { + let deque: ObjectDeque; + + beforeEach(() => { + deque = new ObjectDeque(); + }); + + test('should add elements to the front of the deque', () => { + deque.addFirst(1); + deque.addFirst(2); + + expect(deque.size).toBe(2); + expect(deque.peekFirst()).toBe(2); + expect(deque.peekLast()).toBe(1); + }); + + test('should add elements to the end of the deque', () => { + deque.addLast(1); + deque.addLast(2); + + expect(deque.size).toBe(2); + expect(deque.peekFirst()).toBe(1); + expect(deque.peekLast()).toBe(2); + }); + + test('should remove elements from the front of the deque', () => { + deque.addLast(1); + deque.addLast(2); + + const removedElement = deque.pollFirst(); + + expect(deque.size).toBe(1); + expect(removedElement).toBe(1); + expect(deque.peekFirst()).toBe(2); + }); + + test('should remove elements from the end of the deque', () => { + deque.addLast(1); + deque.addLast(2); + + const removedElement = deque.pollFirst(); + + expect(deque.size).toBe(1); + expect(removedElement).toBe(1); + expect(deque.peekLast()).toBe(2); + }); + + test('should return the element at the front of the deque without removing it', () => { + deque.addFirst(1); + deque.addFirst(2); + + expect(deque.peekFirst()).toBe(2); + expect(deque.size).toBe(2); + }); + + test('should return the element at the end of the deque without removing it', () => { + deque.addLast(1); + deque.addLast(2); + + expect(deque.peekLast()).toBe(2); + expect(deque.size).toBe(2); + }); + + test('should return the correct size of the deque', () => { + deque.addFirst(1); + deque.addLast(2); + deque.addLast(3); + + expect(deque.size).toBe(3); + }); + + test('should check if the deque is empty', () => { + expect(deque.isEmpty()).toBe(true); + + deque.addFirst(1); + + expect(deque.isEmpty()).toBe(false); + }); + + test('should set elements at a specific index', () => { + deque.addFirst(1); + deque.addLast(2); + deque.addLast(3); + + expect(deque.peekFirst()).toBe(1); + expect(deque.get(1)).toBe(2); + expect(deque.peekLast()).toBe(3); + }); + + test('should insert elements at a specific index', () => { + deque.addFirst(1); + deque.addLast(2); + deque.addLast(3); + + expect(deque.size).toBe(3); + expect(deque.peekFirst()).toBe(1); + expect(deque.get(1)).toBe(2); + expect(deque.get(2)).toBe(3); + expect(deque.peekLast()).toBe(3); + }); +}); diff --git a/test/unit/data-structures/queue/queue.test.ts b/test/unit/data-structures/queue/queue.test.ts index 93d15bc..a32e811 100644 --- a/test/unit/data-structures/queue/queue.test.ts +++ b/test/unit/data-structures/queue/queue.test.ts @@ -1,6 +1,8 @@ import {LinkedListQueue, Queue} from '../../../../src'; import {bigO, magnitude} from '../../../utils'; +import {isDebugTest} from '../../../config'; +const isDebug = isDebugTest; describe('Queue Operation Test', () => { it('should validate a queue', () => { const queue = new Queue(); @@ -209,7 +211,7 @@ describe('Queue Performance Test', () => { for (let i = 0; i < dataSize; i++) { queue.dequeue(); } - console.log(`Queue Performance Test: ${performance.now() - startTime} ms`); + isDebug && console.log(`Queue Performance Test: ${performance.now() - startTime} ms`); expect(performance.now() - startTime).toBeLessThan(bigO.LINEAR * 100); }); diff --git a/test/utils/big-o.ts b/test/utils/big-o.ts index cc9fc86..43d7eca 100644 --- a/test/utils/big-o.ts +++ b/test/utils/big-o.ts @@ -26,7 +26,7 @@ export const bigO = { function findPotentialN(input: any): number { let longestArray: any[] = []; - let mostProperties: { [key: string]: any } = {}; + let mostProperties: {[key: string]: any} = {}; function recurse(obj: any) { if (Array.isArray(obj)) {