├── .eslintignore ├── ts ├── Interfaces │ ├── IKeyValuePair.ts │ ├── ILinkedListItem.ts │ ├── IExtendedEuclidesAlgorithmResult.ts │ ├── IObjectArrayProperty.ts │ ├── IBitArray.ts │ ├── ILinkedList.ts │ ├── IBinaryTreeNode.ts │ ├── IBinaryTree.ts │ ├── IHashTable.ts │ └── IBitMatrix.ts ├── CustomTypes │ └── SortingComparator.ts ├── Graph │ ├── index.ts │ └── DirectedAdjacencyMatrixGraph.ts ├── BinaryTree │ ├── index.ts │ ├── BinaryTreeNode.ts │ ├── RedBlackTreeNode.ts │ ├── BinaryTree.ts │ └── RedBlackTree.ts ├── linearSearch.ts ├── TypedLinkedList.ts ├── Sort │ ├── index.ts │ ├── selectionSort.ts │ ├── gnomeSort.ts │ ├── bubbleSort.ts │ ├── insertionSort.ts │ ├── mergeSort.ts │ └── quickSort.ts ├── binarySum.ts ├── functions │ ├── index.ts │ ├── leastCommonMultiple.ts │ ├── diophantineEquation.ts │ ├── greatestCommonDivisor.ts │ ├── diophantineEquationSolver.ts │ └── extendedEuclidesAlgorithm.ts ├── Queue.ts ├── Stack.ts ├── Trie.ts ├── index.ts ├── TypedQueue.ts ├── TypedStack.ts ├── Utils.ts ├── TrieWithValue.ts ├── FindMaximumSubarray.ts ├── ObjectArray.ts ├── RadixTreeNode.ts ├── ITypedArray.ts ├── LinkedList.ts ├── RadixTree.ts ├── HashTable.ts ├── BitArray.ts └── BitMatrix.ts ├── .travis.yml ├── jestconfig.json ├── .codeclimate.yml ├── tests ├── binarySumSpec.ts ├── linearSearchSpec.ts ├── QueueSpec.ts ├── functions │ ├── diophantineEquationSpec.ts │ ├── leastCommonMultipleSpec.ts │ ├── diophantineEquationSolverSpec.ts │ ├── greatestCommonDivisorSpec.ts │ └── extendedEuclidesAlgorithmSpec.ts ├── Trie.ts ├── TypedQueueSpec.ts ├── FindMaximumSubarraySpec.ts ├── UtilsSpec.ts ├── Sort │ ├── mergeSortSpec.ts │ ├── gnomeSortSpec.ts │ ├── bubbleSortSpec.ts │ ├── quickSortSpec.ts │ ├── insertionSortSpec.ts │ └── selectionSortSpec.ts ├── TrieWithValues.ts ├── ObjectArraySpec.ts ├── RadixTreeSpec.ts ├── Graph │ └── DirectedAdjacencyMatrixGraphSpec.ts ├── StackSpec.ts ├── TypedStackSpec.ts ├── LinkedListSpec.ts ├── RadixTreeNodeSpec.ts ├── BitArraySpec.ts ├── HashTableSpec.ts ├── BinaryTreeSpec.ts ├── RedBlackTreeSpec.ts └── BitMatrixSpec.ts ├── tsconfig.json ├── azure-pipelines.yml ├── README.md ├── .gitignore ├── LICENSE ├── package.json ├── tslint.json └── .eslintrc /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*{.,-}min.js 2 | -------------------------------------------------------------------------------- /ts/Interfaces/IKeyValuePair.ts: -------------------------------------------------------------------------------- 1 | export interface IKeyValuePair { 2 | key: T; 3 | value: U; 4 | } -------------------------------------------------------------------------------- /ts/CustomTypes/SortingComparator.ts: -------------------------------------------------------------------------------- 1 | type SortingComparator = (a: T, b: T) => boolean; 2 | 3 | export default SortingComparator; -------------------------------------------------------------------------------- /ts/Interfaces/ILinkedListItem.ts: -------------------------------------------------------------------------------- 1 | export interface ILinkedListItem { 2 | next:ILinkedListItem; 3 | prev:ILinkedListItem; 4 | value:T; 5 | } -------------------------------------------------------------------------------- /ts/Interfaces/IExtendedEuclidesAlgorithmResult.ts: -------------------------------------------------------------------------------- 1 | export interface IExtendedEuclidesAlgorithmResult { 2 | x: number; 3 | y: number; 4 | gcd: number; 5 | } -------------------------------------------------------------------------------- /ts/Graph/index.ts: -------------------------------------------------------------------------------- 1 | import {DirectedAdjacencyMatrixGraph} from "./DirectedAdjacencyMatrixGraph"; 2 | 3 | const Graph = { 4 | DirectedAdjacencyMatrixGraph 5 | } 6 | export default Graph; -------------------------------------------------------------------------------- /ts/Interfaces/IObjectArrayProperty.ts: -------------------------------------------------------------------------------- 1 | import {ITypedArray} from "../ITypedArray"; 2 | export interface IObjectArrayProperty { 3 | name: string; 4 | type: new (size:number) => ITypedArray; 5 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | language: node_js 4 | node_js: 5 | - '10.14.2' 6 | before_install: 7 | after_script: 8 | - npm install -g codeclimate-test-reporter 9 | - codeclimate-test-reporter < ./coverage/lcov.info -------------------------------------------------------------------------------- /ts/Interfaces/IBitArray.ts: -------------------------------------------------------------------------------- 1 | export interface IBitArray { 2 | count(): number; 3 | get(index: number): boolean; 4 | getIndexes(): number[]; 5 | reset(): IBitArray; 6 | resize(newSize: number): IBitArray; 7 | size(): number; 8 | set(index: number, value: boolean): IBitArray; 9 | } -------------------------------------------------------------------------------- /jestconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "transform": { 3 | "^.+\\.(t|j)sx?$": "ts-jest" 4 | }, 5 | "testRegex": "(/tests/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", 6 | "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"], 7 | "collectCoverage": true, 8 | "coverageDirectory": "coverage" 9 | } -------------------------------------------------------------------------------- /ts/Interfaces/ILinkedList.ts: -------------------------------------------------------------------------------- 1 | export interface ILinkedList { 2 | forEach:(callback: (item: T, index:number, list:ILinkedList) => void, thisArg:any) => void; 3 | isEmpty: () => boolean; 4 | push: (value:T) => void; 5 | pop: () => T; 6 | remove: (value:T) => void; 7 | shift: () => T; 8 | unshift: (value:T) => void; 9 | } -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | duplication: 4 | enabled: true 5 | config: 6 | languages: 7 | - javascript 8 | eslint: 9 | enabled: true 10 | fixme: 11 | enabled: true 12 | ratings: 13 | paths: 14 | - "**.inc" 15 | - "**.js" 16 | - "**.jsx" 17 | - "**.module" 18 | exclude_paths: 19 | - tests/ 20 | -------------------------------------------------------------------------------- /tests/binarySumSpec.ts: -------------------------------------------------------------------------------- 1 | import binarySum from '../ts/binarySum'; 2 | describe('Binary sum', function () { 3 | it('should sum to arrays', function () { 4 | expect(binarySum([1,1], [0,1])).toEqual([1,0,0]); //3 + 1 = 4 5 | expect(binarySum([1,0], [0,1])).toEqual([0,1,1]); //2 + 1 = 3 6 | expect(binarySum([0,1,1,0], [1,1,1,1])).toEqual([1,0,1,0,1]); //6 + 15 = 3 7 | }); 8 | }); -------------------------------------------------------------------------------- /tests/linearSearchSpec.ts: -------------------------------------------------------------------------------- 1 | import linearSearch from "../ts/linearSearch"; 2 | describe('Linear search', function () { 3 | it('should return null when value is not found', function () { 4 | expect(linearSearch([1,4,2,3], 5)).toBeNull(); 5 | }); 6 | 7 | it('should return index of value', function () { 8 | expect(linearSearch([1,4,2,3], 1)).toEqual(0); 9 | }); 10 | }); -------------------------------------------------------------------------------- /ts/BinaryTree/index.ts: -------------------------------------------------------------------------------- 1 | import * as BTImplementatio from "./BinaryTree"; 2 | import {BinaryTreeNode} from "./BinaryTreeNode"; 3 | import RedBlackTree from "./RedBlackTree"; 4 | import RedBlackTreeNode from "./RedBlackTreeNode"; 5 | 6 | const BinaryTree = { 7 | BinaryTree: BTImplementatio.BinaryTree, 8 | BinaryTreeNode, 9 | RedBlackTree, 10 | RedBlackTreeNode 11 | } 12 | export default BinaryTree; -------------------------------------------------------------------------------- /ts/linearSearch.ts: -------------------------------------------------------------------------------- 1 | function linearSearch (array:number[], value:number) { 2 | let index:number = null; 3 | let counter = 0; 4 | const arraySize = array.length; 5 | while (counter < arraySize && index === null) { 6 | if (value === array[counter]) { 7 | index = counter; 8 | } 9 | counter++; 10 | } 11 | return index; 12 | } 13 | 14 | export default linearSearch; -------------------------------------------------------------------------------- /ts/TypedLinkedList.ts: -------------------------------------------------------------------------------- 1 | import {ILinkedList} from "./Interfaces/ILinkedList"; 2 | import {ITypedArray} from "./ITypedArray"; 3 | /*class TypedLinkedList implements ILinkedList { 4 | public list:ITypedArray; 5 | public prevList:ITypedArray; 6 | public nextList:ITypedArray; 7 | 8 | constructor (TypedArray:{new(size): ITypedArray}, size:number) { 9 | this.list = new TypedArray(size); 10 | } 11 | }*/ -------------------------------------------------------------------------------- /ts/Interfaces/IBinaryTreeNode.ts: -------------------------------------------------------------------------------- 1 | export class IBinaryTreeNode{ 2 | left:IBinaryTreeNode; 3 | right:IBinaryTreeNode; 4 | value:T; 5 | parent:IBinaryTreeNode; 6 | hasLeftChild: () => boolean; 7 | hasRightChild: ()=> boolean; 8 | isLeftChild: ()=> boolean; 9 | isRightChild: ()=> boolean; 10 | isRoot: ()=> boolean; 11 | min: ()=> IBinaryTreeNode; 12 | max: ()=> IBinaryTreeNode; 13 | } -------------------------------------------------------------------------------- /ts/Sort/index.ts: -------------------------------------------------------------------------------- 1 | import bubbleSort from "./bubbleSort"; 2 | import gnomeSort from "./gnomeSort"; 3 | import insertionSort from "./insertionSort"; 4 | import mergeSort from "./mergeSort"; 5 | import quickSort from "./quickSort"; 6 | import selectionSort from "./selectionSort"; 7 | 8 | const Sort = { 9 | bubbleSort, 10 | gnomeSort, 11 | insertionSort, 12 | mergeSort, 13 | quickSort, 14 | selectionSort 15 | }; 16 | 17 | export default Sort; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "UMD", 4 | "noImplicitAny": true, 5 | "removeComments": true, 6 | "preserveConstEnums": true, 7 | "outDir": "dist/", 8 | "sourceMap": true, 9 | "declaration": true, 10 | "types": ["jest"], 11 | "target": "ES2017", 12 | "moduleResolution": "node" 13 | }, 14 | "exclude": [ 15 | "node_modules", 16 | "wwwroot", 17 | "dist", 18 | "tests" 19 | ] 20 | } -------------------------------------------------------------------------------- /ts/binarySum.ts: -------------------------------------------------------------------------------- 1 | function binarySum(A:number[], B:number[]) { 2 | var i:number = 0; 3 | var n:number = A.length; 4 | var C:number[] = []; 5 | A = A.slice(0).reverse(); 6 | B = B.slice(0).reverse(); 7 | while (i < n) { 8 | let carryOn = C[i]; 9 | C[i] = A[i] ^ B[i] ^ carryOn; 10 | C[i + 1] = (A[i] & B[i]) | (A[i] & carryOn) | (B[i] & carryOn); 11 | i++; 12 | } 13 | return C.reverse(); 14 | } 15 | 16 | export default binarySum; -------------------------------------------------------------------------------- /ts/Interfaces/IBinaryTree.ts: -------------------------------------------------------------------------------- 1 | interface IBinaryTree { 2 | add: (value: T) => void; 3 | remove: (value: T) => boolean; 4 | reverseTreeWalk: (callback: (pv: any, cv: T) => any, initialValue: any) => any; 5 | inOrderTreeWalk: (callback: (pv: any, cv: T) => any, initialValue: any) => any; 6 | isEmpty: () => boolean; 7 | max: () => U; 8 | min: () => U; 9 | search: (value: T) => U; 10 | successor: (value: T) => U; 11 | } 12 | 13 | export default IBinaryTree; -------------------------------------------------------------------------------- /tests/QueueSpec.ts: -------------------------------------------------------------------------------- 1 | import {Queue} from "../ts/Queue"; 2 | describe('Queue', function () { 3 | 4 | describe('enqueue', function () { 5 | it('should add elements to the stack', function () { 6 | var q = new Queue(); 7 | q.enqueue(1); 8 | expect(q.size()).toEqual(1); 9 | q.enqueue(2); 10 | expect(q.size()).toEqual(2); 11 | q.dequeue(); 12 | expect(q.size()).toEqual(1); 13 | }); 14 | }); 15 | }); -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Node.js 2 | # Build a general Node.js project with npm. 3 | # Add steps that analyze code, save build artifacts, deploy, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/languages/javascript 5 | 6 | trigger: 7 | - master 8 | 9 | pool: 10 | vmImage: 'Ubuntu-16.04' 11 | 12 | steps: 13 | - task: NodeTool@0 14 | inputs: 15 | versionSpec: '10.x' 16 | displayName: 'Install Node.js' 17 | 18 | - script: | 19 | npm install 20 | npm test 21 | displayName: 'npm install and test' 22 | -------------------------------------------------------------------------------- /ts/Interfaces/IHashTable.ts: -------------------------------------------------------------------------------- 1 | import {IKeyValuePair} from "./IKeyValuePair"; 2 | 3 | export interface IHashTable { 4 | clear(): void; 5 | clone(): IHashTable; 6 | contains(value: V): boolean; 7 | containsKey(value: K): boolean; 8 | containsValue(value: V): boolean; 9 | elements(): IKeyValuePair[]; 10 | get(key: K): V; 11 | put(key: K, value: V): void; 12 | putAll(elements: IKeyValuePair[]): void; 13 | remove(key: K): V; 14 | size(): number; 15 | values(): V[]; 16 | } -------------------------------------------------------------------------------- /ts/functions/index.ts: -------------------------------------------------------------------------------- 1 | import {diophantineEquation} from "./diophantineEquation"; 2 | import {diophantineEquationSolver} from "./diophantineEquationSolver"; 3 | import {extendedEuclidesAlgorithm} from "./extendedEuclidesAlgorithm"; 4 | import {greatestCommonDivisor} from "./greatestCommonDivisor"; 5 | import {leastCommonMultiple} from "./leastCommonMultiple"; 6 | 7 | const functions = { 8 | diophantineEquation, 9 | diophantineEquationSolver, 10 | extendedEuclidesAlgorithm, 11 | greatestCommonDivisor, 12 | leastCommonMultiple 13 | }; 14 | export default functions; -------------------------------------------------------------------------------- /ts/functions/leastCommonMultiple.ts: -------------------------------------------------------------------------------- 1 | import { greatestCommonDivisor } from "./greatestCommonDivisor"; 2 | 3 | /** 4 | * Least common multiple algorithm. If a and b are 0 it is a special cases and 0 will be returned. If one of the number is 0 the other one will be returned. 5 | * @param {number} a 6 | * @param {number} b 7 | */ 8 | export function leastCommonMultiple(a: number, b: number): number { 9 | if (a === 0 && b === 0) { 10 | return 0; 11 | } 12 | if (a === 0) { 13 | return b; 14 | } 15 | if (b === 0) { 16 | return Math.abs(a); 17 | } 18 | return Math.abs(a * b) / greatestCommonDivisor(Math.abs(a), Math.abs(b)) 19 | } -------------------------------------------------------------------------------- /ts/Sort/selectionSort.ts: -------------------------------------------------------------------------------- 1 | import {Utils} from "../Utils"; 2 | import SortingComparator from "../CustomTypes/SortingComparator"; 3 | function selectionSort(array: T[], comparator: SortingComparator = Utils.gt) { 4 | if (!Array.isArray(array) || array.length < 2) { 5 | return array; 6 | } 7 | 8 | let position: number = 0; 9 | const arraySize: number = array.length; 10 | while (position < arraySize) { 11 | const lowestIndex: number = Utils.minIndex(array, position, arraySize, comparator); 12 | Utils.swapValues(array, position, lowestIndex); 13 | position++; 14 | } 15 | 16 | return array; 17 | } 18 | 19 | export default selectionSort; -------------------------------------------------------------------------------- /ts/Interfaces/IBitMatrix.ts: -------------------------------------------------------------------------------- 1 | export interface IBitMatrix { 2 | clone(): IBitMatrix; 3 | count(): number; 4 | get(rowIndex: number, colIndex: number): boolean; 5 | getIndexes(resultPerColumn?: boolean): number[][]; 6 | getRowIndexes(row: number): number[]; 7 | getColIndexes(column: number): number[]; 8 | reset(): IBitMatrix; 9 | resize(newRowCount: number, newColCount: number): IBitMatrix; 10 | size(): number[]; 11 | set(rowIndex: number, colIndex: number, value: boolean): IBitMatrix; 12 | setBuffer(newBuffer: Uint32Array): IBitMatrix; 13 | spliceColumn(startIndex: number, deleteCount: number): IBitMatrix; 14 | spliceRow(startIndex: number, deleteCount: number): IBitMatrix; 15 | } -------------------------------------------------------------------------------- /ts/Sort/gnomeSort.ts: -------------------------------------------------------------------------------- 1 | function gnomeSort(array: T[], comparator: (a: T, b: T) => boolean = (a, b) => a > b) { 2 | if (!Array.isArray(array) || array.length < 2) { 3 | return array; 4 | } 5 | 6 | let position: number = 0; 7 | let length: number = array.length; 8 | let temp: T = null; 9 | while (position < length) { 10 | if (position === 0 || !comparator(array[position - 1], array[position])) { 11 | position++; 12 | } else { 13 | temp = array[position - 1]; 14 | array[position - 1] = array[position]; 15 | array[position] = temp; 16 | position--; 17 | } 18 | } 19 | return array; 20 | } 21 | 22 | export default gnomeSort; -------------------------------------------------------------------------------- /ts/Sort/bubbleSort.ts: -------------------------------------------------------------------------------- 1 | function bubbleSort(array: T[], comparator: (a: T, b: T) => boolean = (a, b) => a > b) { 2 | if (!Array.isArray(array) || array.length < 2) { 3 | return array; 4 | } 5 | let isNotSorted: boolean = true; 6 | let size: number = array.length - 1; 7 | let temp: T; 8 | while (isNotSorted) { 9 | isNotSorted = false; 10 | for (let i = 0; i < size; i++) { 11 | if (comparator(array[i], array[i + 1])) { 12 | temp = array[i + 1]; 13 | array[i + 1] = array[i]; 14 | array[i] = temp; 15 | isNotSorted = true; 16 | } 17 | } 18 | } 19 | return array; 20 | } 21 | 22 | export default bubbleSort; -------------------------------------------------------------------------------- /ts/Queue.ts: -------------------------------------------------------------------------------- 1 | export class Queue { 2 | protected queue: T[] = []; 3 | protected head: number = 0; 4 | protected MAX_QUEUE_SIZE = 1000; 5 | 6 | constructor(MAX_QUEUE_SIZE?: number) { 7 | this.MAX_QUEUE_SIZE = MAX_QUEUE_SIZE; 8 | } 9 | 10 | public enqueue (element: T) { 11 | this.queue.push(element); 12 | } 13 | 14 | public dequeue (): T { 15 | const output = this.queue[this.head]; 16 | this.queue[this.head] = null; 17 | this.head++; 18 | if (this.head >= this.MAX_QUEUE_SIZE) { 19 | this.queue.splice(0, this.head); 20 | this.head = 0; 21 | } 22 | 23 | return output; 24 | } 25 | 26 | public size () { 27 | return this.queue.length - this.head; 28 | } 29 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TypeScript algorithms and data structures 2 | 3 | Useful algorithms and Data structures written in typescript. 4 | 5 | [![Build Status](https://travis-ci.org/sularome/TypeScript-Algorithms-and-Data-Structures.svg?branch=master)](https://travis-ci.org/sularome/TypeScript-Algorithms-and-Data-Structures) [![Code Climate](https://codeclimate.com/github/sularome/JavaScript-Algorithms/badges/gpa.svg)](https://codeclimate.com/github/sularome/JavaScript-Algorithms) [![Test Coverage](https://codeclimate.com/github/sularome/JavaScript-Algorithms/badges/coverage.svg)](https://codeclimate.com/github/sularome/JavaScript-Algorithms/coverage) [![Issue Count](https://codeclimate.com/github/sularome/JavaScript-Algorithms/badges/issue_count.svg)](https://codeclimate.com/github/sularome/JavaScript-Algorithms) 6 | -------------------------------------------------------------------------------- /tests/functions/diophantineEquationSpec.ts: -------------------------------------------------------------------------------- 1 | import { diophantineEquation } from "../../ts/functions/diophantineEquation"; 2 | 3 | describe("diophantineEquation", () => { 4 | it("should return NaN if both parameters are 0", () => { 5 | expect(diophantineEquation(0, 0, 0)).toBeNull(); 6 | }); 7 | it("should return NaN if gcd(a, b) foes not divide c", () => { 8 | expect(diophantineEquation(15, 20, 3)).toBeNull(); 9 | }); 10 | it("should return result for Diophantine equation when one exist", () => { 11 | expect(diophantineEquation(10, 6, 14)).toEqual([-7, 14]); 12 | }); 13 | it("should return result for Diophantine equation when result is negative", () => { 14 | expect(diophantineEquation(391, 299, -69)).toEqual([9, -12]); 15 | }); 16 | }); -------------------------------------------------------------------------------- /tests/Trie.ts: -------------------------------------------------------------------------------- 1 | import {Trie} from "../ts/Trie"; 2 | 3 | describe("Trie", function () { 4 | it("should be able to check if word exist", function () { 5 | const trie = new Trie(); 6 | expect(trie.contains("test")).toEqual(false); 7 | trie.insert("test"); 8 | expect(trie.contains("test")).toEqual(true); 9 | expect(trie.contains("test for non existing word")).toEqual(false); 10 | trie.insert("test with value"); 11 | expect(trie.contains("test with value")).toEqual(true); 12 | }); 13 | 14 | it("should be able to add word", function () { 15 | const trie = new Trie(); 16 | expect(trie.contains("test")).toEqual(false); 17 | trie.insert("test"); 18 | expect(trie.contains("test")).toEqual(true); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /tests/TypedQueueSpec.ts: -------------------------------------------------------------------------------- 1 | import {TypedQueue} from "../ts/TypedQueue"; 2 | describe('Queue', function () { 3 | describe('enqueue', function () { 4 | it('should add elements to the stack', function () { 5 | var q = new TypedQueue(new Uint8Array(3)); 6 | q.enqueue(1); 7 | expect(q.size()).toEqual(1); 8 | expect(q.peek()).toEqual(1); 9 | q.enqueue(2); 10 | expect(q.peek()).toEqual(1); 11 | expect(q.size()).toEqual(2); 12 | q.dequeue(); 13 | expect(q.peek()).toEqual(2); 14 | expect(q.size()).toEqual(1); 15 | q.enqueue(2); 16 | expect(q.size()).toEqual(2); 17 | q.dequeue(); 18 | expect(q.size()).toEqual(1); 19 | }); 20 | }); 21 | }); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # nyc test coverage 20 | .nyc_output 21 | 22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 23 | .grunt 24 | 25 | # node-waf configuration 26 | .lock-wscript 27 | 28 | # Compiled binary addons (http://nodejs.org/api/addons.html) 29 | build/Release 30 | 31 | # Dependency directories 32 | node_modules 33 | jspm_packages 34 | 35 | # Optional npm cache directory 36 | .npm 37 | 38 | # Optional REPL history 39 | .node_repl_history 40 | 41 | # Webstorm 42 | .idea 43 | dist/ 44 | typings/ 45 | -------------------------------------------------------------------------------- /ts/functions/diophantineEquation.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Solves diophantine equation with 2 parameters. 3 | * @param {number} a 4 | * @param {number} b 5 | * @param {number} c 6 | */ 7 | import { greatestCommonDivisor } from "./greatestCommonDivisor"; 8 | import { extendedEuclidesAlgorithm } from "./extendedEuclidesAlgorithm"; 9 | import { IExtendedEuclidesAlgorithmResult } from "../Interfaces/IExtendedEuclidesAlgorithmResult"; 10 | 11 | export function diophantineEquation(a: number, b: number, c: number): number[] { 12 | if (a + b === 0) { 13 | return null; 14 | } 15 | const gcd = greatestCommonDivisor(a, b); 16 | const ratio: number = c / gcd; 17 | if (!Number.isInteger(ratio)) { 18 | return null; 19 | } 20 | const extendedEuclid: IExtendedEuclidesAlgorithmResult = extendedEuclidesAlgorithm(a, b); 21 | return [ratio * extendedEuclid.x, ratio * extendedEuclid.y]; 22 | } -------------------------------------------------------------------------------- /ts/Sort/insertionSort.ts: -------------------------------------------------------------------------------- 1 | import SortingComparator from "../CustomTypes/SortingComparator"; 2 | import {Utils} from "../Utils"; 3 | function insertionSort (array: T[], comparator: SortingComparator = Utils.gt, startIndex: number = 0, endIndex: number = -1) { 4 | if (!Array.isArray(array)) { 5 | return array; 6 | } 7 | 8 | if (endIndex === -1) { 9 | endIndex = array.length; 10 | } 11 | 12 | if ((endIndex - startIndex) < 2) { 13 | return array; 14 | } 15 | for (let j = startIndex + 1, length = endIndex; j < length; j++) { 16 | let key = array[j]; 17 | let index = j - 1; 18 | while (index >= startIndex && comparator(array[index], key) ) { 19 | array[index + 1] = array[index]; 20 | index--; 21 | } 22 | array[index + 1] = key; 23 | } 24 | 25 | return array; 26 | } 27 | 28 | export default insertionSort; -------------------------------------------------------------------------------- /ts/functions/greatestCommonDivisor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Euclid's algorithm for finding the greatest common divisor of two numbers greater than 0 3 | * @param {number} a greater than 0 4 | * @param {number} b greater than 0 5 | */ 6 | export function greatestCommonDivisor(a: number, b: number): number { 7 | if (!Number.isInteger(a) || !Number.isInteger(b)) { 8 | return NaN; 9 | } 10 | if (a === 0 && b === 0) { 11 | return NaN; 12 | } 13 | if (a < 0) { 14 | a = Math.abs(a); 15 | } 16 | if (b < 0) { 17 | b = Math.abs(b); 18 | } 19 | if (a === b) { 20 | return a; 21 | } else if (a < b) { 22 | return greatestCommonDivisorInternal(b, a); 23 | } else { 24 | return greatestCommonDivisorInternal(a, b); 25 | } 26 | } 27 | 28 | function greatestCommonDivisorInternal(a: number, b: number): number { 29 | return b > 0 ? greatestCommonDivisor(b , a % b) : a; 30 | } -------------------------------------------------------------------------------- /tests/FindMaximumSubarraySpec.ts: -------------------------------------------------------------------------------- 1 | import FindMaximumSubarray from "../ts/FindMaximumSubarray"; 2 | describe(`Find maximum subarray`, function () { 3 | it(`Find maximum subarray`, function () { 4 | let [leftIndex, rightIndex, sum] = FindMaximumSubarray([-1, 3, 2, 5, -1]); 5 | expect(leftIndex).toEqual(1); 6 | expect(rightIndex).toEqual(3); 7 | expect(sum).toEqual(10); 8 | [leftIndex, rightIndex, sum] = FindMaximumSubarray([-1, 1]); 9 | expect(leftIndex).toEqual(1); 10 | expect(rightIndex).toEqual(1); 11 | expect(sum).toEqual(1); 12 | [leftIndex, rightIndex, sum] = FindMaximumSubarray([-1, 1, -1]); 13 | expect(leftIndex).toEqual(1); 14 | expect(rightIndex).toEqual(1); 15 | expect(sum).toEqual(1); 16 | [leftIndex, rightIndex, sum] = FindMaximumSubarray([-1]); 17 | expect(leftIndex).toEqual(0); 18 | expect(rightIndex).toEqual(0); 19 | expect(sum).toEqual(0); 20 | }); 21 | }); -------------------------------------------------------------------------------- /tests/UtilsSpec.ts: -------------------------------------------------------------------------------- 1 | import {Utils} from "../ts/Utils"; 2 | describe("Utils", function () { 3 | describe("minIndex", function () { 4 | it("Should find the lowest index in part of array", function () { 5 | const ar = [3, 4, 5, 1, 2, 3, 1, 2]; 6 | expect(Utils.minIndex(ar)).toEqual(3); 7 | expect(Utils.minIndex(ar, 0, 4)).toEqual(3); 8 | expect(Utils.minIndex(ar, 4)).toEqual(6); 9 | expect(Utils.minIndex([1, 4], 0, 2, Utils.gt)).toEqual(0); 10 | expect(Utils.minIndex([1, 4], 1, 2, Utils.gt)).toEqual(1); 11 | }); 12 | }); 13 | 14 | describe("swapValues", function () { 15 | it("Should swap elements in array", function () { 16 | const ar = [3, 4, 5, 1, 2, 3, 1, 2]; 17 | Utils.swapValues(ar, 0, 1); 18 | expect(ar).toEqual([4, 3, 5, 1, 2, 3, 1, 2]); 19 | Utils.swapValues(ar, 3, 5); 20 | expect(ar).toEqual([4, 3, 5, 3, 2, 1, 1, 2]); 21 | }); 22 | }); 23 | }); -------------------------------------------------------------------------------- /tests/Sort/mergeSortSpec.ts: -------------------------------------------------------------------------------- 1 | import mergeSort from "../../ts/Sort/mergeSort"; 2 | describe("Merge sort", function () { 3 | it("should sort", function () { 4 | expect(mergeSort([1, 4, 2, 3])).toEqual([1, 2, 3, 4]); 5 | expect(mergeSort([11, 4, 2, 3])).toEqual([2, 3, 4, 11]); 6 | expect(mergeSort([11, 4, 2])).toEqual([2, 4, 11]); 7 | expect(mergeSort([11, 4, 2, 3, 5])).toEqual([2, 3, 4, 5, 11]); 8 | expect(mergeSort([5, 2, 4, 6, 1, 3])).toEqual([1, 2, 3, 4, 5, 6]); 9 | }); 10 | 11 | it("should return empty array if empty or one element array is passed", function () { 12 | expect(mergeSort([])).toEqual([]); 13 | expect(mergeSort([1])).toEqual([1]); 14 | }); 15 | 16 | it("should reverse sort", function () { 17 | expect(mergeSort([1, 4, 2, 3], true)).toEqual([4, 3, 2, 1]); 18 | expect(mergeSort([11, 4, 2, 3], true)).toEqual([11, 4, 3, 2]); 19 | expect(mergeSort([5, 2, 4, 6, 1, 3], true)).toEqual([6, 5, 4, 3, 2, 1]); 20 | }); 21 | }); -------------------------------------------------------------------------------- /ts/Stack.ts: -------------------------------------------------------------------------------- 1 | export class Stack { 2 | protected stack:T[] = []; 3 | 4 | public clear ():void { 5 | this.stack.length = 0; 6 | } 7 | 8 | public isEmpty ():boolean { 9 | return this.stack.length === 0; 10 | } 11 | 12 | public search (element:T):number { 13 | const index = this.stack.lastIndexOf(element); 14 | return (index < 0) ? -1 : this.stack.length - 1 - index; 15 | } 16 | 17 | public peek ():T { 18 | if (this.isEmpty()) { 19 | throw new Error('The stack is empty'); 20 | } 21 | return this.stack[this.stack.length - 1]; 22 | } 23 | 24 | public pop ():T { 25 | if (this.isEmpty()) { 26 | throw new Error('The stack is empty'); 27 | } 28 | return this.stack.pop(); 29 | } 30 | 31 | public push (element:T):Stack { 32 | this.stack.push(element); 33 | return this; 34 | } 35 | 36 | public size ():number { 37 | return this.stack.length; 38 | } 39 | } -------------------------------------------------------------------------------- /tests/functions/leastCommonMultipleSpec.ts: -------------------------------------------------------------------------------- 1 | import { leastCommonMultiple } from "../../ts/functions/leastCommonMultiple"; 2 | 3 | describe('leastCommonMultiple', function () { 4 | it('should calculate the least common multiple of two co-prime numbers', function () { 5 | expect(leastCommonMultiple(3, 5)).toEqual(15); 6 | }); 7 | it('should return 0 if both numbers are 0', function () { 8 | expect(leastCommonMultiple(0, 0)).toEqual(0); 9 | }); 10 | it('should return first number if second is 0', function () { 11 | expect(leastCommonMultiple(3, 0)).toEqual(3); 12 | }); 13 | it('should return the second if it is divisible by first', function () { 14 | expect(leastCommonMultiple(15, 30)).toEqual(30); 15 | }); 16 | it('should return the first if it is divisible by second', function () { 17 | expect(leastCommonMultiple(6, 3)).toEqual(6); 18 | }); 19 | it('should work for negative numbers too', function () { 20 | expect(leastCommonMultiple(-5, 3)).toEqual(15); 21 | }); 22 | }); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 sularome 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ts/Trie.ts: -------------------------------------------------------------------------------- 1 | // TODO: getWordsFromPrefix 2 | 3 | export class Trie { 4 | public static KEY: string = "id"; 5 | public root: any = {}; 6 | 7 | public insert(word: string): boolean { 8 | const wordwSize = word.length; 9 | let i: number = 0; 10 | let level = this.root; 11 | while (i < wordwSize) { 12 | if (!level[word[i]]) { 13 | level[word[i]] = {}; 14 | } 15 | level = level[word[i]]; 16 | i++; 17 | if (i === wordwSize) { 18 | level[Trie.KEY] = true; 19 | } 20 | } 21 | return true; 22 | } 23 | 24 | public contains(word: string) { 25 | const wordwSize = word.length; 26 | let i: number = 0; 27 | let level = this.root; 28 | while (i < wordwSize) { 29 | if (!level[word[i]]) { 30 | return false; 31 | } 32 | level = level[word[i]]; 33 | i++; 34 | if (i === wordwSize && level[Trie.KEY]) { 35 | return true; 36 | } 37 | } 38 | return false; 39 | } 40 | } 41 | 42 | 43 | -------------------------------------------------------------------------------- /ts/functions/diophantineEquationSolver.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Should return a function that when provided with index will return solution for diophantine equation 3 | * @param {number} a 4 | * @param {number} b 5 | * @param {number} c 6 | */ 7 | import { greatestCommonDivisor } from "./greatestCommonDivisor"; 8 | import { extendedEuclidesAlgorithm } from "./extendedEuclidesAlgorithm"; 9 | import { IExtendedEuclidesAlgorithmResult } from "../Interfaces/IExtendedEuclidesAlgorithmResult"; 10 | 11 | export function diophantineEquationSolver(a: number, b: number, c: number): (index: number) => number[] { 12 | if (a + b === 0) { 13 | return null; 14 | } 15 | const gcd = greatestCommonDivisor(a, b); 16 | const ratio: number = c / gcd; 17 | if (!Number.isInteger(ratio)) { 18 | return null; 19 | } 20 | const extendedEuclid: IExtendedEuclidesAlgorithmResult = extendedEuclidesAlgorithm(a, b); 21 | const x0: number = ratio * extendedEuclid.x; 22 | const y0: number = ratio * extendedEuclid.y; 23 | const p: number = a / gcd; 24 | const q: number = b / gcd; 25 | return (index: number) => { 26 | return [x0 + (q * index), y0 - (p * index)]; 27 | } 28 | } -------------------------------------------------------------------------------- /tests/TrieWithValues.ts: -------------------------------------------------------------------------------- 1 | import {TrieWithValue} from "../ts/TrieWithValue"; 2 | 3 | describe("Trie", function () { 4 | it("should be able to check if word exist", function () { 5 | const trie = new TrieWithValue(); 6 | expect(trie.contains("test")).toEqual(false); 7 | trie.insert("test"); 8 | expect(trie.contains("test")).toEqual(true); 9 | expect(trie.contains("test for non existing word")).toEqual(false); 10 | trie.insert("test with value", 20); 11 | expect(trie.contains("test with value")).toEqual(true); 12 | }); 13 | 14 | it("should be able to add word", function () { 15 | const trie = new TrieWithValue(); 16 | trie.insert("test"); 17 | expect(trie.contains("test")).toEqual(true); 18 | }); 19 | 20 | it("should be able to add word with value", function () { 21 | const trie = new TrieWithValue(); 22 | const value: number = 23; 23 | trie.insert("test", value); 24 | trie.insert("tester", value + 3); 25 | expect(trie.contains("test")).toEqual(true); 26 | expect(trie.getValue("test")).toEqual(value); 27 | expect(trie.getValue("tester")).toEqual(value + 3); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /ts/BinaryTree/BinaryTreeNode.ts: -------------------------------------------------------------------------------- 1 | import {IBinaryTreeNode} from "../Interfaces/IBinaryTreeNode"; 2 | export class BinaryTreeNode implements IBinaryTreeNode{ 3 | public left:BinaryTreeNode = null; 4 | public right:BinaryTreeNode = null; 5 | public value:T; 6 | public parent:BinaryTreeNode = null; 7 | 8 | constructor(value:T, parent: BinaryTreeNode) { 9 | this.value = value; 10 | this.parent = parent; 11 | } 12 | 13 | public hasLeftChild ():boolean { 14 | return this.left !== null; 15 | } 16 | 17 | public hasRightChild ():boolean { 18 | return this.right !== null; 19 | } 20 | 21 | public isLeftChild ():boolean { 22 | return this.parent && this.parent.left === this; 23 | } 24 | 25 | public isRightChild ():boolean { 26 | return this.parent && this.parent.right === this; 27 | } 28 | 29 | public isRoot ():boolean { 30 | return this.parent === null; 31 | } 32 | 33 | public min ():BinaryTreeNode { 34 | if (this.left !== null) { 35 | return this.left.min(); 36 | } 37 | return this; 38 | } 39 | 40 | public max ():BinaryTreeNode { 41 | if (this.right !== null) { 42 | return this.right.max(); 43 | } 44 | return this; 45 | } 46 | } -------------------------------------------------------------------------------- /ts/index.ts: -------------------------------------------------------------------------------- 1 | import {BitArray} from "./BitArray"; 2 | import {BitMatrix} from "./BitMatrix"; 3 | import FindMaximumSubarray from "./FindMaximumSubarray"; 4 | import {HashTable} from "./HashTable"; 5 | import {LinkedList} from "./LinkedList"; 6 | import {ObjectArray} from "./ObjectArray"; 7 | import {Queue} from "./Queue"; 8 | import {RadixTree} from "./RadixTree"; 9 | import {RadixTreeNode} from "./RadixTreeNode"; 10 | import {Stack} from "./Stack"; 11 | import {Trie} from "./Trie"; 12 | import {TrieWithValue} from "./TrieWithValue"; 13 | import {TypedQueue} from "./TypedQueue"; 14 | import {TypedStack} from "./TypedStack"; 15 | import {Utils} from "./Utils"; 16 | import binarySum from "./binarySum"; 17 | import linearSearch from "./linearSearch"; 18 | import BinaryTree from "./BinaryTree/index"; 19 | import functions from "./functions/index"; 20 | import Graph from "./Graph/index"; 21 | import Sort from "./Sort/index"; 22 | 23 | export { 24 | BitArray, 25 | BitMatrix, 26 | FindMaximumSubarray, 27 | HashTable, 28 | LinkedList, 29 | ObjectArray, 30 | Queue, 31 | RadixTree, 32 | RadixTreeNode, 33 | Stack, 34 | Trie, 35 | TrieWithValue, 36 | TypedQueue, 37 | TypedStack, 38 | Utils, 39 | binarySum, 40 | linearSearch, 41 | BinaryTree, 42 | functions, 43 | Graph, 44 | Sort 45 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript-algorithms-and-datastructures", 3 | "version": "0.0.7", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "files": [ 8 | "dist/**/*" 9 | ], 10 | "keywords": [ 11 | "TypeScript", 12 | "Data Structure", 13 | "Algorithm", 14 | "BitArray", 15 | "Trie", 16 | "RadixTrie" 17 | ], 18 | "scripts": { 19 | "ci": "npm run test", 20 | "clear": "npx rimraf dist", 21 | "test": "npx jest --config jestconfig.json", 22 | "build": "npm run clear && npx tsc", 23 | "lint": "npx tslint", 24 | "prepare": "npm run build", 25 | "prepublishOnly": "npm test", 26 | "postversion": "git push && git push --tags" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "https://github.com/sularome/TypeScript-Algorithms-and-Data-Structures.git" 31 | }, 32 | "author": "Yordan Stoev", 33 | "license": "ISC", 34 | "bugs": { 35 | "url": "https://github.com/sularome/TypeScript-Algorithms-and-Data-Structures/issues" 36 | }, 37 | "homepage": "https://github.com/sularome/TypeScript-Algorithms-and-Data-Structures", 38 | "devDependencies": { 39 | "@types/jest": "^25.2.1", 40 | "jest": "^25.4.0", 41 | "rimraf": "^3.0.2", 42 | "ts-jest": "^25.4.0", 43 | "tslint": "^6.1.1", 44 | "typescript": "3.8.3" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/Sort/gnomeSortSpec.ts: -------------------------------------------------------------------------------- 1 | import gnomeSort from "../../ts/Sort/gnomeSort"; 2 | describe("Gnome/Simple sort", function () { 3 | it("should sort", function () { 4 | expect(gnomeSort([1, 4, 2, 3])).toEqual([1, 2, 3, 4]); 5 | expect(gnomeSort([11, 4, 2, 3])).toEqual([2, 3, 4, 11]); 6 | expect(gnomeSort([11, 4, 2])).toEqual([2, 4, 11]); 7 | expect(gnomeSort([11, 4, 2, 3, 5])).toEqual([2, 3, 4, 5, 11]); 8 | expect(gnomeSort([5, 2, 4, 6, 1, 3])).toEqual([1, 2, 3, 4, 5, 6]); 9 | }); 10 | 11 | it("should return empty array if empty or one element array is passed", function () { 12 | expect(gnomeSort([])).toEqual([]); 13 | expect(gnomeSort([1])).toEqual([1]); 14 | }); 15 | 16 | it("should reverse sort", function () { 17 | expect(gnomeSort([1, 4, 2, 3], (a, b) => a < b)).toEqual([4, 3, 2, 1]); 18 | expect(gnomeSort([11, 4, 2, 3], (a, b) => a < b)).toEqual([11, 4, 3, 2]); 19 | expect(gnomeSort([5, 2, 4, 6, 1, 3], (a, b) => a < b)).toEqual([6, 5, 4, 3, 2, 1]); 20 | }); 21 | 22 | it("should work for any kind of objects", function () { 23 | expect(gnomeSort(["a", "b", "a", "C", "d"], (a, b) => a > b)).toEqual(["C", "a", "a", "b", "d"]); 24 | expect(gnomeSort([{id: 5}, {id: 4}, {id: 3}, {id: 2}], (a, b) => a.id > b.id)).toEqual([{id: 2}, {id: 3}, {id: 4}, {id: 5}]); 25 | }); 26 | }); -------------------------------------------------------------------------------- /tests/ObjectArraySpec.ts: -------------------------------------------------------------------------------- 1 | import {ObjectArray} from "../ts/ObjectArray"; 2 | describe('ObjectArray', function () { 3 | 4 | it('should be able to add items', function () { 5 | var list = new ObjectArray(200, [{name:'prev', type:Float64Array}, {name:'next', type:Float64Array}, {name:'value', type:Float64Array}]); 6 | var index = list.add([1,2,3]); 7 | expect(list.get(index)).toEqual([1,2,3]); 8 | index = list.add([2,3,4]); 9 | expect(list.get(index)).toEqual([2,3,4]); 10 | index = list.add([3,4,5]); 11 | expect(list.get(index)).toEqual([3,4,5]); 12 | }); 13 | 14 | it('should throw an error if we exceed array max size', function () { 15 | var list = new ObjectArray(2, [{name:'prev', type:Float64Array}, {name:'next', type:Float64Array}, {name:'value', type:Float64Array}]); 16 | list.add([1,2,3]); 17 | list.add([1,2,3]); 18 | expect(list.add.bind(list)).toThrowError(RangeError); 19 | }); 20 | 21 | it('should be able to remove items', function () { 22 | var list = new ObjectArray(200, [{name:'prev', type:Float64Array}, {name:'next', type:Float64Array}, {name:'value', type:Float64Array}]); 23 | var index = list.add([1,2,3]); 24 | expect(list.get(index)).toEqual([1,2,3]); 25 | list.remove(index); 26 | expect(list.get(index)).toEqual(null); 27 | }); 28 | }); -------------------------------------------------------------------------------- /tests/Sort/bubbleSortSpec.ts: -------------------------------------------------------------------------------- 1 | import bubbleSort from "../../ts/Sort/bubbleSort"; 2 | describe("Bubble sort", function () { 3 | it("should sort", function () { 4 | expect(bubbleSort([1, 4, 2, 3])).toEqual([1, 2, 3, 4]); 5 | expect(bubbleSort([11, 4, 2, 3])).toEqual([2, 3, 4, 11]); 6 | expect(bubbleSort([11, 4, 2])).toEqual([2, 4, 11]); 7 | expect(bubbleSort([11, 4, 2, 3, 5])).toEqual([2, 3, 4, 5, 11]); 8 | expect(bubbleSort([5, 2, 4, 6, 1, 3])).toEqual([1, 2, 3, 4, 5, 6]); 9 | }); 10 | 11 | it("should return empty array if empty or one element array is passed", function () { 12 | expect(bubbleSort([])).toEqual([]); 13 | expect(bubbleSort([1])).toEqual([1]); 14 | }); 15 | 16 | it("should reverse sort", function () { 17 | expect(bubbleSort([1, 4, 2, 3], (a, b) => a < b)).toEqual([4, 3, 2, 1]); 18 | expect(bubbleSort([11, 4, 2, 3], (a, b) => a < b)).toEqual([11, 4, 3, 2]); 19 | expect(bubbleSort([5, 2, 4, 6, 1, 3], (a, b) => a < b)).toEqual([6, 5, 4, 3, 2, 1]); 20 | }); 21 | 22 | it("should work for any kind of objects", function () { 23 | expect(bubbleSort(["a", "b", "a", "C", "d"], (a, b) => a > b)).toEqual(["C", "a", "a", "b", "d"]); 24 | expect(bubbleSort([{id: 5}, {id: 4}, {id: 3}, {id: 2}], (a, b) => a.id > b.id)).toEqual([{id: 2}, {id: 3}, {id: 4}, {id: 5}]); 25 | }); 26 | }); -------------------------------------------------------------------------------- /ts/Sort/mergeSort.ts: -------------------------------------------------------------------------------- 1 | function mergeSort (array: number[], reverse = false, startIndex?: number, endIndex?: number) { 2 | if (!Array.isArray(array)) { 3 | return array; 4 | } 5 | startIndex = startIndex === void 0 ? 0 : startIndex; 6 | endIndex = endIndex === void 0 ? array.length : endIndex; 7 | if (array.length < 2 || endIndex - startIndex < 2) { 8 | return array; 9 | } 10 | const middleIndex: number = startIndex + Math.floor((endIndex - startIndex) / 2); 11 | 12 | mergeSort(array, reverse, startIndex, middleIndex); 13 | mergeSort(array, reverse, middleIndex, endIndex); 14 | merge(array, reverse, startIndex, middleIndex, endIndex); 15 | return array; 16 | } 17 | 18 | function merge(array: number[], reverse: boolean, startIndex: number, middleIndex: number, endIndex: number) { 19 | const left: number[] = array.slice(startIndex, middleIndex); 20 | const right: number[] = array.slice(middleIndex, endIndex); 21 | left.push(reverse ? -Infinity : Infinity); 22 | right.push(reverse ? -Infinity : Infinity); 23 | let i: number = 0; 24 | let j: number = 0; 25 | for (let k: number = startIndex; k < endIndex; k++) { 26 | if ((reverse && left[i] >= right[j] ) || (!reverse && left[i] <= right[j])) { 27 | array[k] = left[i]; 28 | i++; 29 | } else { 30 | array[k] = right[j]; 31 | j++; 32 | } 33 | } 34 | } 35 | 36 | export default mergeSort; -------------------------------------------------------------------------------- /ts/TypedQueue.ts: -------------------------------------------------------------------------------- 1 | import {ITypedArray} from "./ITypedArray"; 2 | export class TypedQueue { 3 | protected queue: ITypedArray; 4 | protected head:number = 0; 5 | protected tail:number = 0; 6 | protected memoryLength:number = 0; 7 | 8 | constructor (queue:ITypedArray) { 9 | this.queue = queue; 10 | this.memoryLength = queue.length; 11 | } 12 | 13 | public enqueue (element:number) { 14 | if (this.size() === this.memoryLength - 1) { 15 | throw new RangeError('The queue is full'); 16 | } 17 | this.queue[this.tail] = element; 18 | this.tail++; 19 | if (this.tail === this.memoryLength) { 20 | this.tail = 0; 21 | } 22 | } 23 | 24 | public dequeue ():number { 25 | if (this.isEmpty()) { 26 | throw new Error('The queue is empty'); 27 | } 28 | var element = this.queue[this.head]; 29 | this.head++; 30 | if (this.head === this.memoryLength) { 31 | this.head = 0; 32 | } 33 | return element; 34 | } 35 | 36 | public isEmpty () { 37 | return this.tail === this.head; 38 | } 39 | 40 | public peek ():number { 41 | if (this.isEmpty()) { 42 | throw new Error('The stack is empty'); 43 | } 44 | return this.queue[this.head]; 45 | } 46 | 47 | public size () { 48 | return (this.tail < this.head) ? this.tail + this.memoryLength - this.head : this.tail - this.head; 49 | } 50 | } -------------------------------------------------------------------------------- /ts/TypedStack.ts: -------------------------------------------------------------------------------- 1 | import {ITypedArray} from './ITypedArray'; 2 | export class TypedStack { 3 | 4 | protected stack:ITypedArray; 5 | protected top:number = -1; 6 | protected max:number = -1; 7 | 8 | constructor (TypedArray: {new(size:number): ITypedArray}, size:number) { 9 | this.stack = new TypedArray(size); 10 | this.max = size; 11 | } 12 | 13 | public clear ():void { 14 | this.top = -1; 15 | } 16 | 17 | public isEmpty ():boolean { 18 | return this.top === -1; 19 | } 20 | 21 | public search (element:number):number { 22 | const index = this.stack.lastIndexOf(element); 23 | return (index < 0) ? -1 : this.top - index; 24 | } 25 | 26 | public peek ():number { 27 | if (this.isEmpty()) { 28 | throw new Error('The stack is empty'); 29 | } 30 | return this.stack[this.top]; 31 | } 32 | 33 | public pop ():number { 34 | if (this.top === -1) { 35 | throw new RangeError('The stack is empty.'); 36 | } 37 | var index = this.top; 38 | this.top--; 39 | return this.stack[index]; 40 | } 41 | 42 | public push (element:number) { 43 | if (this.top >= this.max) { 44 | throw new RangeError('You exceeded the buffer size.'); 45 | } 46 | this.top++; 47 | this.stack[this.top] = element; 48 | return this; 49 | } 50 | 51 | public size ():number { 52 | return this.top + 1; 53 | } 54 | } -------------------------------------------------------------------------------- /tests/Sort/quickSortSpec.ts: -------------------------------------------------------------------------------- 1 | import quickSort from "../../ts/Sort/quickSort"; 2 | import {Utils} from "../../ts/Utils"; 3 | describe("quicksort sort", function () { 4 | it("should sort", function () { 5 | expect(quickSort([4, 1])).toEqual([1, 4]); 6 | expect(quickSort([4, 1, 3])).toEqual([1, 3, 4]); 7 | expect(quickSort([1, 4, 2, 3])).toEqual([1, 2, 3, 4]); 8 | expect(quickSort([11, 4, 2, 3])).toEqual([2, 3, 4, 11]); 9 | expect(quickSort([11, 4, 2])).toEqual([2, 4, 11]); 10 | expect(quickSort([11, 4, 2, 3, 5])).toEqual([2, 3, 4, 5, 11]); 11 | expect(quickSort([5, 2, 4, 6, 1, 3])).toEqual([1, 2, 3, 4, 5, 6]); 12 | }); 13 | 14 | it("should return empty array if empty or one element array is passed", function () { 15 | expect(quickSort([], Utils.gt)).toEqual([]); 16 | expect(quickSort([1], Utils.gt)).toEqual([1]); 17 | }); 18 | 19 | it("should reverse sort", function () { 20 | expect(quickSort([1, 4, 2, 3], Utils.lt)).toEqual([4, 3, 2, 1]); 21 | expect(quickSort([11, 4, 2, 3], Utils.lt)).toEqual([11, 4, 3, 2]); 22 | expect(quickSort([5, 2, 4, 6, 1, 3], Utils.lt)).toEqual([6, 5, 4, 3, 2, 1]); 23 | }); 24 | 25 | it("should work for any kind of objects", function () { 26 | expect(quickSort(["a", "b", "a", "C", "d"], Utils.gt)).toEqual(["C", "a", "a", "b", "d"]); 27 | expect(quickSort([{id: 5}, {id: 4}, {id: 3}, {id: 2}], (a, b) => a.id > b.id)).toEqual([{id: 2}, {id: 3}, {id: 4}, {id: 5}]); 28 | }); 29 | }); -------------------------------------------------------------------------------- /tests/Sort/insertionSortSpec.ts: -------------------------------------------------------------------------------- 1 | import insertionSort from "../../ts/Sort/insertionSort"; 2 | import {Utils} from "../../ts/Utils"; 3 | describe("Insertion sort", function () { 4 | it("should sort", function () { 5 | expect(insertionSort([1, 4, 2, 3])).toEqual([1, 2, 3, 4]); 6 | expect(insertionSort([11, 4, 2, 3])).toEqual([2, 3, 4, 11]); 7 | expect(insertionSort([11, 4, 2])).toEqual([2, 4, 11]); 8 | expect(insertionSort([11, 4, 2, 3, 5])).toEqual([2, 3, 4, 5, 11]); 9 | expect(insertionSort([5, 2, 4, 6, 1, 3])).toEqual([1, 2, 3, 4, 5, 6]); 10 | }); 11 | 12 | it("should return empty array if empty or one element array is passed", function () { 13 | expect(insertionSort([])).toEqual([]); 14 | expect(insertionSort([1])).toEqual([1]); 15 | }); 16 | 17 | it("should reverse sort", function () { 18 | expect(insertionSort([1, 4, 2, 3], Utils.lt)).toEqual([4, 3, 2, 1]); 19 | expect(insertionSort([11, 4, 2, 3], Utils.lt)).toEqual([11, 4, 3, 2]); 20 | expect(insertionSort([5, 2, 4, 6, 1, 3], Utils.lt)).toEqual([6, 5, 4, 3, 2, 1]); 21 | }); 22 | 23 | it("should sort part of array", function () { 24 | expect(insertionSort([1, 4, 2, 3], Utils.gt, 1, 4)).toEqual([1, 2, 3, 4]); 25 | expect(insertionSort([1, 4, 2, 3], Utils.gt, 1, 3)).toEqual([1, 2, 4, 3]); 26 | expect(insertionSort([5, 2, 4, 6, 1, 3], Utils.gt, 1, 3)).toEqual([5, 2, 4, 6, 1, 3]); 27 | expect(insertionSort([5, 2, 4, 6, 1, 3], Utils.gt, 2, 5)).toEqual([5, 2, 1, 4, 6, 3]); 28 | }); 29 | }); -------------------------------------------------------------------------------- /tests/Sort/selectionSortSpec.ts: -------------------------------------------------------------------------------- 1 | import selectionSort from "../../ts/Sort/selectionSort"; 2 | describe("selection sort sort", function () { 3 | it("should sort", function () { 4 | expect(selectionSort([4, 1])).toEqual([1, 4]); 5 | expect(selectionSort([4, 1, 3])).toEqual([1, 3, 4]); 6 | expect(selectionSort([1, 4, 2, 3])).toEqual([1, 2, 3, 4]); 7 | expect(selectionSort([11, 4, 2, 3])).toEqual([2, 3, 4, 11]); 8 | expect(selectionSort([11, 4, 2])).toEqual([2, 4, 11]); 9 | expect(selectionSort([11, 4, 2, 3, 5])).toEqual([2, 3, 4, 5, 11]); 10 | expect(selectionSort([5, 2, 4, 6, 1, 3])).toEqual([1, 2, 3, 4, 5, 6]); 11 | }); 12 | 13 | it("should return empty array if empty or one element array is passed", function () { 14 | expect(selectionSort([])).toEqual([]); 15 | expect(selectionSort([1])).toEqual([1]); 16 | }); 17 | 18 | it("should reverse sort", function () { 19 | expect(selectionSort([1, 4, 2, 3], (a, b) => a < b)).toEqual([4, 3, 2, 1]); 20 | expect(selectionSort([11, 4, 2, 3], (a, b) => a < b)).toEqual([11, 4, 3, 2]); 21 | expect(selectionSort([5, 2, 4, 6, 1, 3], (a, b) => a < b)).toEqual([6, 5, 4, 3, 2, 1]); 22 | }); 23 | 24 | it("should work for any kind of objects", function () { 25 | expect(selectionSort(["a", "b", "a", "C", "d"], (a, b) => a > b)).toEqual(["C", "a", "a", "b", "d"]); 26 | expect(selectionSort([{id: 5}, {id: 4}, {id: 3}, {id: 2}], (a, b) => a.id > b.id)).toEqual([{id: 2}, {id: 3}, {id: 4}, {id: 5}]); 27 | }); 28 | }); -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": ["ts/", "tests/"], 3 | "rules": { 4 | "class-name": true, 5 | "comment-format": [true, "check-space"], 6 | "indent": [true, "spaces"], 7 | "no-duplicate-variable": true, 8 | "no-eval": true, 9 | "no-internal-module": true, 10 | "no-trailing-whitespace": true, 11 | "no-var-keyword": true, 12 | "one-line": [true, "check-open-brace", "check-whitespace"], 13 | "quotemark": [true, "double"], 14 | "semicolon": false, 15 | "triple-equals": [true, "allow-null-check"], 16 | "typedef-whitespace": [true, { 17 | "call-signature": "nospace", 18 | "index-signature": "nospace", 19 | "parameter": "nospace", 20 | "property-declaration": "nospace", 21 | "variable-declaration": "nospace" 22 | }], 23 | "variable-name": [true, "ban-keywords"], 24 | "whitespace": [true, 25 | "check-branch", 26 | "check-decl", 27 | "check-operator", 28 | "check-separator", 29 | "check-type" 30 | ] 31 | }, 32 | "jsRules": { 33 | "indent": [true, "spaces"], 34 | "no-duplicate-variable": true, 35 | "no-eval": true, 36 | "no-trailing-whitespace": true, 37 | "one-line": [true, "check-open-brace", "check-whitespace"], 38 | "quotemark": [true, "double"], 39 | "semicolon": false, 40 | "triple-equals": [true, "allow-null-check"], 41 | "variable-name": [true, "ban-keywords"], 42 | "whitespace": [true, 43 | "check-branch", 44 | "check-decl", 45 | "check-operator", 46 | "check-separator", 47 | "check-type" 48 | ] 49 | } 50 | } -------------------------------------------------------------------------------- /ts/Sort/quickSort.ts: -------------------------------------------------------------------------------- 1 | import SortingComparator from "../CustomTypes/SortingComparator"; 2 | import {Utils} from "../Utils"; 3 | import insertionSort from "./insertionSort"; 4 | function quickSort(array: T[], comparator: SortingComparator = Utils.gt, leftIndex: number = 0, rightIndex: number = -1): T[] { 5 | if (!Array.isArray(array) || array.length < 2) { 6 | return array; 7 | } 8 | 9 | if (rightIndex === -1) { 10 | rightIndex = array.length - 1; 11 | } 12 | if (leftIndex >= rightIndex) { 13 | return array; 14 | } 15 | 16 | if (leftIndex > rightIndex - 10) { 17 | return insertionSort(array, comparator, leftIndex, rightIndex + 1); 18 | } 19 | 20 | const pivot: number = partition(array, comparator, leftIndex, rightIndex); 21 | quickSort(array, comparator, leftIndex, pivot - 1); 22 | return quickSort(array, comparator, pivot + 1, rightIndex); 23 | } 24 | 25 | function partition (array: T[], comparator: SortingComparator, leftIndex: number, rightIndex: number) { 26 | const pivotIndex: number = leftIndex + Math.floor(Math.random() * (rightIndex - leftIndex)); 27 | Utils.swapValues(array, rightIndex, pivotIndex); 28 | const pivot: T = array[rightIndex]; 29 | let border: number = leftIndex - 1; 30 | for (let j = leftIndex; j < rightIndex; j++) { 31 | if (!comparator(array[j], pivot)) { 32 | border++; 33 | Utils.swapValues(array, j, border); 34 | } 35 | } 36 | Utils.swapValues(array, border + 1, rightIndex); 37 | return border + 1; 38 | } 39 | 40 | export default quickSort; -------------------------------------------------------------------------------- /ts/Utils.ts: -------------------------------------------------------------------------------- 1 | import SortingComparator from "./CustomTypes/SortingComparator"; 2 | export class Utils { 3 | public static range(start: number, end: number, step: number = 1): number[] { 4 | let i = start; 5 | let output: number[] = []; 6 | while (i < end) { 7 | output.push(i); 8 | i = i + step; 9 | } 10 | 11 | return output; 12 | } 13 | 14 | public static swapValues (array: T[], from: number, to: number) { 15 | let temp = array[from]; 16 | array[from] = array[to]; 17 | array[to] = temp; 18 | } 19 | 20 | public static lt (a: T, b: T): boolean { 21 | return a < b 22 | } 23 | 24 | public static gt (a: T, b: T): boolean { 25 | return a > b 26 | } 27 | 28 | public static minIndex (array: T[], from: number = 0, to: number = array.length, comparator: SortingComparator = Utils.gt) { 29 | const arraySize: number = to; 30 | let lowestIndex: number = from; 31 | for (let i = from; i < arraySize; i++) { 32 | if (comparator(array[lowestIndex], array[i])) { 33 | lowestIndex = i; 34 | } 35 | } 36 | return lowestIndex; 37 | } 38 | 39 | public static findIndexBy (array: T[], comparator: (el: T, index?: number, array?: T[]) => boolean) { 40 | let lowestIndex: number = -1; 41 | array.some((entry, index) => { 42 | if (comparator(entry, index, array)) { 43 | lowestIndex = index; 44 | return true; 45 | } 46 | return false; 47 | }); 48 | return lowestIndex; 49 | } 50 | } -------------------------------------------------------------------------------- /tests/functions/diophantineEquationSolverSpec.ts: -------------------------------------------------------------------------------- 1 | import { diophantineEquationSolver } from "../../ts/functions/diophantineEquationSolver"; 2 | 3 | describe("diophantineEquationSolver", () => { 4 | it("should return NaN if both parameters are 0", () => { 5 | expect(diophantineEquationSolver(0, 0, 0)).toBeNull(); 6 | }); 7 | it("should return NaN if gcd(a, b) foes not divide c", () => { 8 | expect(diophantineEquationSolver(15, 20, 3)).toBeNull(); 9 | }); 10 | it("should return result for Diophantine equation when one exist", () => { 11 | const solver = diophantineEquationSolver(10, 6, 14); 12 | expect(solver(0)).toEqual([-7, 14]); 13 | }); 14 | it("should return result for Diophantine equation when result is negative", () => { 15 | const solver = diophantineEquationSolver(391, 299, -69); 16 | expect(solver(0)).toEqual([9, -12]); 17 | }); 18 | it("should return proper solution for greater than zero index", () => { 19 | const solver = diophantineEquationSolver(98, 35, 14); 20 | expect(solver(1)).toEqual([3, -8]); 21 | }); 22 | it("should return proper solution for greater than zero index", () => { 23 | const solver = diophantineEquationSolver(3, 5, 22); 24 | expect(solver(0)).toEqual([44, -22]); 25 | }); 26 | it("should return proper solution for greater than zero index", () => { 27 | const solver = diophantineEquationSolver(3, 5, 22); 28 | expect(solver(1)).toEqual([49, -25]); 29 | }); 30 | it("should return proper solution for greater than one index", () => { 31 | const solver = diophantineEquationSolver(3, 5, 22); 32 | expect(solver(2)).toEqual([54, -28]); 33 | }); 34 | }); -------------------------------------------------------------------------------- /ts/TrieWithValue.ts: -------------------------------------------------------------------------------- 1 | // TODO: add delete and getWordsFromPrefix 2 | 3 | export class TrieWithValue { 4 | public static KEY: string = "id"; 5 | public root: any = {}; 6 | 7 | public insert(word: string, value: T = null): boolean { 8 | const wordwSize = word.length; 9 | let i: number = 0; 10 | let level = this.root; 11 | while (i < wordwSize) { 12 | if (!level[word[i]]) { 13 | level[word[i]] = {}; 14 | } 15 | level = level[word[i]]; 16 | i++; 17 | if (i === wordwSize) { 18 | level[TrieWithValue.KEY] = value; 19 | } 20 | } 21 | return true; 22 | } 23 | 24 | public contains(word: string) { 25 | const wordwSize = word.length; 26 | let i: number = 0; 27 | let level = this.root; 28 | while (i < wordwSize) { 29 | if (!level[word[i]]) { 30 | return false; 31 | } 32 | level = level[word[i]]; 33 | i++; 34 | if (i === wordwSize && level.hasOwnProperty(TrieWithValue.KEY)) { 35 | return true; 36 | } 37 | } 38 | return false; 39 | } 40 | 41 | public getValue = function(word: string) { 42 | const wordwSize = word.length; 43 | let i: number = 0; 44 | let level = this.root; 45 | while (i < wordwSize) { 46 | if (!level[word[i]]) { 47 | return void 0; 48 | } 49 | level = level[word[i]]; 50 | i++; 51 | if (i === wordwSize && level.hasOwnProperty(TrieWithValue.KEY)) { 52 | return level[TrieWithValue.KEY]; 53 | } 54 | } 55 | return void 0; 56 | } 57 | } 58 | 59 | 60 | -------------------------------------------------------------------------------- /ts/FindMaximumSubarray.ts: -------------------------------------------------------------------------------- 1 | function FindMaximumSubarray (array: number[], left: number = 0, right: number = array.length): number[] { 2 | if (left === right) { 3 | return [left, right, left > array.length ? -Infinity : array[left]]; 4 | } else { 5 | const middle = Math.floor((left + right) / 2); 6 | const [leftLow, leftHigh, leftSum] = FindMaximumSubarray(array, left, middle); 7 | const [rightLow, rightHigh, rightSum] = FindMaximumSubarray(array, middle + 1, right); 8 | const [crossLow, crossHigh, crossSum] = FindMaximumCrossingSubarray(array, left, middle, right); 9 | if (leftSum >= rightSum && leftSum >= crossSum) { 10 | return [leftLow, leftHigh, leftSum]; 11 | } 12 | 13 | if (rightSum >= leftSum && rightSum >= crossSum) { 14 | return [rightLow, rightHigh, rightSum]; 15 | } 16 | return [crossLow, crossHigh, crossSum]; 17 | } 18 | } 19 | 20 | function FindMaximumCrossingSubarray (array: number[], left: number, middle: number, right: number) { 21 | let leftSum: number = -Infinity; 22 | let rightSum: number = -Infinity; 23 | let sum: number = 0; 24 | let maxLeft: number = middle; 25 | let maxRight: number = Math.min(middle + 1, array.length - 1); 26 | for (let i = middle; i >= left; i--) { 27 | sum = sum + array[i]; 28 | if (sum > leftSum) { 29 | leftSum = sum; 30 | maxLeft = i; 31 | } 32 | } 33 | 34 | sum = 0; 35 | for (let i = middle + 1; i < right; i++) { 36 | sum = sum + array[i]; 37 | if (sum > rightSum) { 38 | rightSum = sum; 39 | maxRight = i; 40 | } 41 | } 42 | 43 | return [maxLeft, maxRight, Math.max(rightSum, 0) + Math.max(leftSum, 0)]; 44 | } 45 | 46 | export default FindMaximumSubarray; -------------------------------------------------------------------------------- /tests/functions/greatestCommonDivisorSpec.ts: -------------------------------------------------------------------------------- 1 | import { greatestCommonDivisor } from "../../ts/functions/greatestCommonDivisor"; 2 | 3 | describe("greatestCommonDivisor", () => { 4 | it("should be able to find common divisor for the same number", () => { 5 | expect(greatestCommonDivisor(17, 17)).toEqual(17); 6 | }); 7 | it("should return 1 when no common divisor", () => { 8 | expect(greatestCommonDivisor(17, 6)).toEqual(1); 9 | }); 10 | it("should return first if it is multiple of second", () => { 11 | expect(greatestCommonDivisor(15, 35)).toEqual(5); 12 | }); 13 | it("should work if first parameter is negative numbers", () => { 14 | expect(greatestCommonDivisor(-15, 35)).toEqual(5); 15 | }); 16 | it("should work if second parameter is less than 0", () => { 17 | expect(greatestCommonDivisor(15, -35)).toEqual(5); 18 | }); 19 | it("should work if both parameter are less than 0", () => { 20 | expect(greatestCommonDivisor(-15, -35)).toEqual(5); 21 | }); 22 | it("should return NaN if both parameters are 0", () => { 23 | expect(greatestCommonDivisor(0, 0)).toBeNaN(); 24 | }); 25 | it("should return second parameter if first is 0", () => { 26 | expect(greatestCommonDivisor(3, 0)).toEqual(3); 27 | }); 28 | it("should return first parameter if second is 0", () => { 29 | expect(greatestCommonDivisor(0, 4)).toEqual(4); 30 | }); 31 | it("should return NaN if first parameter is not an integer", () => { 32 | expect(greatestCommonDivisor(1.3, 4)).toBeNaN(); 33 | }); 34 | it("should return NaN if second parameter is not an integer", () => { 35 | expect(greatestCommonDivisor(1, 4.1)).toBeNaN(); 36 | }); 37 | it("should return NaN if both parameters are not an integer", () => { 38 | expect(greatestCommonDivisor(1.1, 4.1)).toBeNaN(); 39 | }); 40 | it("should return first number if they are equal", () => { 41 | expect(greatestCommonDivisor(5, 5)).toEqual(5); 42 | }); 43 | }); -------------------------------------------------------------------------------- /ts/functions/extendedEuclidesAlgorithm.ts: -------------------------------------------------------------------------------- 1 | import { IExtendedEuclidesAlgorithmResult } from "../Interfaces/IExtendedEuclidesAlgorithmResult"; 2 | 3 | /** 4 | * Extended Euclid's algorithm for finding the greatest common divisor of two numbers greater than 0 and its polynomial prepresentation 5 | * @param {number} a greater than 0 6 | * @param {number} b greater than 0 7 | */ 8 | export function extendedEuclidesAlgorithm(a: number, b: number): IExtendedEuclidesAlgorithmResult { 9 | if (!Number.isInteger(a) || !Number.isInteger(b)) { 10 | throw new Error("Euclide's extended algorithm works only for positive integers"); 11 | } 12 | if (a === 0 && b === 0) { 13 | throw new Error("Euclide's extended algorithm works only for positive integers"); 14 | } 15 | const aSign = a < 0 ? -1 : 1; 16 | const bSign = b < 0 ? -1 : 1; 17 | a = Math.abs(a); 18 | b = Math.abs(b); 19 | if (a === b) { 20 | return { 21 | gcd: a, 22 | x: 1, 23 | y: 0 24 | }; 25 | } else if (a < b) { 26 | const result = extendedEuclidesAlgorithmInternal(b, a); 27 | return { 28 | gcd: result.gcd, 29 | x: result.y * aSign, 30 | y: result.x * bSign 31 | } 32 | } else { 33 | const result = extendedEuclidesAlgorithmInternal(a, b); 34 | return { 35 | gcd: result.gcd, 36 | x: result.x * aSign, 37 | y: result.y * bSign 38 | } 39 | } 40 | } 41 | 42 | function extendedEuclidesAlgorithmInternal(a: number, b: number): IExtendedEuclidesAlgorithmResult { 43 | const result: IExtendedEuclidesAlgorithmResult = { 44 | gcd: a, 45 | x: 1, 46 | y: 0 47 | }; 48 | if (b !== 0) { 49 | const recursiveResult = extendedEuclidesAlgorithmInternal(b, a % b); 50 | result.gcd = recursiveResult.gcd; 51 | result.x = recursiveResult.y; 52 | result.y = recursiveResult.x - recursiveResult.y * Math.floor(a / b); 53 | } 54 | return result; 55 | } -------------------------------------------------------------------------------- /ts/ObjectArray.ts: -------------------------------------------------------------------------------- 1 | import {IObjectArrayProperty} from "./Interfaces/IObjectArrayProperty"; 2 | import {ITypedArray} from "./ITypedArray"; 3 | import {Utils} from "./Utils"; 4 | export class ObjectArray { 5 | private columns:ITypedArray[] = []; 6 | private nameToColumn:{[key: string]: number} = {}; 7 | private freePointer:number = 0; 8 | private nextFreeList:Float64Array; 9 | private properties: IObjectArrayProperty[]; 10 | 11 | constructor (size:number, properties: IObjectArrayProperty[]) { 12 | this.properties = properties; 13 | this.columns = properties.map(property => { 14 | var TypedArrayConstructor = property.type; 15 | return new TypedArrayConstructor(size); 16 | }); 17 | properties.forEach((property, i) => this.nameToColumn[property.name] = i); 18 | this.nextFreeList = new Float64Array(Utils.range(1, size + 1)); 19 | this.nextFreeList[size - 1] = -1; 20 | } 21 | 22 | public add(obj:number[]) { 23 | if(this.freePointer === -1) { 24 | throw new RangeError('Array size exceeded.'); 25 | } 26 | var index:number = this.freePointer; 27 | this.freePointer = this.nextFreeList[this.freePointer]; 28 | this.nextFreeList[index] = -1; 29 | obj.forEach((value, columnIndex) => this.columns[columnIndex][index] = value); 30 | return index; 31 | } 32 | 33 | public remove(index:number) { 34 | var currentPointer:number = this.freePointer; 35 | this.freePointer = index; 36 | this.nextFreeList[this.freePointer] = currentPointer; 37 | } 38 | 39 | public get(index:number) { 40 | if (this.nextFreeList[index] !== -1) { 41 | return null; 42 | } 43 | return this.columns.map(column => column[index]); 44 | } 45 | 46 | public getHash(index:number):any { 47 | var output:any = {}; 48 | if (this.nextFreeList[index] !== -1) { 49 | return null; 50 | } 51 | this.columns.map((column, columnIndex) => output[this.properties[columnIndex].name] = column[index]); 52 | return output; 53 | } 54 | } -------------------------------------------------------------------------------- /tests/RadixTreeSpec.ts: -------------------------------------------------------------------------------- 1 | import {RadixTree} from "../ts/RadixTree"; 2 | 3 | describe("Radix Tree", function () { 4 | it("should be able to add word that doesn't exist", function () { 5 | const tree: RadixTree = new RadixTree(); 6 | tree.insert("test", 1); 7 | tree.insert("slow", 2); 8 | tree.insert("water", 3); 9 | expect(tree.contains("test")).toEqual(true); 10 | expect(tree.contains("slow")).toEqual(true); 11 | expect(tree.contains("water")).toEqual(true); 12 | }); 13 | 14 | it("should be able to add word that extends existing word", function () { 15 | const tree: RadixTree = new RadixTree(); 16 | tree.insert("test", 1); 17 | tree.insert("slow", 2); 18 | tree.insert("water", 3); 19 | tree.insert("slower", 4); 20 | tree.insert("tester", 5); 21 | tree.insert("team", 6); 22 | tree.insert("toast", 7); 23 | expect(tree.contains("test")).toEqual(true); 24 | expect(tree.contains("slow")).toEqual(true); 25 | expect(tree.contains("water")).toEqual(true); 26 | expect(tree.contains("slower")).toEqual(true); 27 | expect(tree.contains("tester")).toEqual(true); 28 | expect(tree.contains("toast")).toEqual(true); 29 | expect(tree.contains("team")).toEqual(true); 30 | expect(tree.contains("te")).toEqual(false); 31 | }); 32 | 33 | it("should be able to add word that extends existing word", function () { 34 | const tree: RadixTree = new RadixTree(); 35 | tree.insert("test", 1); 36 | tree.insert("slow", 2); 37 | tree.insert("water", 3); 38 | tree.insert("slower", 4); 39 | tree.insert("tester", 5); 40 | tree.insert("team", 6); 41 | tree.insert("toast", 7); 42 | expect(tree.get("test")).toEqual(1); 43 | expect(tree.get("slow")).toEqual(2); 44 | expect(tree.get("water")).toEqual(3); 45 | expect(tree.get("slower")).toEqual(4); 46 | expect(tree.get("tester")).toEqual(5); 47 | expect(tree.get("team")).toEqual(6); 48 | expect(tree.get("toast")).toEqual(7); 49 | expect(tree.get("te")).toEqual(null); 50 | }); 51 | }); -------------------------------------------------------------------------------- /ts/RadixTreeNode.ts: -------------------------------------------------------------------------------- 1 | export class RadixTreeNode { 2 | public word: string; 3 | public value: T; 4 | public children: RadixTreeNode[] = []; 5 | 6 | constructor (word: string, value: T) { 7 | this.word = word; 8 | this.value = value; 9 | } 10 | 11 | public addChild(child: RadixTreeNode) { 12 | return this.children.push(child); 13 | } 14 | 15 | public isLeaf() { 16 | return this.value !== null; 17 | } 18 | 19 | public getNodeWithSharedPrefix(prefix: string): RadixTreeNode { 20 | const partialMatch: RadixTreeNode[] = this.children.filter(node => node.word[0] === prefix[0]); 21 | if (partialMatch.length === 0) { 22 | return null; 23 | } 24 | return partialMatch[0] 25 | } 26 | 27 | public getCommonPrefix(prefix: string): string { 28 | let i = 0; 29 | let commonPrefix = ""; 30 | while (prefix[i] === this.word[i]) { 31 | commonPrefix += prefix[i]; 32 | i++; 33 | } 34 | return commonPrefix; 35 | } 36 | 37 | public splitNode(sufix: string): void { 38 | const commonPrefix: string = this.getCommonPrefix(sufix); 39 | const currentChildren = this.children.slice(0); 40 | const newNode = new RadixTreeNode(this.word.substr(commonPrefix.length), this.value); 41 | currentChildren.forEach(newNode.addChild, newNode); 42 | this.word = commonPrefix; 43 | this.value = null; 44 | this.children = [newNode]; 45 | } 46 | 47 | public selectNextNodeFromPrefix(prefix: string): RadixTreeNode { 48 | const exactMatch: RadixTreeNode[] = this.children.filter(node => node.word === prefix); 49 | if (exactMatch.length === 1) { 50 | return exactMatch[0]; 51 | } 52 | const partialMatch: RadixTreeNode[] = this.children.filter(node => node.word[0] === prefix[0]); 53 | if (partialMatch.length === 0) { 54 | return null; 55 | } 56 | const partialMatchWord: string = partialMatch[0].word; 57 | 58 | if (prefix.indexOf(partialMatchWord) === -1) { 59 | return null; 60 | } 61 | return partialMatch[0]; 62 | } 63 | } -------------------------------------------------------------------------------- /ts/ITypedArray.ts: -------------------------------------------------------------------------------- 1 | interface ITypedArray { 2 | BYTES_PER_ELEMENT: number; 3 | buffer: ArrayBuffer; 4 | byteLength: number; 5 | byteOffset: number; 6 | copyWithin(target: number, start: number, end?: number): ITypedArray; 7 | every(callbackfn: (value: number, index: number, array: ITypedArray) => boolean, thisArg?: any): boolean; 8 | fill(value: number, start?: number, end?: number): ITypedArray; 9 | filter(callbackfn: (value: number, index: number, array: ITypedArray) => boolean, thisArg?: any): ITypedArray; 10 | find(predicate: (value: number, index: number, obj: ITypedArray) => boolean, thisArg?: any): number; 11 | findIndex(predicate: (value: number) => boolean, thisArg?: any): number; 12 | forEach(callbackfn: (value: number, index: number, array: ITypedArray) => void, thisArg?: any): void; 13 | indexOf(searchElement: number, fromIndex?: number): number; 14 | join(separator?: string): string; 15 | lastIndexOf(searchElement: number, fromIndex?: number): number; 16 | length: number; 17 | map(callbackfn: (value: number, index: number, array: ITypedArray) => number, thisArg?: any): ITypedArray; 18 | reduce(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: ITypedArray) => number, initialValue?: number): number; 19 | reduce(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: ITypedArray) => U, initialValue: U): U; 20 | reduceRight(callbackfn: (previousValue: number, currentValue: number, currentIndex: number, array: ITypedArray) => number, initialValue?: number): number; 21 | reduceRight(callbackfn: (previousValue: U, currentValue: number, currentIndex: number, array: ITypedArray) => U, initialValue: U): U; 22 | reverse(): ITypedArray; 23 | set(array: ArrayLike, offset?: number): void; 24 | slice(start?: number, end?: number): ITypedArray; 25 | some(callbackfn: (value: number, index: number, array: ITypedArray) => boolean, thisArg?: any): boolean; 26 | sort(compareFn?: (a: number, b: number) => number): ITypedArray; 27 | subarray(begin: number, end?: number): ITypedArray; 28 | toLocaleString(): string; 29 | toString(): string; 30 | 31 | [index: number]: number; 32 | } 33 | 34 | export {ITypedArray}; -------------------------------------------------------------------------------- /ts/BinaryTree/RedBlackTreeNode.ts: -------------------------------------------------------------------------------- 1 | import {IBinaryTreeNode} from "../Interfaces/IBinaryTreeNode"; 2 | class RedBlackTreeNode implements IBinaryTreeNode { 3 | public static sentinel: RedBlackTreeNode = new RedBlackTreeNode(null, null, true); 4 | 5 | // Red: false, Black: true 6 | public color: boolean = false; 7 | public left: RedBlackTreeNode = RedBlackTreeNode.sentinel; 8 | public right: RedBlackTreeNode = RedBlackTreeNode.sentinel; 9 | public value: T; 10 | public parent: RedBlackTreeNode = RedBlackTreeNode.sentinel; 11 | 12 | constructor(value: T, parent: RedBlackTreeNode, color: boolean) { 13 | this.value = value; 14 | this.parent = parent; 15 | this.color = color; 16 | } 17 | 18 | public hasLeftChild (): boolean { 19 | return this.left !== RedBlackTreeNode.sentinel; 20 | } 21 | 22 | public isBlack (): boolean { 23 | return this.color === true; 24 | } 25 | 26 | public hasRightChild (): boolean { 27 | return this.right !== RedBlackTreeNode.sentinel; 28 | } 29 | 30 | public isLeftChild (): boolean { 31 | return this.parent && this.parent.left === this; 32 | } 33 | 34 | public isRed (): boolean { 35 | return this.color === false; 36 | } 37 | 38 | public isRightChild (): boolean { 39 | return this.parent && this.parent.right === this; 40 | } 41 | 42 | public isSentinel (): boolean { 43 | return this === RedBlackTreeNode.sentinel; 44 | } 45 | 46 | public isNotSentinel (): boolean { 47 | return this !== RedBlackTreeNode.sentinel; 48 | } 49 | 50 | public isRoot (): boolean { 51 | return this.parent === RedBlackTreeNode.sentinel; 52 | } 53 | 54 | public setRed (): void { 55 | this.color = false; 56 | } 57 | 58 | public setBlack (): void { 59 | this.color = true; 60 | } 61 | 62 | public min (): RedBlackTreeNode { 63 | if (this.left !== RedBlackTreeNode.sentinel) { 64 | return this.left.min(); 65 | } 66 | return this; 67 | } 68 | 69 | public max (): RedBlackTreeNode { 70 | if (this.right !== RedBlackTreeNode.sentinel) { 71 | return this.right.max(); 72 | } 73 | return this; 74 | } 75 | } 76 | 77 | export default RedBlackTreeNode; -------------------------------------------------------------------------------- /tests/Graph/DirectedAdjacencyMatrixGraphSpec.ts: -------------------------------------------------------------------------------- 1 | import {DirectedAdjacencyMatrixGraph} from "../../ts/Graph/DirectedAdjacencyMatrixGraph"; 2 | 3 | describe("DirectedAdjacencyMatrixGraph", function () { 4 | it("should be able to add nodes", function () { 5 | const graph = new DirectedAdjacencyMatrixGraph(); 6 | graph.addNode("test"); 7 | graph.addNode("test 2"); 8 | graph.addNode("test 3"); 9 | expect(graph.nodes()).toEqual(["test", "test 2", "test 3"]) 10 | }); 11 | 12 | it("should be able to get adjacent nodes", function () { 13 | const graph = new DirectedAdjacencyMatrixGraph(); 14 | graph.addNode("a"); 15 | graph.addNode("b"); 16 | graph.addNode("c"); 17 | graph.addNode("d"); 18 | graph.addEdge("b", "a"); 19 | graph.addEdge("b", "c"); 20 | graph.addEdge("d", "b"); 21 | expect(graph.adjacent("b")).toEqual(["a", "c"]); 22 | expect(graph.outdegree("b")).toEqual(["a", "c"]); 23 | }); 24 | 25 | it("should be able to get indegree nodes", function () { 26 | const graph = new DirectedAdjacencyMatrixGraph(); 27 | graph.addNode("a"); 28 | graph.addNode("b"); 29 | graph.addNode("c"); 30 | graph.addNode("d"); 31 | graph.addEdge("b", "a"); 32 | graph.addEdge("b", "c"); 33 | graph.addEdge("d", "b"); 34 | expect(graph.indegree("a")).toEqual(["b"]); 35 | expect(graph.indegree("b")).toEqual(["d"]); 36 | expect(graph.indegree("c")).toEqual(["b"]); 37 | expect(graph.indegree("d")).toEqual([]); 38 | }); 39 | it("should be able to remove edge", function () { 40 | const graph = new DirectedAdjacencyMatrixGraph(); 41 | graph.addNode("a"); 42 | graph.addNode("b"); 43 | graph.addEdge("b", "a"); 44 | expect(graph.indegree("a")).toEqual(["b"]); 45 | expect(graph.outdegree("b")).toEqual(["a"]); 46 | graph.removeEdge("b", "a"); 47 | expect(graph.indegree("a")).toEqual([]); 48 | expect(graph.outdegree("b")).toEqual([]); 49 | }); 50 | describe("topologicalSort", function () { 51 | it("topologicalSort", function () { 52 | const graph = new DirectedAdjacencyMatrixGraph(); 53 | graph.addNode("5"); 54 | graph.addNode("7"); 55 | graph.addNode("3"); 56 | graph.addNode("11"); 57 | graph.addNode("8"); 58 | graph.addNode("2"); 59 | graph.addNode("9"); 60 | graph.addNode("10"); 61 | graph.addEdge("5", "11"); 62 | graph.addEdge("7", "11"); 63 | graph.addEdge("7", "8"); 64 | graph.addEdge("3", "8"); 65 | graph.addEdge("3", "10"); 66 | graph.addEdge("11", "2"); 67 | graph.addEdge("11", "9"); 68 | graph.addEdge("11", "10"); 69 | graph.addEdge("8", "9"); 70 | expect(graph.topologicalSort()).toEqual(["5", "7", "3", "11", "8", "2", "10", "9"]); 71 | }); 72 | }); 73 | }); -------------------------------------------------------------------------------- /ts/LinkedList.ts: -------------------------------------------------------------------------------- 1 | import {ILinkedListItem} from "./Interfaces/ILinkedListItem"; 2 | export class LinkedList { 3 | public head:ILinkedListItem = LinkedList.emptyListItem(); 4 | public length:number = 0; 5 | public tail:ILinkedListItem = LinkedList.emptyListItem(); 6 | 7 | public static emptyListItem ():ILinkedListItem { 8 | return >({ prev:null, value:null, next:null }); 9 | } 10 | 11 | public static newItem (prev:ILinkedListItem, next:ILinkedListItem, value: T):ILinkedListItem { 12 | return >({ prev: prev, next: next, value: value }); 13 | } 14 | 15 | constructor () { 16 | this.tail.prev = this.head; 17 | this.head.next = this.tail; 18 | } 19 | 20 | public forEach(callback: (item: T, index:number, list:LinkedList) => void, thisArg:any = null): void { 21 | var currentItem = this.head.next; 22 | var counter = 0; 23 | while(currentItem !== this.tail) { 24 | callback.call(thisArg, currentItem.value, counter, this); 25 | counter++; 26 | currentItem = currentItem.next; 27 | } 28 | } 29 | 30 | public isEmpty():boolean { 31 | return this.head.next === this.tail; 32 | } 33 | 34 | public push(value:T): void { 35 | this.addAfter(value, this.tail.prev); 36 | } 37 | 38 | public pop(): T{ 39 | var currentItem = this.tail.prev; 40 | if(this.isEmpty()) { 41 | throw new Error(`The linked list is empty.`); 42 | } 43 | this.removeItem(currentItem); 44 | return currentItem.value; 45 | } 46 | 47 | public remove(value:T): void{ 48 | var currentItem = this.search(value); 49 | if (currentItem) { 50 | this.removeItem(currentItem); 51 | } else { 52 | throw new Error(`Cannot remove the value ${value}, it's not present in the linked list.`); 53 | } 54 | } 55 | 56 | public shift(): T{ 57 | var item = this.head.next; 58 | if(this.isEmpty()) { 59 | throw new Error(`The linked list is empty.`); 60 | } 61 | this.removeItem(item); 62 | return item.value; 63 | } 64 | 65 | public unshift(value:T): void{ 66 | this.addAfter(value, this.head); 67 | } 68 | 69 | private addAfter(value:T, itemAfter:ILinkedListItem): void{ 70 | var newItem = LinkedList.newItem(itemAfter, itemAfter.next, value); 71 | itemAfter.next.prev = newItem; 72 | itemAfter.next = newItem; 73 | this.length++; 74 | } 75 | 76 | private removeItem(item:ILinkedListItem): void{ 77 | item.prev.next = item.next; 78 | item.next.prev = item.prev; 79 | this.length--; 80 | } 81 | 82 | private search(value:T): ILinkedListItem{ 83 | var currentItem = this.head.next; 84 | while(currentItem !== this.tail) { 85 | if (value === currentItem.value) { 86 | return currentItem; 87 | } 88 | currentItem = currentItem.next; 89 | } 90 | return null; 91 | } 92 | } -------------------------------------------------------------------------------- /tests/StackSpec.ts: -------------------------------------------------------------------------------- 1 | import {Stack} from "../ts/Stack"; 2 | describe('Stack', function () { 3 | describe('clear', function () { 4 | it('should remove all elements from the stack', function () { 5 | var s = new Stack(); 6 | s.push(1); 7 | s.push(2); 8 | expect(s.size()).toEqual(2); 9 | s.push(3); 10 | expect(s.size()).toEqual(3); 11 | s.clear(); 12 | expect(s.size()).toEqual(0); 13 | expect(s.isEmpty()).toEqual(true); 14 | }); 15 | }); 16 | 17 | describe('empty', function () { 18 | it('should return true if stack is empty', function () { 19 | var s = new Stack(); 20 | expect(s.isEmpty()).toEqual(true); 21 | s.push(1); 22 | expect(s.isEmpty()).toEqual(false); 23 | s.clear(); 24 | expect(s.isEmpty()).toEqual(true); 25 | }); 26 | }); 27 | 28 | describe('search', function () { 29 | it('should return the index from the top of the element in the stack', function () { 30 | var s = new Stack(); 31 | s.push(1); 32 | expect(s.search(1)).toEqual(0); 33 | s.push(2); 34 | expect(s.search(1)).toEqual(1); 35 | expect(s.search(2)).toEqual(0); 36 | s.clear(); 37 | expect(s.search(1)).toEqual(-1); 38 | }); 39 | }); 40 | 41 | describe('peek', function () { 42 | it('should return the last element added to the stack', function () { 43 | var s = new Stack(); 44 | s.push(1); 45 | expect(s.peek()).toEqual(1); 46 | s.push(2); 47 | expect(s.peek()).toEqual(2); 48 | s.pop(); 49 | expect(s.peek()).toEqual(1); 50 | s.pop(); 51 | expect(s.peek).toThrow(); 52 | }); 53 | }); 54 | 55 | describe('pop', function () { 56 | it('should pop the last element added to the stack', function () { 57 | var s = new Stack(); 58 | s.push(1); 59 | expect(s.pop()).toEqual(1); 60 | s.push(2); 61 | s.push(3); 62 | expect(s.pop()).toEqual(3); 63 | expect(s.pop()).toEqual(2); 64 | expect(s.pop).toThrow(); 65 | }); 66 | }); 67 | 68 | describe('push', function () { 69 | it('should add element to the stack', function () { 70 | var s = new Stack(); 71 | s.push(1); 72 | expect(s.size()).toEqual(1); 73 | s.push(1); 74 | expect(s.size()).toEqual(2); 75 | }); 76 | }); 77 | 78 | describe('size', function () { 79 | it('should return the number of items on the stack', function () { 80 | var s = new Stack(); 81 | s.push(1); 82 | expect(s.size()).toEqual(1); 83 | s.push(1); 84 | expect(s.size()).toEqual(2); 85 | s.push(1); 86 | expect(s.size()).toEqual(3); 87 | s.pop(); 88 | expect(s.size()).toEqual(2); 89 | s.clear(); 90 | expect(s.size()).toEqual(0); 91 | }); 92 | }); 93 | }); -------------------------------------------------------------------------------- /ts/RadixTree.ts: -------------------------------------------------------------------------------- 1 | import {RadixTreeNode} from "./RadixTreeNode"; 2 | export class RadixTree { 3 | private root: RadixTreeNode = new RadixTreeNode("", null); 4 | 5 | constructor () { } 6 | 7 | insert(word: string, value: T) { 8 | let currentNode: RadixTreeNode = this.root; 9 | let cumulatedWord: string = ""; 10 | while (true) { 11 | const sufix: string = word.substr(cumulatedWord.length); 12 | const nextNode: RadixTreeNode = currentNode.selectNextNodeFromPrefix(sufix); 13 | if (nextNode === null) { 14 | if (currentNode.isLeaf()) { 15 | currentNode.addChild(new RadixTreeNode("", currentNode.value)); 16 | currentNode.addChild(new RadixTreeNode(sufix, value)); 17 | currentNode.value = null; 18 | } else { 19 | let nodeToBeSplitted = currentNode.getNodeWithSharedPrefix(sufix); 20 | if (nodeToBeSplitted === null) { 21 | currentNode.addChild(new RadixTreeNode(sufix, value)); 22 | } else { 23 | nodeToBeSplitted.splitNode(sufix); 24 | currentNode = nodeToBeSplitted; 25 | cumulatedWord += nodeToBeSplitted.word; 26 | continue; 27 | } 28 | } 29 | break; 30 | } else { 31 | currentNode = nextNode; 32 | cumulatedWord += nextNode.word; 33 | } 34 | } 35 | 36 | } 37 | 38 | contains(word: string) { 39 | let currentNode: RadixTreeNode = this.root; 40 | let cumulatedWord: string = ""; 41 | 42 | while (currentNode !== null && !currentNode.isLeaf() && cumulatedWord.length <= word.length) { 43 | 44 | const sufix: string = word.substr(cumulatedWord.length); 45 | const nextNode: RadixTreeNode = currentNode.selectNextNodeFromPrefix(sufix); 46 | if (nextNode !== null) { 47 | currentNode = nextNode; 48 | cumulatedWord += nextNode.word; 49 | } else { 50 | currentNode = null; 51 | } 52 | } 53 | return (currentNode !== null && currentNode.isLeaf() && cumulatedWord.length === word.length); 54 | } 55 | 56 | get(word: string) { 57 | let currentNode: RadixTreeNode = this.root; 58 | let cumulatedWord: string = ""; 59 | 60 | while (currentNode !== null && !currentNode.isLeaf() && cumulatedWord.length <= word.length) { 61 | const sufix: string = word.substr(cumulatedWord.length); 62 | const nextNode: RadixTreeNode = currentNode.selectNextNodeFromPrefix(sufix); 63 | 64 | if (nextNode !== null) { 65 | currentNode = nextNode; 66 | cumulatedWord += nextNode.word; 67 | } else { 68 | currentNode = null; 69 | } 70 | } 71 | if (currentNode !== null && currentNode.isLeaf() && cumulatedWord.length === word.length) { 72 | return currentNode.value; 73 | } else { 74 | return null; 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /tests/TypedStackSpec.ts: -------------------------------------------------------------------------------- 1 | import {TypedStack} from "../ts/TypedStack"; 2 | describe('Stack', function () { 3 | describe('clear', function () { 4 | it('should remove all elements from the stack', function () { 5 | var s = new TypedStack(Uint8Array, 8); 6 | s.push(1); 7 | s.push(2); 8 | expect(s.size()).toEqual(2); 9 | s.push(3); 10 | expect(s.size()).toEqual(3); 11 | s.clear(); 12 | expect(s.size()).toEqual(0); 13 | expect(s.isEmpty()).toEqual(true); 14 | }); 15 | }); 16 | describe('empty', function () { 17 | it('should return true if stack is empty', function () { 18 | var s = new TypedStack(Uint8Array, 8); 19 | expect(s.isEmpty()).toEqual(true); 20 | s.push(1); 21 | expect(s.isEmpty()).toEqual(false); 22 | s.clear(); 23 | expect(s.isEmpty()).toEqual(true); 24 | }); 25 | }); 26 | 27 | describe('search', function () { 28 | it('should return the index from the top of the element in the stack', function () { 29 | var s = new TypedStack(Uint8Array, 8); 30 | s.push(1); 31 | expect(s.search(1)).toEqual(0); 32 | s.push(2); 33 | expect(s.search(1)).toEqual(1); 34 | expect(s.search(2)).toEqual(0); 35 | s.clear(); 36 | expect(s.search(1)).toEqual(-1); 37 | }); 38 | }); 39 | 40 | describe('peek', function () { 41 | it('should return the last element added to the stack', function () { 42 | var s = new TypedStack(Uint8Array, 8); 43 | s.push(1); 44 | expect(s.peek()).toEqual(1); 45 | s.push(2); 46 | expect(s.peek()).toEqual(2); 47 | s.pop(); 48 | expect(s.peek()).toEqual(1); 49 | s.pop(); 50 | expect(s.peek).toThrow(); 51 | }); 52 | }); 53 | 54 | describe('pop', function () { 55 | it('should pop the last element added to the stack', function () { 56 | var s = new TypedStack(Uint8Array, 8); 57 | s.push(1); 58 | expect(s.pop()).toEqual(1); 59 | s.push(2); 60 | s.push(3); 61 | expect(s.pop()).toEqual(3); 62 | expect(s.pop()).toEqual(2); 63 | expect(s.pop).toThrow(); 64 | }); 65 | }); 66 | 67 | describe('push', function () { 68 | it('should add element to the stack', function () { 69 | var s = new TypedStack(Uint8Array, 3); 70 | s.push(1); 71 | expect(s.size()).toEqual(1); 72 | s.push(1); 73 | expect(s.size()).toEqual(2); 74 | expect(s.push).toThrow(); 75 | }); 76 | }); 77 | 78 | describe('size', function () { 79 | it('should return the number of items on the stack', function () { 80 | var s = new TypedStack(Uint8Array, 3); 81 | s.push(1); 82 | expect(s.size()).toEqual(1); 83 | s.push(1); 84 | expect(s.size()).toEqual(2); 85 | s.push(1); 86 | expect(s.size()).toEqual(3); 87 | s.pop(); 88 | expect(s.size()).toEqual(2); 89 | s.clear(); 90 | expect(s.size()).toEqual(0); 91 | }); 92 | }); 93 | }); -------------------------------------------------------------------------------- /tests/LinkedListSpec.ts: -------------------------------------------------------------------------------- 1 | import {LinkedList} from "../ts/LinkedList"; 2 | describe('LinkedList', function () { 3 | 4 | it('should be able to push items', function () { 5 | var list = new LinkedList(); 6 | expect(list.length).toEqual(0); 7 | list.push(1); 8 | expect(list.length).toEqual(1); 9 | list.push(1); 10 | expect(list.length).toEqual(2); 11 | list.pop(); 12 | expect(list.length).toEqual(1); 13 | list.pop(); 14 | expect(list.length).toEqual(0); 15 | }); 16 | 17 | it('should be able to go through all elements using forEach', function () { 18 | var list = new LinkedList(); 19 | list.push(1); 20 | list.push(2); 21 | list.push(3); 22 | list.push(4); 23 | list.forEach((el, index) => { 24 | expect(el).toEqual(index + 1); 25 | }); 26 | }); 27 | 28 | it('should be able to unshift items', function () { 29 | var list = new LinkedList(); 30 | expect(list.length).toEqual(0); 31 | list.unshift(1); 32 | expect(list.length).toEqual(1); 33 | list.unshift(1); 34 | expect(list.length).toEqual(2); 35 | list.shift(); 36 | expect(list.length).toEqual(1); 37 | list.shift(); 38 | expect(list.length).toEqual(0); 39 | }); 40 | 41 | it('should be able to pop items', function () { 42 | var list = new LinkedList(); 43 | expect(list.length).toEqual(0); 44 | list.push(1); 45 | expect(list.length).toEqual(1); 46 | list.push(2); 47 | expect(list.length).toEqual(2); 48 | expect(list.pop()).toEqual(2); 49 | expect(list.length).toEqual(1); 50 | expect(list.pop()).toEqual(1); 51 | expect(list.length).toEqual(0); 52 | }); 53 | 54 | it('pop should throw error if the list is empty', function () { 55 | var list = new LinkedList(); 56 | expect(list.pop).toThrow(); 57 | list.push(1); 58 | expect(list.pop()).toEqual(1); 59 | expect(list.pop).toThrow(); 60 | }); 61 | 62 | it('isEmpty should return true when there are no items in the list', function () { 63 | var list = new LinkedList(); 64 | expect(list.isEmpty()).toEqual(true); 65 | list.push(1); 66 | expect(list.length).toEqual(1); 67 | list.push(2); 68 | expect(list.length).toEqual(2); 69 | expect(list.pop()).toEqual(2); 70 | expect(list.length).toEqual(1); 71 | expect(list.pop()).toEqual(1); 72 | expect(list.isEmpty()).toEqual(true); 73 | }); 74 | 75 | it('remove should remove item from the list', function () { 76 | var list = new LinkedList(); 77 | list.push(1); 78 | list.push(2); 79 | list.push(3); 80 | expect(list.length).toEqual(3); 81 | list.remove(3); 82 | expect(list.length).toEqual(2); 83 | list.remove(1); 84 | expect(list.length).toEqual(1); 85 | list.remove(2); 86 | expect(list.length).toEqual(0); 87 | }); 88 | 89 | it('remove should throw if not existing item is being removed', function () { 90 | var list = new LinkedList(); 91 | expect(function () { list.remove(5);}).toThrow(); 92 | list.push(1); 93 | list.remove(1); 94 | expect(function () { list.remove(5);}).toThrow(); 95 | }); 96 | }); -------------------------------------------------------------------------------- /ts/Graph/DirectedAdjacencyMatrixGraph.ts: -------------------------------------------------------------------------------- 1 | import {IBitMatrix} from "../Interfaces/IBitMatrix"; 2 | import {BitMatrix} from "../BitMatrix"; 3 | import {Queue} from "../Queue"; 4 | 5 | export class DirectedAdjacencyMatrixGraph { 6 | private hashFunction: (node: T) => string = (a: T) => a.toString(); 7 | private vertices: {[name: string]: number} = {}; 8 | private edgeToVertexMap: T[] = []; 9 | private edges: IBitMatrix = new BitMatrix(0, 0); 10 | 11 | constructor (hashFunction?: (node: T) => string) { 12 | this.hashFunction = hashFunction || this.hashFunction; 13 | } 14 | 15 | public outdegree(vertex: T): T[] { 16 | const vertexIndex: number = this.vertices[this.hashFunction(vertex)]; 17 | return this.edges.getRowIndexes(vertexIndex).map(vertexIndex => this.edgeToVertexMap[vertexIndex]); 18 | } 19 | 20 | public adjacent (vertex: T): T[] { 21 | return this.outdegree(vertex); 22 | } 23 | 24 | public addNode (node: T): DirectedAdjacencyMatrixGraph { 25 | this.edgeToVertexMap.push(node); 26 | const newNodeIndex: number = this.edges.size()[0]; 27 | this.vertices[this.hashFunction(node)] = newNodeIndex; 28 | this.edges.resize(newNodeIndex + 1, newNodeIndex + 1); 29 | return this; 30 | } 31 | 32 | public addEdge (from: T, to: T): DirectedAdjacencyMatrixGraph { 33 | const fromNodeIndex: number = this.vertices[this.hashFunction(from)]; 34 | const toNodeIndex: number = this.vertices[this.hashFunction(to)]; 35 | this.edges.set(fromNodeIndex, toNodeIndex, true); 36 | return this; 37 | } 38 | 39 | public indegree (vertex: T): T[] { 40 | const vertexIndex: number = this.vertices[this.hashFunction(vertex)]; 41 | const indexes: number[] = this.edges.getColIndexes(vertexIndex); 42 | return indexes.map(index => this.edgeToVertexMap[index]); 43 | } 44 | 45 | public nodes (): T[] { 46 | return this.edgeToVertexMap.slice(0); 47 | } 48 | 49 | public removeEdge (from: T, to: T): DirectedAdjacencyMatrixGraph { 50 | const fromNodeIndex: number = this.vertices[this.hashFunction(from)]; 51 | const toNodeIndex: number = this.vertices[this.hashFunction(to)]; 52 | this.edges.set(fromNodeIndex, toNodeIndex, false); 53 | return this; 54 | } 55 | 56 | public topologicalSort() { 57 | const sortedElements: T[] = []; 58 | const verticesCount: number = this.edges.size()[0]; 59 | const edgesCopy: IBitMatrix = this.edges.clone(); 60 | const verticesWithNoIncomingEdges: Queue = new Queue(verticesCount); 61 | edgesCopy.getIndexes(true) 62 | .map((column, index) => column.length === 0 ? index : -1) 63 | .filter(index => index !== -1) 64 | .forEach(index => verticesWithNoIncomingEdges.enqueue(index)); 65 | 66 | while (verticesWithNoIncomingEdges.size() > 0) { 67 | const currentNodeIndex: number = verticesWithNoIncomingEdges.dequeue(); 68 | sortedElements.push(this.edgeToVertexMap[currentNodeIndex]); 69 | edgesCopy.getRowIndexes(currentNodeIndex).forEach(index => { 70 | edgesCopy.set(currentNodeIndex, index, false); 71 | if (edgesCopy.getColIndexes(index).length === 0) { 72 | verticesWithNoIncomingEdges.enqueue(index); 73 | } 74 | }); 75 | } 76 | 77 | if (edgesCopy.count() > 0) { 78 | throw new Error(`The graph contains cycles.`); 79 | } 80 | return sortedElements; 81 | } 82 | } -------------------------------------------------------------------------------- /tests/RadixTreeNodeSpec.ts: -------------------------------------------------------------------------------- 1 | import {RadixTreeNode} from "../ts/RadixTreeNode"; 2 | 3 | describe("RadixTreeNode", function () { 4 | it("should be able to search empty node", function () { 5 | const node: RadixTreeNode = new RadixTreeNode("", null); 6 | expect(node.selectNextNodeFromPrefix("test")).toBeNull(); 7 | expect(node.selectNextNodeFromPrefix("")).toBeNull(); 8 | }); 9 | 10 | it("should be able to find empty node by prefix", function () { 11 | const node: RadixTreeNode = new RadixTreeNode("", null); 12 | node.addChild(new RadixTreeNode("", 1)); 13 | expect(node.selectNextNodeFromPrefix("").value).toEqual(1); 14 | }); 15 | 16 | it("should be able to find existing exact node by prefix", function () { 17 | const node: RadixTreeNode = new RadixTreeNode("", null); 18 | node.addChild(new RadixTreeNode("test", 1)); 19 | expect(node.selectNextNodeFromPrefix("test").value).toEqual(1); 20 | }); 21 | 22 | it("should be able to find existing partial node by prefix", function () { 23 | const node: RadixTreeNode = new RadixTreeNode("", null); 24 | node.addChild(new RadixTreeNode("tes", 1)); 25 | expect(node.selectNextNodeFromPrefix("test").value).toEqual(1); 26 | }); 27 | 28 | it("should be able to find existing exact node by prefix when there is an empty node before it", function () { 29 | const node: RadixTreeNode = new RadixTreeNode("", null); 30 | node.addChild(new RadixTreeNode("", 2)); 31 | node.addChild(new RadixTreeNode("test", 1)); 32 | expect(node.selectNextNodeFromPrefix("test").value).toEqual(1); 33 | }); 34 | 35 | it("should be able to find existing exact node by prefix when there is an empty node after it", function () { 36 | const node: RadixTreeNode = new RadixTreeNode("", null); 37 | node.addChild(new RadixTreeNode("test", 1)); 38 | node.addChild(new RadixTreeNode("", 2)); 39 | expect(node.selectNextNodeFromPrefix("test").value).toEqual(1); 40 | }); 41 | 42 | it("should be able to find not exact match by prefix", function () { 43 | const node: RadixTreeNode = new RadixTreeNode("", null); 44 | node.addChild(new RadixTreeNode("test", 1)); 45 | node.addChild(new RadixTreeNode("", 2)); 46 | expect(node.selectNextNodeFromPrefix("tester").value).toEqual(1); 47 | }); 48 | 49 | it("should return null if no node is found by prefix", function () { 50 | const node: RadixTreeNode = new RadixTreeNode("", null); 51 | node.addChild(new RadixTreeNode("test", 1)); 52 | node.addChild(new RadixTreeNode("", 2)); 53 | expect(node.selectNextNodeFromPrefix("tes")).toBeNull(); 54 | }); 55 | 56 | describe("getCommonPrefix", function () { 57 | it("should return empty string if no common prefix", function () { 58 | const node: RadixTreeNode = new RadixTreeNode("test", null); 59 | expect(node.getCommonPrefix("ars")).toEqual(""); 60 | }); 61 | it("should return common prefix", function () { 62 | const node: RadixTreeNode = new RadixTreeNode("test", null); 63 | expect(node.getCommonPrefix("tes")).toEqual("tes"); 64 | expect(node.getCommonPrefix("ter")).toEqual("te"); 65 | }); 66 | it("should return empty string if looking for empty string", function () { 67 | const node: RadixTreeNode = new RadixTreeNode("test", null); 68 | expect(node.getCommonPrefix("")).toEqual(""); 69 | }); 70 | }); 71 | }); -------------------------------------------------------------------------------- /ts/HashTable.ts: -------------------------------------------------------------------------------- 1 | import {IHashTable} from "./Interfaces/IHashTable"; 2 | import {IKeyValuePair} from "./Interfaces/IKeyValuePair"; 3 | import {Utils} from "./Utils"; 4 | export class HashTable implements IHashTable { 5 | private capacity: number = 0; 6 | private buckets: IKeyValuePair[][] = []; 7 | private hash: (value: K) => number; 8 | private equalKey: (value1: K, value2: K) => boolean = (value1: K, value2: K) => value1 === value2; 9 | private equalValue: (value1: V, value2: V) => boolean = (value1: V, value2: V) => value1 === value2; 10 | 11 | constructor(capacity: number, hash: (value: K) => number) { 12 | this.capacity = capacity; 13 | this.hash = hash; 14 | for (let i = 0; i < this.capacity; i++) { 15 | this.buckets.push([]); 16 | } 17 | } 18 | 19 | public clear(): void { 20 | for (let i = 0; i < this.capacity; i++) { 21 | this.buckets.push([]); 22 | } 23 | } 24 | 25 | public clone(): IHashTable { 26 | const copy: IHashTable = new HashTable(this.capacity, this.hash); 27 | copy.putAll(this.elements()); 28 | return copy; 29 | } 30 | 31 | public contains(value: V): boolean { 32 | return this.buckets.some(bucket => bucket.some(entry => this.equalValue(entry.value, value))); 33 | } 34 | 35 | public containsKey(key: K): boolean { 36 | const bucket: IKeyValuePair[] = this.getBucket(key); 37 | if (bucket.length === 0) { 38 | return false; 39 | } 40 | return bucket.some(entry => this.equalKey(entry.key, key)); 41 | } 42 | 43 | public containsValue(value: V): boolean { 44 | return this.buckets.some(bucket => bucket.some(entry => this.equalValue(entry.value, value))); 45 | } 46 | 47 | public elements(): IKeyValuePair[] { 48 | return this.buckets.reduce((pv, cv) => pv.concat(cv), [ ]); 49 | } 50 | 51 | public get(key: K): V { 52 | const bucket: IKeyValuePair[] = this.getBucket(key); 53 | let value: V = null; 54 | bucket.some(entry => { 55 | if (this.equalKey(entry.key, key)) { 56 | value = entry.value; 57 | return true; 58 | } 59 | return false; 60 | }); 61 | return value; 62 | } 63 | 64 | public put(key: K, value: V) { 65 | const bucket: IKeyValuePair[] = this.getBucket(key); 66 | bucket.push({key: key, value: value}); 67 | } 68 | 69 | public putAll(elements: IKeyValuePair[]) { 70 | elements.forEach(el => this.put(el.key, el.value)); 71 | } 72 | 73 | public remove(key: K): V { 74 | const bucket: IKeyValuePair[] = this.getBucket(key); 75 | const keyIndex: number = Utils.findIndexBy(bucket, (el) => this.equalKey(el.key, key)); 76 | if (keyIndex === -1) { 77 | return null; 78 | } else { 79 | return bucket.splice(keyIndex, 1)[0].value; 80 | } 81 | } 82 | 83 | public size(): number { 84 | return this.buckets.reduce((total, bucket) => total + bucket.length, 0); 85 | } 86 | 87 | public values(): V[] { 88 | return this.elements().map(e => e.value); 89 | } 90 | 91 | private getBucket(key: K): IKeyValuePair[] { 92 | let index = this.hash(key); 93 | if (index < 0) { 94 | index = Math.abs(index); 95 | } 96 | return this.buckets[index < this.buckets.length ? index : index % this.buckets.length]; 97 | } 98 | } 99 | 100 | export default HashTable; -------------------------------------------------------------------------------- /tests/functions/extendedEuclidesAlgorithmSpec.ts: -------------------------------------------------------------------------------- 1 | import { extendedEuclidesAlgorithm } from "../../ts/functions/extendedEuclidesAlgorithm"; 2 | 3 | describe("extendedEuclidesAlgorithm", () => { 4 | it("should be able to find common divisor for the same number", () => { 5 | expect(extendedEuclidesAlgorithm(17, 17).gcd).toEqual(17); 6 | }); 7 | it("should return 1 when no common divisor", () => { 8 | expect(extendedEuclidesAlgorithm(17, 6).gcd).toEqual(1); 9 | }); 10 | it("should return first if it is multiple of second", () => { 11 | expect(extendedEuclidesAlgorithm(15, 35).gcd).toEqual(5); 12 | }); 13 | it("should throw error if both parameters are 0", () => { 14 | expect(() => extendedEuclidesAlgorithm(0, 0)).toThrow(); 15 | }); 16 | it("should return second parameter if first is 0", () => { 17 | expect(extendedEuclidesAlgorithm(3, 0).gcd).toEqual(3); 18 | }); 19 | it("should return first parameter if second is 0", () => { 20 | expect(extendedEuclidesAlgorithm(0, 4).gcd).toEqual(4); 21 | }); 22 | it("should rthrow error if first parameter is not an integer", () => { 23 | expect(() => extendedEuclidesAlgorithm(1.3, 4)).toThrow(); 24 | }); 25 | it("should throw error if second parameter is not an integer", () => { 26 | expect(() => extendedEuclidesAlgorithm(1, 4.1)).toThrow(); 27 | }); 28 | it("should throw error if both parameters are not an integer", () => { 29 | expect(() => extendedEuclidesAlgorithm(1.1, 4.1)).toThrow(); 30 | }); 31 | it("should return first number if they are equal", () => { 32 | expect(extendedEuclidesAlgorithm(5, 5).gcd).toEqual(5); 33 | }); 34 | it("should calculate proper gcd components if parameters are equal", () => { 35 | expect(extendedEuclidesAlgorithm(5, 5)).toEqual({ 36 | gcd: 5, 37 | x: 1, 38 | y: 0 39 | }); 40 | }); 41 | it("should calculate proper gcd components if first parameter is 0", () => { 42 | expect(extendedEuclidesAlgorithm(0, 5)).toEqual({ 43 | gcd: 5, 44 | x: 0, 45 | y: 1 46 | }); 47 | }); 48 | it("should calculate proper gcd components if second parameter is 0", () => { 49 | expect(extendedEuclidesAlgorithm(5, 0)).toEqual({ 50 | gcd: 5, 51 | x: 1, 52 | y: 0 53 | }); 54 | }); 55 | it("should calculate proper gcd components when numbers have common divisor greater than 1", () => { 56 | expect(extendedEuclidesAlgorithm(10, 6)).toEqual({ 57 | gcd: 2, 58 | x: -1, 59 | y: 2 60 | }); 61 | }); 62 | it("should calculate proper gcd components when first number is less than 0", () => { 63 | expect(extendedEuclidesAlgorithm(-10, 6)).toEqual({ 64 | gcd: 2, 65 | x: 1, 66 | y: 2 67 | }); 68 | }); 69 | it("should calculate proper gcd components when second number is less than 0", () => { 70 | expect(extendedEuclidesAlgorithm(10, -6)).toEqual({ 71 | gcd: 2, 72 | x: -1, 73 | y: -2 74 | }); 75 | }); 76 | it("should calculate proper gcd components when both numbers are less than 0", () => { 77 | expect(extendedEuclidesAlgorithm(-10, -6)).toEqual({ 78 | gcd: 2, 79 | x: 1, 80 | y: -2 81 | }); 82 | }); 83 | it("should calculate proper gcd components when numbers have common divisor equal to 1", () => { 84 | expect(extendedEuclidesAlgorithm(7, 5)).toEqual({ 85 | gcd: 1, 86 | x: -2, 87 | y: 3 88 | }); 89 | }); 90 | it("should calculate proper gcd components when numbers are more complicated", () => { 91 | expect(extendedEuclidesAlgorithm(391, 299)).toEqual({ 92 | gcd: 23, 93 | x: -3, 94 | y: 4 95 | }); 96 | }); 97 | }); -------------------------------------------------------------------------------- /ts/BitArray.ts: -------------------------------------------------------------------------------- 1 | import {IBitArray} from "./Interfaces/IBitArray"; 2 | export class BitArray implements IBitArray { 3 | private length: number = 0; 4 | private buffer: Uint32Array; 5 | 6 | constructor (size: number) { 7 | this.length = size; 8 | this.buffer = new Uint32Array(this.calculateBufferSize(this.length)); 9 | } 10 | 11 | public count() { 12 | return this.buffer.reduce((total, chunk) => this.bitsCountInNumber(chunk) + total, 0); 13 | } 14 | 15 | public get(index: number) { 16 | this.validateIndex(index); 17 | const chunkOffset = Math.floor(index / 32); 18 | const bitOffset = index - chunkOffset * 32; 19 | return !! (this.buffer[chunkOffset] & (1 << bitOffset)); 20 | } 21 | 22 | public getIndexes() { 23 | let result: number[] = []; 24 | let bitIndex: number = 0; 25 | let bit: boolean = false; 26 | while (bitIndex < this.length) { 27 | bit = this.get(bitIndex); 28 | if (bit) { 29 | result.push(bitIndex); 30 | } 31 | bitIndex++; 32 | } 33 | return result; 34 | } 35 | 36 | public reset(): BitArray { 37 | this.buffer.fill(0); 38 | return this; 39 | } 40 | 41 | public resize(newSize: number): BitArray { 42 | if (newSize < 0) { 43 | throw new RangeError(`Invalid new BitSet size ${newSize}`) 44 | } 45 | const newBufferSize = this.calculateBufferSize(newSize); 46 | this.length = newSize; 47 | if (newBufferSize > this.buffer.length) { 48 | const oldBuffer = this.buffer; 49 | this.buffer = new Uint32Array(newBufferSize); 50 | this.buffer.set(oldBuffer); 51 | } else if (newBufferSize < this.buffer.length) { 52 | const oldBuffer = this.buffer; 53 | this.buffer = new Uint32Array(newBufferSize); 54 | this.buffer.set(oldBuffer.slice(0, newBufferSize)); 55 | } 56 | return this; 57 | } 58 | 59 | public size() { 60 | return this.length; 61 | } 62 | 63 | public splice(startIndex: number, deleteCount: number): void { 64 | if (isNaN(deleteCount) || deleteCount < 1) { 65 | return; 66 | } 67 | if (startIndex < 0) { 68 | startIndex = this.length + startIndex; 69 | if (startIndex < 0) { 70 | throw new RangeError(`${startIndex} is less than 0`) 71 | } 72 | } else if (startIndex >= this.length) { 73 | throw new RangeError(`${startIndex} exceeds the array size ${this.length}`) 74 | } 75 | 76 | const tempBuffer: number[] = this.getIndexes() 77 | .filter(index => index < startIndex || index >= startIndex + deleteCount) 78 | .map(index => index >= startIndex + deleteCount ? index - deleteCount : index); 79 | this.reset(); 80 | this.resize(this.length - deleteCount); 81 | tempBuffer.forEach(id => this.set(id, true)); 82 | 83 | } 84 | 85 | public set(index: number, value: boolean) { 86 | this.validateIndex(index); 87 | const chunkOffset = Math.floor(index / 32); 88 | const offset = index - chunkOffset * 32; 89 | if (value) { 90 | this.buffer[chunkOffset] |= (1 << offset); 91 | } else { 92 | this.buffer[chunkOffset] &= ~(1 << offset); 93 | } 94 | return this; 95 | } 96 | 97 | private calculateBufferSize(bitsCount: number): number { 98 | return Math.ceil(bitsCount / 32) * 4; 99 | } 100 | 101 | private bitsCountInNumber (value: number) { 102 | value -= ((value >>> 1) & 0x55555555); 103 | value = (value & 0x33333333) + ((value >>> 2) & 0x33333333); 104 | return (((value + (value >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24); 105 | } 106 | 107 | private validateIndex(index: number) { 108 | if (index > this.length - 1 || index < 0) { 109 | throw new RangeError("Index is too large."); 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | ecmaFeatures: 2 | modules: true 3 | jsx: true 4 | 5 | env: 6 | amd: true 7 | browser: true 8 | es6: true 9 | jquery: true 10 | node: true 11 | 12 | # http://eslint.org/docs/rules/ 13 | rules: 14 | # Possible Errors 15 | comma-dangle: [2, never] 16 | no-cond-assign: 2 17 | no-console: 0 18 | no-constant-condition: 2 19 | no-control-regex: 2 20 | no-debugger: 2 21 | no-dupe-args: 2 22 | no-dupe-keys: 2 23 | no-duplicate-case: 2 24 | no-empty: 2 25 | no-empty-character-class: 2 26 | no-ex-assign: 2 27 | no-extra-boolean-cast: 2 28 | no-extra-parens: 0 29 | no-extra-semi: 2 30 | no-func-assign: 2 31 | no-inner-declarations: [2, functions] 32 | no-invalid-regexp: 2 33 | no-irregular-whitespace: 2 34 | no-negated-in-lhs: 2 35 | no-obj-calls: 2 36 | no-regex-spaces: 2 37 | no-sparse-arrays: 2 38 | no-unexpected-multiline: 2 39 | no-unreachable: 2 40 | use-isnan: 2 41 | valid-jsdoc: 0 42 | valid-typeof: 2 43 | 44 | # Best Practices 45 | accessor-pairs: 2 46 | block-scoped-var: 0 47 | complexity: [2, 6] 48 | consistent-return: 0 49 | curly: 0 50 | default-case: 0 51 | dot-location: 0 52 | dot-notation: 0 53 | eqeqeq: 2 54 | guard-for-in: 2 55 | no-alert: 2 56 | no-caller: 2 57 | no-case-declarations: 2 58 | no-div-regex: 2 59 | no-else-return: 0 60 | no-empty-label: 2 61 | no-empty-pattern: 2 62 | no-eq-null: 2 63 | no-eval: 2 64 | no-extend-native: 2 65 | no-extra-bind: 2 66 | no-fallthrough: 2 67 | no-floating-decimal: 0 68 | no-implicit-coercion: 0 69 | no-implied-eval: 2 70 | no-invalid-this: 0 71 | no-iterator: 2 72 | no-labels: 0 73 | no-lone-blocks: 2 74 | no-loop-func: 2 75 | no-magic-number: 0 76 | no-multi-spaces: 0 77 | no-multi-str: 0 78 | no-native-reassign: 2 79 | no-new-func: 2 80 | no-new-wrappers: 2 81 | no-new: 2 82 | no-octal-escape: 2 83 | no-octal: 2 84 | no-proto: 2 85 | no-redeclare: 2 86 | no-return-assign: 2 87 | no-script-url: 2 88 | no-self-compare: 2 89 | no-sequences: 0 90 | no-throw-literal: 0 91 | no-unused-expressions: 2 92 | no-useless-call: 2 93 | no-useless-concat: 2 94 | no-void: 2 95 | no-warning-comments: 0 96 | no-with: 2 97 | radix: 2 98 | vars-on-top: 0 99 | wrap-iife: 2 100 | yoda: 0 101 | 102 | # Strict 103 | strict: 0 104 | 105 | # Variables 106 | init-declarations: 0 107 | no-catch-shadow: 2 108 | no-delete-var: 2 109 | no-label-var: 2 110 | no-shadow-restricted-names: 2 111 | no-shadow: 0 112 | no-undef-init: 2 113 | no-undef: 0 114 | no-undefined: 0 115 | no-unused-vars: 0 116 | no-use-before-define: 0 117 | 118 | # Node.js and CommonJS 119 | callback-return: 2 120 | global-require: 2 121 | handle-callback-err: 2 122 | no-mixed-requires: 0 123 | no-new-require: 0 124 | no-path-concat: 2 125 | no-process-exit: 2 126 | no-restricted-modules: 0 127 | no-sync: 0 128 | 129 | # Stylistic Issues 130 | array-bracket-spacing: 0 131 | block-spacing: 0 132 | brace-style: 0 133 | camelcase: 0 134 | comma-spacing: 0 135 | comma-style: 0 136 | computed-property-spacing: 0 137 | consistent-this: 0 138 | eol-last: 0 139 | func-names: 0 140 | func-style: 0 141 | id-length: 0 142 | id-match: 0 143 | indent: 0 144 | jsx-quotes: 0 145 | key-spacing: 0 146 | linebreak-style: 0 147 | lines-around-comment: 0 148 | max-depth: 0 149 | max-len: 0 150 | max-nested-callbacks: 0 151 | max-params: 0 152 | max-statements: [2, 30] 153 | new-cap: 0 154 | new-parens: 0 155 | newline-after-var: 0 156 | no-array-constructor: 0 157 | no-bitwise: 0 158 | no-continue: 0 159 | no-inline-comments: 0 160 | no-lonely-if: 0 161 | no-mixed-spaces-and-tabs: 0 162 | no-multiple-empty-lines: 0 163 | no-negated-condition: 0 164 | no-nested-ternary: 0 165 | no-new-object: 0 166 | no-plusplus: 0 167 | no-restricted-syntax: 0 168 | no-spaced-func: 0 169 | no-ternary: 0 170 | no-trailing-spaces: 0 171 | no-underscore-dangle: 0 172 | no-unneeded-ternary: 0 173 | object-curly-spacing: 0 174 | one-var: 0 175 | operator-assignment: 0 176 | operator-linebreak: 0 177 | padded-blocks: 0 178 | quote-props: 0 179 | quotes: 0 180 | require-jsdoc: 0 181 | semi-spacing: 0 182 | semi: 0 183 | sort-vars: 0 184 | space-after-keywords: 0 185 | space-before-blocks: 0 186 | space-before-function-paren: 0 187 | space-before-keywords: 0 188 | space-in-parens: 0 189 | space-infix-ops: 0 190 | space-return-throw-case: 0 191 | space-unary-ops: 0 192 | spaced-comment: 0 193 | wrap-regex: 0 194 | 195 | # ECMAScript 6 196 | arrow-body-style: 0 197 | arrow-parens: 0 198 | arrow-spacing: 0 199 | constructor-super: 0 200 | generator-star-spacing: 0 201 | no-arrow-condition: 0 202 | no-class-assign: 0 203 | no-const-assign: 0 204 | no-dupe-class-members: 0 205 | no-this-before-super: 0 206 | no-var: 0 207 | object-shorthand: 0 208 | prefer-arrow-callback: 0 209 | prefer-const: 0 210 | prefer-reflect: 0 211 | prefer-spread: 0 212 | prefer-template: 0 213 | require-yield: 0 214 | -------------------------------------------------------------------------------- /ts/BinaryTree/BinaryTree.ts: -------------------------------------------------------------------------------- 1 | import IBinaryTree from "../Interfaces/IBinaryTree"; 2 | import {IBinaryTreeNode} from "../Interfaces/IBinaryTreeNode"; 3 | import {BinaryTreeNode} from "./BinaryTreeNode"; 4 | export class BinaryTree implements IBinaryTree> { 5 | 6 | public comparator: (a: T, b: T) => boolean; 7 | public root: IBinaryTreeNode = null; 8 | 9 | constructor(comparator: (a: T, b: T) => boolean = (a, b) => a > b) { 10 | this.comparator = comparator; 11 | } 12 | 13 | public add(value: T): void { 14 | let currentNode: IBinaryTreeNode = this.root; 15 | let futureParent: IBinaryTreeNode = null; 16 | const newNode: IBinaryTreeNode = new BinaryTreeNode(value, null); 17 | while (currentNode !== null) { 18 | futureParent = currentNode; 19 | if (futureParent !== null) { 20 | if (this.comparator(futureParent.value, value)) { 21 | currentNode = futureParent.left; 22 | } else { 23 | currentNode = futureParent.right; 24 | } 25 | } 26 | } 27 | 28 | newNode.parent = futureParent; 29 | if (futureParent === null) { 30 | this.root = newNode; 31 | } else if (this.comparator(futureParent.value, value)) { 32 | futureParent.left = newNode; 33 | } else { 34 | futureParent.right = newNode; 35 | } 36 | } 37 | 38 | public remove(value: T) { 39 | let node: IBinaryTreeNode = this.search(value); 40 | if (node === null) { 41 | return false; 42 | } 43 | 44 | if (!node.hasLeftChild()) { 45 | this.transplant(node, node.right); 46 | } else if (!node.hasRightChild()) { 47 | this.transplant(node, node.left); 48 | } else { 49 | let successor = node.right.min(); 50 | if (successor !== node.right) { 51 | this.transplant(successor, successor.right); 52 | successor.right = node.right; 53 | successor.right.parent = successor; 54 | } 55 | this.transplant(node, successor); 56 | } 57 | } 58 | 59 | public inOrderTreeWalk(callback: (pv: any, cv: T) => any, initialValue: any) { 60 | return this.inorderNodeWalk(this.root, callback, initialValue); 61 | } 62 | 63 | public isEmpty() { 64 | return this.root === null; 65 | } 66 | 67 | public max(): IBinaryTreeNode { 68 | const currentNode: IBinaryTreeNode = this.root; 69 | if (this.isEmpty()) { 70 | return null; 71 | } 72 | return currentNode.max(); 73 | } 74 | 75 | public min(): IBinaryTreeNode { 76 | const currentNode: IBinaryTreeNode = this.root; 77 | if (this.isEmpty()) { 78 | return null; 79 | } 80 | return currentNode.min(); 81 | } 82 | 83 | public reverseTreeWalk(callback: (pv: any, cv: T) => any, initialValue: any) { 84 | return this.reverseNodeWalk(this.root, callback, initialValue); 85 | } 86 | 87 | public search(value: T) { 88 | let currentNode: IBinaryTreeNode = this.root; 89 | while (currentNode && currentNode.value !== value) { 90 | if (this.comparator(currentNode.value, value)) { 91 | currentNode = currentNode.left; 92 | } else { 93 | currentNode = currentNode.right; 94 | } 95 | } 96 | 97 | return currentNode; 98 | } 99 | 100 | public successor (value: T) { 101 | let node = this.search(value); 102 | let ancestor: IBinaryTreeNode; 103 | if (node === null || value === null) { 104 | return null; 105 | } 106 | if (node.right !== null) { 107 | return node.right.min(); 108 | } 109 | ancestor = node.parent; 110 | while (ancestor !== null && ancestor.right === node) { 111 | node = ancestor; 112 | ancestor = node.parent; 113 | } 114 | return ancestor; 115 | } 116 | 117 | private inorderNodeWalk(node: IBinaryTreeNode, callback: (pv: any, cv: T) => any, previousValue: any) { 118 | if (node !== null) { 119 | previousValue = this.inorderNodeWalk(node.left, callback, previousValue); 120 | previousValue = callback(previousValue, node.value); 121 | previousValue = this.inorderNodeWalk(node.right, callback, previousValue); 122 | return previousValue; 123 | } else { 124 | return previousValue; 125 | } 126 | } 127 | 128 | private reverseNodeWalk(node: IBinaryTreeNode, callback: (pv: any, cv: T) => any, previousValue: any) { 129 | if (node !== null) { 130 | previousValue = this.reverseNodeWalk(node.right, callback, previousValue); 131 | previousValue = callback(previousValue, node.value); 132 | previousValue = this.reverseNodeWalk(node.left, callback, previousValue); 133 | return previousValue; 134 | } else { 135 | return previousValue; 136 | } 137 | } 138 | 139 | private transplant(node: IBinaryTreeNode, newNode: IBinaryTreeNode) { 140 | if (node.isRoot()) { 141 | this.root = newNode; 142 | } else if (node.isLeftChild()) { 143 | node.parent.left = newNode; 144 | } else { 145 | node.parent.right = newNode; 146 | } 147 | if (newNode !== null) { 148 | newNode.parent = node.parent; 149 | } 150 | } 151 | } -------------------------------------------------------------------------------- /tests/BitArraySpec.ts: -------------------------------------------------------------------------------- 1 | import {BitArray} from "../ts/BitArray"; 2 | 3 | describe("BitArray", function () { 4 | it("be able to set bits", function () { 5 | const bs = new BitArray(100); 6 | bs.set(1, true); 7 | bs.set(99, true); 8 | expect(bs.get(0)).toEqual(false); 9 | expect(bs.get(1)).toEqual(true); 10 | expect(bs.get(54)).toEqual(false); 11 | expect(bs.get(99)).toEqual(true); 12 | }); 13 | 14 | it("be able to get set bits count", function () { 15 | const bs = new BitArray(100); 16 | bs.set(1, true); 17 | bs.set(99, true); 18 | expect(bs.count()).toEqual(2); 19 | bs.set(99, false); 20 | expect(bs.count()).toEqual(1); 21 | }); 22 | 23 | it("be able to get set bits count", function () { 24 | const bs = new BitArray(100); 25 | bs.set(1, true); 26 | bs.set(99, true); 27 | expect(bs.getIndexes()).toEqual([1, 99]); 28 | bs.set(29, true); 29 | expect(bs.getIndexes()).toEqual([1, 29, 99]); 30 | bs.set(29, false); 31 | expect(bs.getIndexes()).toEqual([1, 99]); 32 | }); 33 | 34 | it("should throw error if trying to set bit out of bounds", function () { 35 | const bs = new BitArray(100); 36 | expect(() => bs.set(1, true)).not.toThrowError(); 37 | expect(() => bs.set(100, true)).toThrowError(); 38 | expect(() => bs.set(101, true)).toThrowError(); 39 | expect(() => bs.set(-1, true)).toThrowError(); 40 | }); 41 | 42 | it("should be able to zero all values", function () { 43 | const bs = new BitArray(100); 44 | bs.set(1, true); 45 | bs.set(99, true); 46 | expect(bs.get(0)).toEqual(false); 47 | expect(bs.get(1)).toEqual(true); 48 | expect(bs.get(54)).toEqual(false); 49 | expect(bs.get(99)).toEqual(true); 50 | bs.reset(); 51 | expect(bs.get(0)).toEqual(false); 52 | expect(bs.get(1)).toEqual(false); 53 | expect(bs.get(54)).toEqual(false); 54 | expect(bs.get(99)).toEqual(false); 55 | }); 56 | 57 | describe("resize", function () { 58 | it("should be able to extend the BitArray", function () { 59 | const bs = new BitArray(100); 60 | bs.set(1, true); 61 | bs.set(2, true); 62 | bs.set(99, true); 63 | bs.resize(302); 64 | expect(bs.get(300)).toEqual(false); 65 | expect(bs.get(301)).toEqual(false); 66 | bs.set(300, true); 67 | bs.set(301, true); 68 | expect(bs.get(300)).toEqual(true); 69 | expect(bs.get(301)).toEqual(true); 70 | }); 71 | 72 | it("should be able to shrink the BitArray", function () { 73 | const bs = new BitArray(100); 74 | bs.set(1, true); 75 | bs.set(2, true); 76 | bs.set(99, true); 77 | bs.resize(51); 78 | expect(bs.get(49)).toEqual(false); 79 | expect(bs.get(50)).toEqual(false); 80 | bs.set(50, true); 81 | expect(bs.get(49)).toEqual(false); 82 | expect(bs.get(50)).toEqual(true); 83 | }); 84 | 85 | it("nothing should be affected if the size doesn't change", function () { 86 | const bs = new BitArray(100); 87 | bs.set(1, true); 88 | bs.set(2, true); 89 | bs.set(99, true); 90 | bs.resize(100); 91 | expect(bs.get(0)).toEqual(false); 92 | expect(bs.get(1)).toEqual(true); 93 | expect(bs.get(54)).toEqual(false); 94 | expect(bs.get(99)).toEqual(true); 95 | }); 96 | 97 | it("should throw error if index is invalid", function () { 98 | const bs = new BitArray(100); 99 | expect(() => bs.resize(-1)).toThrowError(); 100 | }); 101 | }); 102 | 103 | describe("splice", function () { 104 | it("should remove number of elements", function () { 105 | const bs = new BitArray(100); 106 | bs.set(1, true); 107 | bs.set(2, true); 108 | bs.set(5, true); 109 | bs.set(6, true); 110 | bs.set(99, true); 111 | bs.splice(2, 3); 112 | expect(bs.size()).toEqual(97); 113 | expect(bs.get(1)).toEqual(true); 114 | expect(bs.get(2)).toEqual(true); 115 | expect(bs.get(3)).toEqual(true); 116 | expect(bs.get(5)).toEqual(false); 117 | expect(bs.get(96)).toEqual(true); 118 | }); 119 | 120 | it("should be able to provide negative start index", function () { 121 | const bs = new BitArray(100); 122 | bs.set(1, true); 123 | bs.set(2, true); 124 | bs.set(5, true); 125 | bs.set(6, true); 126 | bs.set(99, true); 127 | bs.splice(-98, 3); 128 | expect(bs.size()).toEqual(97); 129 | expect(bs.get(1)).toEqual(true); 130 | expect(bs.get(2)).toEqual(true); 131 | expect(bs.get(3)).toEqual(true); 132 | expect(bs.get(5)).toEqual(false); 133 | expect(bs.get(96)).toEqual(true); 134 | }); 135 | 136 | it("delete is NaN or less than 1 do nothing", function () { 137 | const bs = new BitArray(100); 138 | bs.splice(-98, -3); 139 | expect(bs.size()).toEqual(100); 140 | bs.splice(-98, Number.NaN); 141 | expect(bs.size()).toEqual(100); 142 | }); 143 | 144 | it("should throw error if start index is greater than array length", function () { 145 | const bs = new BitArray(100); 146 | expect(() => bs.splice(101, 2)).toThrowError(); 147 | }); 148 | 149 | it("should splice from 0 if negative value passed and it is greater than array length by abs value", function () { 150 | const bs = new BitArray(100); 151 | expect(() => bs.splice(-101, 2)).toThrowError(); 152 | }); 153 | }); 154 | }); -------------------------------------------------------------------------------- /tests/HashTableSpec.ts: -------------------------------------------------------------------------------- 1 | import HashTable from "../ts/HashTable"; 2 | describe(`HashTable`, function () { 3 | it(`should be able to put elements and retrieve them`, function () { 4 | const table = new HashTable(5, (key) => key); 5 | table.put(1, 3); 6 | expect(table.size()).toEqual(1); 7 | table.put(2, 10); 8 | expect(table.size()).toEqual(2); 9 | expect(table.get(1)).toEqual(3); 10 | expect(table.get(2)).toEqual(10); 11 | }); 12 | 13 | it(`should be able to rcloneetrieve elements with colisions`, function () { 14 | const table = new HashTable(5, (key) => 3); 15 | table.put(1, 3); 16 | table.put(2, 10); 17 | expect(table.get(1)).toEqual(3); 18 | expect(table.get(2)).toEqual(10); 19 | }); 20 | 21 | it(`not found element will return null`, function () { 22 | const table = new HashTable(5, (key) => key); 23 | table.put(1, 3); 24 | table.put(2, 10); 25 | expect(table.get(3)).toBeNull(); 26 | }); 27 | 28 | it(`not found element will return null even if another value is in the bucket`, function () { 29 | const table = new HashTable(5, (key) => 3); 30 | table.put(1, 3); 31 | table.put(2, 10); 32 | expect(table.get(3)).toBeNull(); 33 | }); 34 | 35 | it(`should be able to put elements with negative key hash`, function () { 36 | const table = new HashTable(5, (key) => key); 37 | table.put(-1, 3); 38 | expect(table.get(-1)).toEqual(3); 39 | }); 40 | 41 | it(`should be able to put elements with key above capacity`, function () { 42 | const table = new HashTable(5, (key) => key); 43 | table.put(5, 3); 44 | table.put(6, 44); 45 | expect(table.get(5)).toEqual(3); 46 | expect(table.get(6)).toEqual(44); 47 | }); 48 | 49 | it(`should be able to clear the hashtable`, function () { 50 | const table = new HashTable(5, (key) => key); 51 | table.put(5, 3); 52 | table.put(6, 44); 53 | expect(table.get(5)).toEqual(3); 54 | expect(table.get(6)).toEqual(44); 55 | table.clear(); 56 | expect(table.get(5)).toBeNull(); 57 | expect(table.get(6)).toBeNull(); 58 | 59 | }); 60 | 61 | describe(`contains`, function () { 62 | it(`should return false if hastable doesn't contain value`, function () { 63 | const table = new HashTable(5, (key) => key); 64 | table.put(1, 3); 65 | table.put(2, 10); 66 | expect(table.contains(1)).toEqual(false); 67 | }); 68 | it(`should return true if hastable contains value`, function () { 69 | const table = new HashTable(5, (key) => key); 70 | table.put(1, 3); 71 | table.put(2, 10); 72 | expect(table.contains(3)).toEqual(true); 73 | expect(table.contains(10)).toEqual(true); 74 | }); 75 | 76 | describe(`containsKey`, function () { 77 | it(`should return false if hastable doesn't contain the key`, function () { 78 | const table = new HashTable(5, (key) => key); 79 | table.put(1, 3); 80 | table.put(2, 10); 81 | expect(table.containsKey(3)).toEqual(false); 82 | expect(table.containsKey(0)).toEqual(false); 83 | expect(table.containsKey(-1)).toEqual(false); 84 | }); 85 | it(`should return true if hastable contains the key`, function () { 86 | const table = new HashTable(5, (key) => key); 87 | table.put(1, 3); 88 | table.put(2, 10); 89 | expect(table.containsKey(1)).toEqual(true); 90 | expect(table.containsKey(2)).toEqual(true); 91 | }); 92 | }); 93 | 94 | describe(`containsValue`, function () { 95 | it(`will return false if hashtable maps one or more keys to this value`, function () { 96 | const table = new HashTable(5, (key) => key); 97 | table.put(1, 3); 98 | table.put(2, 10); 99 | expect(table.containsValue(4)).toEqual(false); 100 | expect(table.containsValue(0)).toEqual(false); 101 | expect(table.containsValue(-1)).toEqual(false); 102 | }); 103 | it(`will return true if hashtable maps one or more keys to this value`, function () { 104 | const table = new HashTable(5, (key) => key); 105 | table.put(1, 3); 106 | table.put(2, 10); 107 | expect(table.containsValue(3)).toEqual(true); 108 | expect(table.containsValue(10)).toEqual(true); 109 | }); 110 | }); 111 | 112 | describe(`clone`, function () { 113 | it(`will return a shallow copy of the hashtable`, function () { 114 | const table = new HashTable(5, (key) => key); 115 | table.put(1, 3); 116 | table.put(2, 10); 117 | 118 | const clone = table.clone(); 119 | expect(clone.elements()).toEqual(table.elements()); 120 | }); 121 | }); 122 | 123 | describe(`remove`, function () { 124 | it(`will remove the key and its value from the hashtable`, function () { 125 | const table = new HashTable(5, (key) => key); 126 | table.put(1, 3); 127 | expect(table.size()).toEqual(1); 128 | table.remove(1); 129 | expect(table.size()).toEqual(0); 130 | }); 131 | it(`will remove the key and its value from the hashtable even if we have collision`, function () { 132 | const table = new HashTable(5, (key) => key); 133 | table.put(1, 3); 134 | table.put(6, 4); 135 | expect(table.size()).toEqual(2); 136 | table.remove(1); 137 | expect(table.size()).toEqual(1); 138 | }); 139 | it(`will do nothing if element not found`, function () { 140 | const table = new HashTable(5, (key) => key); 141 | table.put(1, 3); 142 | table.put(6, 4); 143 | expect(table.size()).toEqual(2); 144 | table.remove(3); 145 | expect(table.size()).toEqual(2); 146 | }); 147 | it(`will return the element that was removed`, function () { 148 | const table = new HashTable(5, (key) => key); 149 | table.put(1, 3); 150 | table.put(6, 4); 151 | expect(table.remove(1)).toEqual(3); 152 | expect(table.remove(6)).toEqual(4); 153 | }); 154 | }); 155 | }); 156 | }); -------------------------------------------------------------------------------- /ts/BitMatrix.ts: -------------------------------------------------------------------------------- 1 | import {IBitMatrix} from "./Interfaces/IBitMatrix"; 2 | export class BitMatrix implements IBitMatrix { 3 | private rowCount: number = 0; 4 | private colCount: number = 0; 5 | private buffer: Uint32Array; 6 | 7 | constructor (rowSize: number, colSize: number) { 8 | this.rowCount = rowSize; 9 | this.colCount = colSize; 10 | this.buffer = new Uint32Array(this.calculateBufferSize(this.colCount * this.rowCount)); 11 | } 12 | 13 | public clone(): IBitMatrix { 14 | const clone = new BitMatrix(this.rowCount, this.colCount); 15 | clone.setBuffer(this.buffer.slice(0)); 16 | return clone; 17 | } 18 | 19 | public count(): number { 20 | return this.buffer.reduce((total, chunk) => this.bitsCountInNumber(chunk) + total, 0); 21 | } 22 | 23 | public get(rowIndex: number, colIndex: number) { 24 | this.validateIndex(rowIndex, colIndex); 25 | const index: number = this.getBitPosition(rowIndex, colIndex); 26 | const chunkOffset = Math.floor(index / 32); 27 | const bitOffset = index - chunkOffset * 32; 28 | return !! (this.buffer[chunkOffset] & (1 << bitOffset)); 29 | } 30 | 31 | public getColIndexes(column: number): number[] { 32 | const output: number[] = []; 33 | for (let i = 0; i < this.rowCount; i++) { 34 | if (this.get(i, column)) { 35 | output.push(i); 36 | } 37 | } 38 | 39 | return output; 40 | } 41 | 42 | public getRowIndexes(row: number): number[] { 43 | const output: number[] = []; 44 | for (let i = 0; i < this.colCount; i++) { 45 | if (this.get(row, i)) { 46 | output.push(i); 47 | } 48 | } 49 | 50 | return output; 51 | } 52 | 53 | public getIndexes(resultPerColumn: boolean = false): number[][] { 54 | let result: number[][] = []; 55 | let index: number = 0; 56 | if (resultPerColumn) { 57 | while (index < this.colCount) { 58 | result.push(this.getColIndexes(index)); 59 | index++; 60 | } 61 | } else { 62 | while (index < this.rowCount) { 63 | result.push(this.getRowIndexes(index)); 64 | index++; 65 | } 66 | } 67 | return result; 68 | } 69 | 70 | public reset(): BitMatrix { 71 | this.buffer.fill(0); 72 | return this; 73 | } 74 | 75 | public resize(rowCount: number, colCount: number): BitMatrix { 76 | if (rowCount < 0 || colCount < 0) { 77 | throw new RangeError(`Invalid new BitMatrix size ${rowCount}x${colCount}`) 78 | } 79 | const setIndexes: number[][] = this.getIndexes(); 80 | this.buffer = new Uint32Array(this.calculateBufferSize(rowCount * colCount)); 81 | this.rowCount = rowCount; 82 | this.colCount = colCount; 83 | setIndexes.filter((setIndexes, row) => row < rowCount) 84 | .forEach((setIndexes, row) => { 85 | setIndexes.forEach(column => { 86 | if (column < this.colCount) { 87 | this.set(row, column, true); 88 | } 89 | }); 90 | }); 91 | return this; 92 | } 93 | 94 | public setBuffer(newBuffer: Uint32Array): IBitMatrix { 95 | if (!newBuffer || newBuffer.length !== this.buffer.length) { 96 | throw new Error(`Invalid buffer ${newBuffer}`); 97 | } 98 | this.buffer = newBuffer; 99 | return this; 100 | } 101 | 102 | public size(): number[] { 103 | return [this.rowCount, this.colCount]; 104 | } 105 | 106 | public spliceColumn(startIndex: number, deleteCount: number): IBitMatrix { 107 | if (isNaN(deleteCount) || deleteCount < 1) { 108 | return; 109 | } 110 | if (startIndex < 0) { 111 | startIndex = this.colCount + startIndex; 112 | if (startIndex < 0) { 113 | throw new RangeError(`${startIndex} is less than 0`) 114 | } 115 | } else if (startIndex >= this.colCount) { 116 | throw new RangeError(`${startIndex} exceeds the matrix size ${this.colCount}`) 117 | } 118 | 119 | const tempBuffer: number[][] = this.getIndexes(); 120 | this.reset(); 121 | this.resize(this.rowCount, this.colCount - deleteCount); 122 | tempBuffer.forEach((indexes, row) => { 123 | indexes.forEach(id => { 124 | if (id < startIndex || id >= startIndex + deleteCount) { 125 | let normalizedId = id >= startIndex + deleteCount ? id - deleteCount : id; 126 | this.set(row, normalizedId, true); 127 | } 128 | }); 129 | }); 130 | } 131 | 132 | public spliceRow(startIndex: number, deleteCount: number): IBitMatrix { 133 | if (isNaN(deleteCount) || deleteCount < 1) { 134 | return; 135 | } 136 | if (startIndex < 0) { 137 | startIndex = this.colCount + startIndex; 138 | if (startIndex < 0) { 139 | throw new RangeError(`${startIndex} is less than 0`) 140 | } 141 | } else if (startIndex >= this.colCount) { 142 | throw new RangeError(`${startIndex} exceeds the matrix size ${this.colCount}`) 143 | } 144 | 145 | const tempBuffer: number[][] = this.getIndexes().filter((setIds, row) => row < startIndex || row >= startIndex + deleteCount); 146 | this.reset(); 147 | this.resize(this.rowCount - deleteCount, this.colCount); 148 | tempBuffer.forEach((indexes, row) => { 149 | indexes.forEach(id => { 150 | this.set(row, id, true); 151 | }); 152 | }); 153 | } 154 | 155 | public set(rowIndex: number, colIndex: number, value: boolean) { 156 | this.validateIndex(rowIndex, colIndex); 157 | const index: number = this.getBitPosition(rowIndex, colIndex); 158 | const chunkOffset = Math.floor(index / 32); 159 | const offset = index - chunkOffset * 32; 160 | if (value) { 161 | this.buffer[chunkOffset] |= (1 << offset); 162 | } else { 163 | this.buffer[chunkOffset] &= ~(1 << offset); 164 | } 165 | return this; 166 | } 167 | 168 | private calculateBufferSize(bitsCount: number): number { 169 | return Math.ceil(bitsCount / 32) * 4; 170 | } 171 | 172 | private getBitPosition (rowIndex: number, colIndex: number): number { 173 | return this.colCount * rowIndex + colIndex; 174 | } 175 | 176 | private bitsCountInNumber (value: number) { 177 | value -= ((value >>> 1) & 0x55555555); 178 | value = (value & 0x33333333) + ((value >>> 2) & 0x33333333); 179 | return (((value + (value >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24); 180 | } 181 | 182 | private validateIndex(rowIndex: number, colIndex: number) { 183 | if (rowIndex >= this.rowCount || rowIndex < 0) { 184 | throw new RangeError(`Row index is incorrect. Maximum allowed index: ${this.rowCount - 1}. Actual index ${rowIndex}`); 185 | } 186 | if (colIndex >= this.colCount || colIndex < 0) { 187 | throw new RangeError(`Column index is incorrect. Maximum allowed index: ${this.colCount - 1}. Actual index ${colIndex}`); 188 | } 189 | } 190 | } -------------------------------------------------------------------------------- /tests/BinaryTreeSpec.ts: -------------------------------------------------------------------------------- 1 | import {BinaryTree} from "../ts/BinaryTree/BinaryTree"; 2 | 3 | describe("BinaryTree", function () { 4 | describe("add" , function () { 5 | it("should be able to add node", function () { 6 | const tree = new BinaryTree(); 7 | tree.add(2); 8 | expect(tree.root.value).toEqual(2); 9 | tree.add(1); 10 | expect(tree.root.left.value).toEqual(1); 11 | }); 12 | it("should be able to add node", function () { 13 | const tree = new BinaryTree((a, b) => a > b); 14 | tree.add(5); 15 | expect(tree.root.value).toEqual(5); 16 | tree.add(6); 17 | expect(tree.root.right.value).toEqual(6); 18 | tree.add(4); 19 | expect(tree.root.left.value).toEqual(4); 20 | }); 21 | }); 22 | 23 | it("should be able to add node to binary tree with object values", function () { 24 | const tree = new BinaryTree((a, b) => a.amount > b.amount); 25 | tree.add({amount : 5}); 26 | expect(tree.root.value).toEqual({amount : 5}); 27 | tree.add({amount : 6}); 28 | expect(tree.root.right.value).toEqual({amount : 6}); 29 | tree.add({amount : 4}); 30 | expect(tree.root.left.value).toEqual({amount : 4}); 31 | }); 32 | 33 | it("should be able to walk the binary tree in order", function () { 34 | const tree = new BinaryTree((a, b) => a > b); 35 | tree.add(5); 36 | tree.add(6); 37 | tree.add(4); 38 | tree.add(1); 39 | tree.add(2); 40 | expect(tree.inOrderTreeWalk(function (pv, cv) { 41 | pv.push(cv); 42 | return pv; 43 | }, [])).toEqual([1, 2, 4, 5, 6]); 44 | }); 45 | 46 | it("should be able to walk the binary tree that has object values in order", function () { 47 | const tree = new BinaryTree((a, b) => a.amount > b.amount); 48 | tree.add({amount: 6}); 49 | tree.add({amount: 4}); 50 | tree.add({amount: 5}); 51 | tree.add({amount: 1}); 52 | tree.add({amount: 2}); 53 | expect(tree.inOrderTreeWalk(function (pv, cv) { 54 | pv.push(cv.amount); 55 | return pv; 56 | }, [])).toEqual([1, 2, 4, 5, 6]); 57 | }); 58 | 59 | 60 | it("should be able to walk the binary tree in order", function () { 61 | const tree = new BinaryTree((a, b) => a > b); 62 | tree.add(5); 63 | tree.add(6); 64 | tree.add(4); 65 | tree.add(1); 66 | tree.add(2); 67 | expect(tree.reverseTreeWalk(function (pv, cv) { 68 | pv.push(cv); 69 | return pv; 70 | }, [])).toEqual([6, 5, 4, 2, 1]); 71 | }); 72 | 73 | it("should be able to walk the binary tree in reverseorder", function () { 74 | const tree = new BinaryTree((a, b) => a.amount > b.amount); 75 | tree.add({amount: 6}); 76 | tree.add({amount: 4}); 77 | tree.add({amount: 5}); 78 | tree.add({amount: 1}); 79 | tree.add({amount: 2}); 80 | expect(tree.reverseTreeWalk(function (pv, cv) { 81 | pv.push(cv.amount); 82 | return pv; 83 | }, [])).toEqual([6, 5, 4, 2, 1]); 84 | }); 85 | 86 | describe(" search", function () { 87 | it("should return null when no root", function () { 88 | const tree = new BinaryTree((a, b) => a > b); 89 | expect(tree.search(5)).toBeNull(); 90 | }); 91 | 92 | it("should return element when found", function () { 93 | const tree = new BinaryTree((a, b) => a > b); 94 | tree.add(5); 95 | expect(tree.search(5).value).toEqual(5); 96 | tree.add(6); 97 | expect(tree.search(6).value).toEqual(6); 98 | }); 99 | 100 | it("should return null when no element was found", function () { 101 | const tree = new BinaryTree((a, b) => a > b); 102 | tree.add(5); 103 | expect(tree.search(23)).toBeNull(); 104 | tree.add(6); 105 | expect(tree.search(16)).toBeNull(); 106 | }); 107 | }); 108 | 109 | it("should be able to get max value", function () { 110 | const tree = new BinaryTree((a, b) => a > b); 111 | expect(tree.min()).toBeNull(); 112 | tree.add(1); 113 | tree.add(2); 114 | tree.add(4); 115 | tree.add(5); 116 | expect(tree.min().value).toEqual(1); 117 | }); 118 | 119 | it("should be able to get min value", function () { 120 | const tree = new BinaryTree((a, b) => a > b); 121 | expect(tree.max()).toBeNull(); 122 | tree.add(1); 123 | tree.add(2); 124 | tree.add(4); 125 | tree.add(5); 126 | expect(tree.max().value).toEqual(5); 127 | }); 128 | 129 | it("should be able to get node successor", function () { 130 | const tree = new BinaryTree((a, b) => a > b); 131 | tree.add(1); 132 | tree.add(2); 133 | tree.add(4); 134 | tree.add(5); 135 | expect(tree.successor(2).value).toEqual(4); 136 | }); 137 | 138 | it("should be able to get node successor when it doesn\"t have right child", function () { 139 | const tree = new BinaryTree((a, b) => a > b); 140 | tree.add(1); 141 | tree.add(10); 142 | tree.add(9); 143 | tree.add(8); 144 | expect(tree.successor(8).value).toEqual(9); 145 | expect(tree.successor(9).value).toEqual(10); 146 | }); 147 | 148 | it("should return null for node successor if tree is empty", function () { 149 | const tree = new BinaryTree((a, b) => a > b); 150 | expect(tree.successor(2)).toBeNull(); 151 | }); 152 | 153 | it("should return null if there is no successor", function () { 154 | const tree = new BinaryTree((a, b) => a > b); 155 | tree.add(1); 156 | expect(tree.successor(1)).toBeNull(); 157 | tree.add(2); 158 | tree.add(4); 159 | tree.add(5); 160 | expect(tree.successor(5)).toBeNull(); 161 | expect(tree.successor(4).value).toEqual(5); 162 | }); 163 | 164 | describe("remove", function () { 165 | it(`should return false if node was not found`, function () { 166 | const tree = new BinaryTree(); 167 | expect(tree.remove(1)).toEqual(false); 168 | }); 169 | 170 | it(`should remove node if it doesn"t have children`, function () { 171 | const tree = new BinaryTree(); 172 | tree.add(1); 173 | expect(tree.search(1)).not.toBeNull(); 174 | tree.remove(1); 175 | expect(tree.search(1)).toBeNull(); 176 | }); 177 | 178 | it(`should move the left child up if the node to remove has only left child and no right child`, function () { 179 | const tree = new BinaryTree(); 180 | tree.add(2); 181 | tree.add(1); 182 | expect(tree.search(2)).not.toBeNull(); 183 | tree.remove(2); 184 | expect(tree.search(2)).toBeNull(); 185 | expect(tree.root.value).toEqual(1); 186 | }); 187 | 188 | it(`should move the right child up if the node to remove has only right child and no left child`, function () { 189 | const tree = new BinaryTree(); 190 | tree.add(1); 191 | tree.add(2); 192 | expect(tree.search(1)).not.toBeNull(); 193 | tree.remove(1); 194 | expect(tree.search(1)).toBeNull(); 195 | expect(tree.root.value).toEqual(2); 196 | }); 197 | }); 198 | }); -------------------------------------------------------------------------------- /ts/BinaryTree/RedBlackTree.ts: -------------------------------------------------------------------------------- 1 | import IBinaryTree from "../Interfaces/IBinaryTree"; 2 | import RedBlackTreeNode from "./RedBlackTreeNode"; 3 | class RedBlackTree implements IBinaryTree> { 4 | 5 | public comparator: (a: T, b: T) => boolean; 6 | public root: RedBlackTreeNode = RedBlackTreeNode.sentinel; 7 | 8 | private sentinel: RedBlackTreeNode = RedBlackTreeNode.sentinel; 9 | 10 | constructor(comparator: (a: T, b: T) => boolean = (a, b) => a > b) { 11 | this.comparator = comparator; 12 | } 13 | 14 | public add(value: T): void { 15 | let parent: RedBlackTreeNode = this.sentinel; 16 | let currentLoopNode: RedBlackTreeNode = this.root; 17 | const newNode: RedBlackTreeNode = new RedBlackTreeNode(value, RedBlackTreeNode.sentinel, false); 18 | while (currentLoopNode.isNotSentinel()) { 19 | parent = currentLoopNode; 20 | if (this.comparator(currentLoopNode.value, newNode.value)) { 21 | currentLoopNode = currentLoopNode.left; 22 | } else { 23 | currentLoopNode = currentLoopNode.right; 24 | } 25 | } 26 | newNode.parent = parent; 27 | if (parent.isSentinel()) { 28 | this.root = newNode; 29 | } else if (this.comparator(parent.value, newNode.value)) { 30 | parent.left = newNode; 31 | } else { 32 | parent.right = newNode; 33 | } 34 | this.addFixup(newNode); 35 | } 36 | public remove(value: T): boolean { 37 | return false; 38 | } 39 | public inOrderTreeWalk(callback: (pv: any, cv: T) => any, initialValue: any): any { 40 | return this.inorderNodeWalk(this.root, callback, initialValue); 41 | } 42 | 43 | public isEmpty() { 44 | return this.root === RedBlackTreeNode.sentinel; 45 | } 46 | 47 | public max(): RedBlackTreeNode { 48 | const currentNode: RedBlackTreeNode = this.root; 49 | if (this.isEmpty()) { 50 | return null; 51 | } 52 | return currentNode.max(); 53 | } 54 | 55 | public min(): RedBlackTreeNode { 56 | const currentNode: RedBlackTreeNode = this.root; 57 | if (this.isEmpty()) { 58 | return null; 59 | } 60 | return currentNode.min(); 61 | } 62 | public reverseTreeWalk(callback: (pv: any, cv: T) => any, initialValue: any): any { 63 | return this.reverseNodeWalk(this.root, callback, initialValue); 64 | } 65 | 66 | public search(value: T): RedBlackTreeNode { 67 | let currentNode: RedBlackTreeNode = this.root; 68 | while (currentNode !== this.sentinel && currentNode.value !== value) { 69 | if (this.comparator(currentNode.value, value)) { 70 | currentNode = currentNode.left; 71 | } else { 72 | currentNode = currentNode.right; 73 | } 74 | } 75 | 76 | return currentNode === this.sentinel ? null : currentNode; 77 | } 78 | 79 | public successor (value: T) { 80 | let node = this.search(value); 81 | let ancestor: RedBlackTreeNode; 82 | if (node === null || value === null) { 83 | return null; 84 | } 85 | if (node.right !== this.sentinel) { 86 | return node.right.min(); 87 | } 88 | ancestor = node.parent; 89 | while (ancestor !== this.sentinel && ancestor.right === node) { 90 | node = ancestor; 91 | ancestor = node.parent; 92 | } 93 | return ancestor === this.sentinel ? null : ancestor; 94 | } 95 | 96 | private addFixup(node: RedBlackTreeNode) { 97 | while (node.parent.isRed()) { 98 | if (node.parent.isLeftChild()) { 99 | let parentSibling = node.parent.parent.right; 100 | if (parentSibling.isRed()) { 101 | node.parent.setBlack(); 102 | parentSibling.setBlack(); 103 | node.parent.parent.setRed(); 104 | node = node.parent.parent; 105 | } else { 106 | if (node.isRightChild()) { 107 | node = node.parent; 108 | this.leftRotate(node); 109 | } 110 | node.parent.setBlack(); 111 | node.parent.parent.setRed(); 112 | this.rightRotate(node.parent.parent) 113 | } 114 | } else { 115 | let parentSibling = node.parent.parent.left; 116 | if (parentSibling.isRed()) { 117 | node.parent.setBlack(); 118 | parentSibling.setBlack(); 119 | node.parent.parent.setRed(); 120 | node = node.parent.parent; 121 | } else { 122 | if (node.isLeftChild()) { 123 | node = node.parent; 124 | this.rightRotate(node); 125 | } 126 | node.parent.setBlack(); 127 | node.parent.parent.setRed(); 128 | this.leftRotate(node.parent.parent) 129 | } 130 | } 131 | } 132 | this.root.setBlack(); 133 | } 134 | 135 | private inorderNodeWalk(node: RedBlackTreeNode, callback: (pv: any, cv: T) => any, previousValue: any) { 136 | if (node !== this.sentinel) { 137 | previousValue = this.inorderNodeWalk(node.left, callback, previousValue); 138 | previousValue = callback(previousValue, node.value); 139 | previousValue = this.inorderNodeWalk(node.right, callback, previousValue); 140 | return previousValue; 141 | } else { 142 | return previousValue; 143 | } 144 | } 145 | 146 | private leftRotate(node: RedBlackTreeNode) { 147 | const promotedNode: RedBlackTreeNode = node.right; 148 | node.right = promotedNode.left; 149 | if (promotedNode.left !== this.sentinel) { 150 | promotedNode.left.parent = node; 151 | } 152 | promotedNode.parent = node.parent; 153 | if (node.parent === this.sentinel) { 154 | this.root = promotedNode; 155 | } else if (node.isLeftChild()) { 156 | node.parent.left = promotedNode; 157 | } else { 158 | node.parent.right = promotedNode; 159 | } 160 | 161 | promotedNode.left = node; 162 | node.parent = promotedNode; 163 | } 164 | 165 | private reverseNodeWalk(node: RedBlackTreeNode, callback: (pv: any, cv: T) => any, previousValue: any) { 166 | if (node !== this.sentinel) { 167 | previousValue = this.reverseNodeWalk(node.right, callback, previousValue); 168 | previousValue = callback(previousValue, node.value); 169 | previousValue = this.reverseNodeWalk(node.left, callback, previousValue); 170 | return previousValue; 171 | } else { 172 | return previousValue; 173 | } 174 | } 175 | 176 | private rightRotate(node: RedBlackTreeNode) { 177 | const promotedNode: RedBlackTreeNode = node.left; 178 | node.left = promotedNode.right; 179 | if (promotedNode.right !== this.sentinel) { 180 | promotedNode.right.parent = node; 181 | } 182 | promotedNode.parent = node.parent; 183 | if (node.parent === this.sentinel) { 184 | this.root = promotedNode; 185 | } else if (node.isRightChild()) { 186 | node.parent.right = promotedNode; 187 | } else { 188 | node.parent.left = promotedNode; 189 | } 190 | 191 | promotedNode.right = node; 192 | node.parent = promotedNode; 193 | } 194 | } 195 | 196 | export default RedBlackTree; -------------------------------------------------------------------------------- /tests/RedBlackTreeSpec.ts: -------------------------------------------------------------------------------- 1 | import {} from "../ts/BinaryTree/RedBlackTree"; 2 | import RedBlackTree from "../ts/BinaryTree/RedBlackTree"; 3 | 4 | describe("RedBlackTree", function () { 5 | describe("add" , function () { 6 | 7 | it("should be able to add node", function () { 8 | const tree = new RedBlackTree(); 9 | tree.add(2); 10 | expect(tree.root.value).toEqual(2); 11 | tree.add(1); 12 | expect(tree.root.left.value).toEqual(1); 13 | expect(tree.root.value).toEqual(2); 14 | }); 15 | it("should be able to add node", function () { 16 | const tree = new RedBlackTree((a, b) => a > b); 17 | tree.add(5); 18 | expect(tree.root.value).toEqual(5); 19 | tree.add(6); 20 | expect(tree.root.right.value).toEqual(6); 21 | tree.add(4); 22 | expect(tree.root.left.value).toEqual(4); 23 | }); 24 | }); 25 | 26 | it("should be able to add node to binary tree with object values", function () { 27 | const tree = new RedBlackTree((a, b) => a.amount > b.amount); 28 | tree.add({amount : 5}); 29 | expect(tree.root.value).toEqual({amount : 5}); 30 | tree.add({amount : 6}); 31 | expect(tree.root.right.value).toEqual({amount : 6}); 32 | tree.add({amount : 4}); 33 | expect(tree.root.left.value).toEqual({amount : 4}); 34 | }); 35 | 36 | it("should be able to walk the binary tree in order", function () { 37 | const tree = new RedBlackTree((a, b) => a > b); 38 | tree.add(5); 39 | tree.add(6); 40 | tree.add(4); 41 | tree.add(1); 42 | tree.add(2); 43 | expect(tree.inOrderTreeWalk(function (pv: number[], cv: number) { 44 | pv.push(cv); 45 | return pv; 46 | }, [])).toEqual([1, 2, 4, 5, 6]); 47 | }); 48 | 49 | it("should be able to walk the binary tree that has object values in order", function () { 50 | const tree = new RedBlackTree((a, b) => a.amount > b.amount); 51 | tree.add({amount: 6}); 52 | tree.add({amount: 4}); 53 | tree.add({amount: 5}); 54 | tree.add({amount: 1}); 55 | tree.add({amount: 2}); 56 | expect(tree.inOrderTreeWalk(function (pv: number[], cv: any) { 57 | pv.push(cv.amount); 58 | return pv; 59 | }, [])).toEqual([1, 2, 4, 5, 6]); 60 | }); 61 | 62 | 63 | it("should be able to walk the binary tree in order", function () { 64 | const tree = new RedBlackTree((a, b) => a > b); 65 | tree.add(5); 66 | tree.add(6); 67 | tree.add(4); 68 | tree.add(1); 69 | tree.add(2); 70 | expect(tree.reverseTreeWalk(function (pv: number[], cv: number) { 71 | pv.push(cv); 72 | return pv; 73 | }, [])).toEqual([6, 5, 4, 2, 1]); 74 | }); 75 | 76 | it("should be able to walk the binary tree in reverseorder", function () { 77 | const tree = new RedBlackTree((a, b) => a.amount > b.amount); 78 | tree.add({amount: 6}); 79 | tree.add({amount: 4}); 80 | tree.add({amount: 5}); 81 | tree.add({amount: 1}); 82 | tree.add({amount: 2}); 83 | expect(tree.reverseTreeWalk(function (pv: number[], cv: any) { 84 | pv.push(cv.amount); 85 | return pv; 86 | }, [])).toEqual([6, 5, 4, 2, 1]); 87 | }); 88 | 89 | describe(" search", function () { 90 | it("should return null when no root", function () { 91 | const tree = new RedBlackTree((a, b) => a > b); 92 | expect(tree.search(5)).toBeNull(); 93 | }); 94 | 95 | it("should return element when found", function () { 96 | const tree = new RedBlackTree((a, b) => a > b); 97 | tree.add(5); 98 | expect(tree.search(5).value).toEqual(5); 99 | tree.add(6); 100 | expect(tree.search(6).value).toEqual(6); 101 | }); 102 | 103 | it("should return null when no element was found", function () { 104 | const tree = new RedBlackTree((a, b) => a > b); 105 | tree.add(5); 106 | expect(tree.search(23)).toBeNull(); 107 | tree.add(6); 108 | expect(tree.search(16)).toBeNull(); 109 | }); 110 | }); 111 | 112 | it("should be able to get min value", function () { 113 | const tree = new RedBlackTree((a, b) => a > b); 114 | expect(tree.min()).toBeNull(); 115 | tree.add(1); 116 | tree.add(2); 117 | tree.add(4); 118 | tree.add(5); 119 | expect(tree.min().value).toEqual(1); 120 | }); 121 | 122 | it("should be able to get min value", function () { 123 | const tree = new RedBlackTree((a, b) => a > b); 124 | expect(tree.max()).toBeNull(); 125 | tree.add(1); 126 | tree.add(2); 127 | tree.add(4); 128 | tree.add(5); 129 | expect(tree.max().value).toEqual(5); 130 | }); 131 | 132 | it("should be able to get node successor", function () { 133 | const tree = new RedBlackTree((a, b) => a > b); 134 | tree.add(1); 135 | tree.add(2); 136 | tree.add(4); 137 | tree.add(5); 138 | expect(tree.successor(2).value).toEqual(4); 139 | }); 140 | 141 | it("should be able to get node successor when it doesn\"t have right child", function () { 142 | const tree = new RedBlackTree((a, b) => a > b); 143 | tree.add(1); 144 | tree.add(10); 145 | tree.add(9); 146 | tree.add(8); 147 | expect(tree.successor(8).value).toEqual(9); 148 | expect(tree.successor(9).value).toEqual(10); 149 | }); 150 | 151 | it("should return null for node successor if tree is empty", function () { 152 | const tree = new RedBlackTree((a, b) => a > b); 153 | expect(tree.successor(2)).toBeNull(); 154 | }); 155 | 156 | it("should return null if there is no successor", function () { 157 | const tree = new RedBlackTree((a, b) => a > b); 158 | tree.add(1); 159 | expect(tree.successor(1)).toBeNull(); 160 | tree.add(2); 161 | tree.add(4); 162 | tree.add(5); 163 | expect(tree.successor(5)).toBeNull(); 164 | expect(tree.successor(4).value).toEqual(5); 165 | }); 166 | 167 | xdescribe("remove", function () { 168 | it(`should return false if node was not found`, function () { 169 | const tree = new RedBlackTree(); 170 | expect(tree.remove(1)).toEqual(false); 171 | }); 172 | 173 | it(`should remove node if it doesn"t have children`, function () { 174 | const tree = new RedBlackTree(); 175 | tree.add(1); 176 | expect(tree.search(1)).not.toBeNull(); 177 | tree.remove(1); 178 | expect(tree.search(1)).toBeNull(); 179 | }); 180 | 181 | it(`should move the left child up if the node to remove has only left child and no right child`, function () { 182 | const tree = new RedBlackTree(); 183 | tree.add(2); 184 | tree.add(1); 185 | expect(tree.search(2)).not.toBeNull(); 186 | tree.remove(2); 187 | expect(tree.search(2)).toBeNull(); 188 | expect(tree.root.value).toEqual(1); 189 | }); 190 | 191 | it(`should move the right child up if the node to remove has only right child and no left child`, function () { 192 | const tree = new RedBlackTree(); 193 | tree.add(1); 194 | tree.add(2); 195 | expect(tree.search(1)).not.toBeNull(); 196 | tree.remove(1); 197 | expect(tree.search(1)).toBeNull(); 198 | expect(tree.root.value).toEqual(2); 199 | }); 200 | }); 201 | }); -------------------------------------------------------------------------------- /tests/BitMatrixSpec.ts: -------------------------------------------------------------------------------- 1 | import {BitMatrix} from "../ts/BitMatrix"; 2 | 3 | describe("BitMatrix", function () { 4 | it("be able to set bits", function () { 5 | const bm = new BitMatrix(100, 100); 6 | bm.set(1, 1, true); 7 | bm.set(99, 0, true); 8 | bm.set(99, 1, true); 9 | bm.set(99, 99, true); 10 | expect(bm.get(0, 1)).toEqual(false); 11 | expect(bm.get(1, 1)).toEqual(true); 12 | expect(bm.get(54, 34)).toEqual(false); 13 | expect(bm.get(99, 0)).toEqual(true); 14 | expect(bm.get(99, 1)).toEqual(true); 15 | expect(bm.get(99, 99)).toEqual(true); 16 | }); 17 | 18 | it("be able to get set bits count", function () { 19 | const bm = new BitMatrix(100, 100); 20 | bm.set(1, 1, true); 21 | bm.set(99, 1, true); 22 | expect(bm.count()).toEqual(2); 23 | bm.set(99, 1, false); 24 | expect(bm.count()).toEqual(1); 25 | }); 26 | 27 | it("be able to get set bits per row", function () { 28 | const bm = new BitMatrix(100, 100); 29 | bm.set(1, 2, true); 30 | bm.set(99, 3, true); 31 | expect(bm.getIndexes()[0]).toEqual([]); 32 | expect(bm.getIndexes()[1]).toEqual([2]); 33 | expect(bm.getIndexes()[99]).toEqual([3]); 34 | bm.set(29, 4, true); 35 | expect(bm.getIndexes()[0]).toEqual([]); 36 | expect(bm.getIndexes()[1]).toEqual([2]); 37 | expect(bm.getIndexes()[29]).toEqual([4]); 38 | expect(bm.getIndexes()[99]).toEqual([3]); 39 | bm.set(29, 4, false); 40 | expect(bm.getIndexes()[0]).toEqual([]); 41 | expect(bm.getIndexes()[1]).toEqual([2]); 42 | expect(bm.getIndexes()[29]).toEqual([]); 43 | expect(bm.getIndexes()[99]).toEqual([3]); 44 | }); 45 | 46 | it("be able to get set bits per column", function () { 47 | const bm = new BitMatrix(100, 100); 48 | bm.set(2, 1, true); 49 | bm.set(3, 99, true); 50 | expect(bm.getIndexes(true)[0]).toEqual([]); 51 | expect(bm.getIndexes(true)[1]).toEqual([2]); 52 | expect(bm.getIndexes(true)[99]).toEqual([3]); 53 | bm.set(4, 29, true); 54 | expect(bm.getIndexes(true)[0]).toEqual([]); 55 | expect(bm.getIndexes(true)[1]).toEqual([2]); 56 | expect(bm.getIndexes(true)[29]).toEqual([4]); 57 | expect(bm.getIndexes(true)[99]).toEqual([3]); 58 | bm.set(4, 29, false); 59 | expect(bm.getIndexes(true)[0]).toEqual([]); 60 | expect(bm.getIndexes(true)[1]).toEqual([2]); 61 | expect(bm.getIndexes(true)[29]).toEqual([]); 62 | expect(bm.getIndexes(true)[99]).toEqual([3]); 63 | }); 64 | 65 | it("should throw error if trying to set bit out of bounds", function () { 66 | const bm = new BitMatrix(100, 100); 67 | expect(() => bm.set(1, 1, true)).not.toThrowError(); 68 | expect(() => bm.set(100, 100, true)).toThrowError(); 69 | expect(() => bm.set(100, 1, true)).toThrowError(); 70 | expect(() => bm.set(101, 0, true)).toThrowError(); 71 | expect(() => bm.set(101, 0, true)).toThrowError(); 72 | expect(() => bm.set(-1, 1, true)).toThrowError(); 73 | expect(() => bm.set(-1, -1, true)).toThrowError(); 74 | expect(() => bm.set(1, -1, true)).toThrowError(); 75 | }); 76 | 77 | it("should be able to zero all values", function () { 78 | const bs = new BitMatrix(100, 100); 79 | bs.set(0, 1, true); 80 | bs.set(0, 99, true); 81 | bs.set(57, 1, true); 82 | bs.set(57, 99, true); 83 | bs.set(99, 1, true); 84 | bs.set(99, 99, true); 85 | expect(bs.get(0, 0)).toEqual(false); 86 | expect(bs.get(0, 1)).toEqual(true); 87 | expect(bs.get(0, 54)).toEqual(false); 88 | expect(bs.get(0, 99)).toEqual(true); 89 | expect(bs.get(57, 0)).toEqual(false); 90 | expect(bs.get(57, 1)).toEqual(true); 91 | expect(bs.get(57, 54)).toEqual(false); 92 | expect(bs.get(57, 99)).toEqual(true); 93 | expect(bs.get(99, 0)).toEqual(false); 94 | expect(bs.get(99, 1)).toEqual(true); 95 | expect(bs.get(99, 54)).toEqual(false); 96 | expect(bs.get(99, 99)).toEqual(true); 97 | bs.reset(); 98 | expect(bs.get(0, 0)).toEqual(false); 99 | expect(bs.get(0, 1)).toEqual(false); 100 | expect(bs.get(0, 54)).toEqual(false); 101 | expect(bs.get(0, 99)).toEqual(false); 102 | expect(bs.get(57, 0)).toEqual(false); 103 | expect(bs.get(57, 1)).toEqual(false); 104 | expect(bs.get(57, 54)).toEqual(false); 105 | expect(bs.get(57, 99)).toEqual(false); 106 | expect(bs.get(99, 0)).toEqual(false); 107 | expect(bs.get(99, 1)).toEqual(false); 108 | expect(bs.get(99, 54)).toEqual(false); 109 | expect(bs.get(99, 99)).toEqual(false); 110 | }); 111 | 112 | describe("resize", function () { 113 | it("should be able to extend the BitMatrix", function () { 114 | const bm = new BitMatrix(100, 110); 115 | bm.set(1, 1, true); 116 | bm.set(1, 2, true); 117 | bm.set(1, 99, true); 118 | bm.resize(300, 302); 119 | expect(bm.get(1, 300)).toEqual(false); 120 | expect(bm.get(1, 301)).toEqual(false); 121 | bm.set(151, 300, true); 122 | bm.set(151, 301, true); 123 | expect(bm.get(151, 300)).toEqual(true); 124 | expect(bm.get(151, 301)).toEqual(true); 125 | }); 126 | 127 | it("should be able to shrink the BitMatrix", function () { 128 | const bm = new BitMatrix(100, 100); 129 | bm.set(23, 1, true); 130 | bm.set(23, 2, true); 131 | bm.set(23, 99, true); 132 | bm.resize(55, 51); 133 | expect(bm.get(23, 49)).toEqual(false); 134 | expect(bm.get(23, 50)).toEqual(false); 135 | bm.set(23, 50, true); 136 | expect(bm.get(23, 49)).toEqual(false); 137 | expect(bm.get(23, 50)).toEqual(true); 138 | }); 139 | 140 | it("nothing should be affected if the size doesn't change", function () { 141 | const bm = new BitMatrix(100, 100); 142 | bm.set(1, 1, true); 143 | bm.set(2, 1, true); 144 | bm.set(99, 1, true); 145 | bm.resize(100, 100); 146 | expect(bm.get(0, 1)).toEqual(false); 147 | expect(bm.get(1, 1)).toEqual(true); 148 | expect(bm.get(54, 1)).toEqual(false); 149 | expect(bm.get(99, 1)).toEqual(true); 150 | }); 151 | 152 | it("should throw error if index is invalid", function () { 153 | const bm = new BitMatrix(1, 1); 154 | expect(() => bm.resize(-1, 10)).toThrowError(); 155 | expect(() => bm.resize(-1, -1)).toThrowError(); 156 | expect(() => bm.resize(11, -1)).toThrowError(); 157 | }); 158 | }); 159 | 160 | describe("splice column", function () { 161 | it("should remove number of elements", function () { 162 | const bs = new BitMatrix(70, 100); 163 | bs.set(1, 1, true); 164 | bs.set(1, 2, true); 165 | bs.set(1, 5, true); 166 | bs.set(1, 6, true); 167 | bs.set(1, 99, true); 168 | bs.spliceColumn(2, 3); 169 | expect(bs.size()).toEqual([70, 97]); 170 | expect(bs.get(1, 1)).toEqual(true); 171 | expect(bs.get(1, 2)).toEqual(true); 172 | expect(bs.get(1, 3)).toEqual(true); 173 | expect(bs.get(1, 5)).toEqual(false); 174 | expect(bs.get(1, 96)).toEqual(true); 175 | }); 176 | 177 | it("should be able to provide negative start index", function () { 178 | const bs = new BitMatrix(60, 100); 179 | bs.set(1, 1, true); 180 | bs.set(1, 2, true); 181 | bs.set(1, 5, true); 182 | bs.set(1, 6, true); 183 | bs.set(1, 99, true); 184 | bs.spliceColumn(-98, 3); 185 | expect(bs.size()).toEqual([60, 97]); 186 | expect(bs.get(1, 1)).toEqual(true); 187 | expect(bs.get(1, 2)).toEqual(true); 188 | expect(bs.get(1, 3)).toEqual(true); 189 | expect(bs.get(1, 5)).toEqual(false); 190 | expect(bs.get(1, 96)).toEqual(true); 191 | }); 192 | 193 | it("delete is NaN or less than 1 do nothing", function () { 194 | const bm = new BitMatrix(100, 100); 195 | bm.spliceColumn(-98, -3); 196 | expect(bm.size()).toEqual([100, 100]); 197 | bm.spliceColumn(-98, Number.NaN); 198 | expect(bm.size()).toEqual([100, 100]); 199 | }); 200 | 201 | it("should throw error if start index is greater than array length", function () { 202 | const bm = new BitMatrix(100, 100); 203 | expect(() => bm.spliceColumn(101, 2)).toThrowError(); 204 | }); 205 | 206 | it("should splice from 0 if negative value passed and it is greater than array length by abs value", function () { 207 | const bm = new BitMatrix(100, 100); 208 | expect(() => bm.spliceColumn(-101, 2)).toThrowError(); 209 | }); 210 | }); 211 | 212 | 213 | describe("splice row", function () { 214 | it("should remove number of elements", function () { 215 | const bs = new BitMatrix(100, 100); 216 | bs.set(1, 1, true); 217 | bs.set(2, 1, true); 218 | bs.set(5, 1, true); 219 | bs.set(6, 1, true); 220 | bs.set(99, 1, true); 221 | bs.spliceRow(2, 3); 222 | expect(bs.size()).toEqual([97, 100]); 223 | expect(bs.get( 1, 1)).toEqual(true); 224 | expect(bs.get( 2, 1)).toEqual(true); 225 | expect(bs.get( 3, 1)).toEqual(true); 226 | expect(bs.get( 5, 1)).toEqual(false); 227 | expect(bs.get( 96, 1)).toEqual(true); 228 | }); 229 | 230 | it("should be able to provide negative start index", function () { 231 | const bs = new BitMatrix(100, 100); 232 | bs.set(1, 1, true); 233 | bs.set(2, 1, true); 234 | bs.set(5, 1, true); 235 | bs.set(6, 1, true); 236 | bs.set(99, 1, true); 237 | bs.spliceRow(-98, 3); 238 | expect(bs.size()).toEqual([97, 100]); 239 | expect(bs.get( 1, 1)).toEqual(true); 240 | expect(bs.get( 2, 1)).toEqual(true); 241 | expect(bs.get( 3, 1)).toEqual(true); 242 | expect(bs.get( 5, 1)).toEqual(false); 243 | expect(bs.get( 96, 1)).toEqual(true); 244 | }); 245 | 246 | it("delete is NaN or less than 1 do nothing", function () { 247 | const bm = new BitMatrix(100, 100); 248 | bm.spliceRow(-98, -3); 249 | expect(bm.size()).toEqual([100, 100]); 250 | bm.spliceRow(-98, Number.NaN); 251 | expect(bm.size()).toEqual([100, 100]); 252 | }); 253 | 254 | it("should throw error if start index is greater than array length", function () { 255 | const bm = new BitMatrix(100, 100); 256 | expect(() => bm.spliceRow(101, 2)).toThrowError(); 257 | }); 258 | 259 | it("should splice from 0 if negative value passed and it is greater than array length by abs value", function () { 260 | const bm = new BitMatrix(100, 100); 261 | expect(() => bm.spliceRow(-101, 2)).toThrowError(); 262 | }); 263 | }); 264 | 265 | it("getColIndexes should return set indexes for column", function () { 266 | const bs = new BitMatrix(100, 70); 267 | bs.set(1, 1, true); 268 | bs.set(2, 1, true); 269 | bs.set(5, 1, true); 270 | bs.set(6, 1, true); 271 | bs.set(99, 1, true); 272 | bs.set(6, 31, true); 273 | bs.set(31, 31, true); 274 | bs.set(6, 32, true); 275 | bs.set(32, 32, true); 276 | expect(bs.getColIndexes(1)).toEqual([1, 2, 5, 6, 99]); 277 | expect(bs.getColIndexes(31)).toEqual([6, 31]); 278 | expect(bs.getColIndexes(32)).toEqual([6, 32]); 279 | }); 280 | 281 | it("getRowIndexes should return set indexes for column", function () { 282 | const bs = new BitMatrix(70, 100); 283 | bs.set(1, 1, true); 284 | bs.set(1, 2, true); 285 | bs.set(1, 5, true); 286 | bs.set(1, 6, true); 287 | bs.set(1, 99, true); 288 | bs.set(31, 6, true); 289 | bs.set(31, 31, true); 290 | bs.set(32, 6, true); 291 | bs.set(32, 32, true); 292 | expect(bs.getRowIndexes(1)).toEqual([1, 2, 5, 6, 99]); 293 | expect(bs.getRowIndexes(31)).toEqual([6, 31]); 294 | expect(bs.getRowIndexes(32)).toEqual([6, 32]); 295 | }); 296 | it("be able to get set bits per column when column count < row count", function () { 297 | const bm = new BitMatrix(100, 10); 298 | bm.set(2, 1, true); 299 | bm.set(3, 9, true); 300 | expect(bm.getIndexes(true)[0]).toEqual([]); 301 | expect(bm.getIndexes(true)[1]).toEqual([2]); 302 | expect(bm.getIndexes(true)[9]).toEqual([3]); 303 | bm.set(4, 8, true); 304 | expect(bm.getIndexes(true)[0]).toEqual([]); 305 | expect(bm.getIndexes(true)[1]).toEqual([2]); 306 | expect(bm.getIndexes(true)[8]).toEqual([4]); 307 | expect(bm.getIndexes(true)[9]).toEqual([3]); 308 | bm.set(4, 8, false); 309 | expect(bm.getIndexes(true)[0]).toEqual([]); 310 | expect(bm.getIndexes(true)[1]).toEqual([2]); 311 | expect(bm.getIndexes(true)[8]).toEqual([]); 312 | expect(bm.getIndexes(true)[9]).toEqual([3]); 313 | }); 314 | }); --------------------------------------------------------------------------------