UNPKG

data-structure-typed

Version:
1,053 lines (900 loc) 33.6 kB
import { DirectedEdge, DirectedGraph, DirectedVertex, VertexKey } from '../../../../src'; describe('DirectedGraph Operation Test', () => { let graph: DirectedGraph; beforeEach(() => { graph = new DirectedGraph(); }); it('should add vertexMap', () => { const vertex1 = new DirectedVertex('A'); const vertex2 = new DirectedVertex('B'); graph.addVertex(vertex1); graph.addVertex(vertex2); expect(graph.hasVertex(vertex1)).toBe(true); expect(graph.hasVertex(vertex2)).toBe(true); }); it('should add edges', () => { const vertex1 = new DirectedVertex('A'); const vertex2 = new DirectedVertex('B'); const edge = new DirectedEdge('A', 'B'); edge.src = edge.src; edge.dest = edge.dest; graph.addVertex(vertex1); graph.addVertex(vertex2); graph.addEdge(edge); expect(graph.outEdgeMap.size).toBe(1); expect(graph.inEdgeMap.size).toBe(1); expect(graph.hasEdge('A', 'B')).toBe(true); expect(graph.hasEdge('B', 'A')).toBe(false); }); it('should delete edges', () => { const vertex1 = new DirectedVertex('A'); // const vertex2 = new DirectedVertex('B'); graph.createVertex('B'); const edge = new DirectedEdge('A', 'B'); graph.addVertex(vertex1); graph.addVertex('B'); graph.addEdge(edge); expect(graph.deleteEdge(edge)).toBe(edge); expect(graph.hasEdge('A', 'B')).toBe(false); }); it('should perform topological sort', () => { const vertexA = new DirectedVertex('A'); const vertexB = new DirectedVertex('B'); const vertexC = new DirectedVertex('C'); const edgeAB = new DirectedEdge('A', 'B'); graph.createEdge('B', 'C'); graph.addVertex(vertexA); graph.addVertex(vertexB); graph.addVertex(vertexC); graph.addEdge(edgeAB); graph.addEdge('B', 'C'); expect(graph.getEdgeSrc(edgeAB)).toBe(vertexA); const topologicalOrder = graph.topologicalSort(); if (topologicalOrder) expect(topologicalOrder).toEqual(['A', 'B', 'C']); graph.deleteEdgesBetween('A', 'B'); const topologicalOrder1 = graph.topologicalSort(); if (topologicalOrder1) expect(topologicalOrder1).toEqual(['B', 'C', 'A']); expect(graph.incomingEdgesOf(vertexC)?.length).toBe(1); expect(graph.degreeOf(vertexA)).toBe(0); expect(graph.inDegreeOf(vertexC)).toBe(1); expect(graph.outDegreeOf(vertexC)).toBe(0); expect(graph.edgesOf(vertexC)?.length).toBe(1); expect(graph.tarjan().dfnMap.size).toBe(3); expect(graph.bellmanFord(vertexC, true, true, true)?.paths.length).toBe(3); expect(graph.getMinPathBetween('B', 'C', true)?.length).toBe(2); expect(graph.setEdgeWeight('B', 'C', 100)).toBe(true); expect(graph.getMinCostBetween('B', 'C', true)).toBe(100); expect(graph.getMinCostBetween('B', 'C')).toBe(1); expect(graph.getAllPathsBetween('B', 'C')?.length).toBe(1); expect(graph.deleteVertex(vertexB)).toBe(true); expect(graph.getAllPathsBetween('B', 'C')?.length).toBe(0); expect(graph.removeManyVertices([vertexB, vertexC])).toBe(true); }); }); class MyVertex<V = any> extends DirectedVertex<V> { constructor(key: VertexKey, value?: V) { super(key, value); this._data = value; } protected _data: V | undefined; get data(): V | undefined { return this._data; } set data(value: V | undefined) { this._data = value; } } class MyEdge<E = any> extends DirectedEdge<E> { constructor(v1: VertexKey, v2: VertexKey, weight?: number, value?: E) { super(v1, v2, weight, value); this._data = value; } protected _data: E | undefined; get data(): E | undefined { return this._data; } set data(value: E | undefined) { this._data = value; } } class MyDirectedGraph< V = any, E = any, VO extends MyVertex<V> = MyVertex<V>, EO extends MyEdge<E> = MyEdge<E> > extends DirectedGraph<V, E, VO, EO> { override createVertex(key: VertexKey, value: V): VO { return new MyVertex(key, value) as VO; } override createEdge(src: VertexKey, dest: VertexKey, weight?: number, value?: E): EO { return new MyEdge(src, dest, weight ?? 1, value) as EO; } } describe('Inherit from DirectedGraph and perform operations', () => { let myGraph = new MyDirectedGraph<string, string>(); beforeEach(() => { myGraph = new MyDirectedGraph(); }); it('Add vertexMap', () => { myGraph.addVertex(1, 'data1'); myGraph.addVertex(2, 'data2'); myGraph.addVertex(3, 'data3'); myGraph.addVertex(4, 'data4'); myGraph.addVertex(5, 'data5'); myGraph.addVertex(new MyVertex(6, 'data6')); myGraph.addVertex(new MyVertex(7, 'data7')); myGraph.addVertex(new MyVertex(8, 'data8')); myGraph.addVertex(new MyVertex(9, 'data9')); }); it('Add edges', () => { myGraph.addVertex(1, 'data1'); myGraph.addVertex(2, 'data2'); myGraph.addEdge(1, 2, 10, 'edge-data1-2'); myGraph.addEdge(new MyEdge(2, 1, 20, 'edge-data2-1')); myGraph.inEdgeMap = myGraph.inEdgeMap; myGraph.outEdgeMap = myGraph.outEdgeMap; expect(myGraph.edgeSet().length).toBe(2); // TODO expect(myGraph.getEdge(1, 2)).toBeInstanceOf(MyEdge); expect(myGraph.getEdge(2, 1)).toBeInstanceOf(MyEdge); }); it('Get edge', () => { myGraph.addVertex(1, 'val1'); myGraph.addVertex(2, 'val1'); myGraph.addEdge(1, 2, 1, 'val1'); const edge1 = myGraph.getEdge(1, 2); const edge2 = myGraph.getEdge(myGraph.getVertex(1), myGraph.getVertex(2)); const edge3 = myGraph.getEdge(1, '100'); // edge1.data has type problem. After the uniform design, the generics of containers (DirectedGraph, BST) are based on the type of value. However, this design has a drawback: when I attempt to inherit from the Vertex or BSTNode classes, the types of the results obtained by all methods are those of the parent class. expect(edge1).toBeInstanceOf(MyEdge); if (edge1) { expect(edge1.data).toBe('val1'); expect(edge1?.value).toBe('val1'); expect(edge1).toBeInstanceOf(MyEdge); expect(edge1.src).toBe(1); expect(edge1).toEqual(edge2); expect(edge3).toBe(undefined); } }); it('Edge set and vertex set', () => { expect(true).toBeTruthy(); }); it('Remove edge between vertexMap', () => { expect(myGraph.isEmpty()).toBe(true); myGraph.addVertex(1, 'data1'); myGraph.addVertex(2, 'data2'); myGraph.addEdge(1, 2, 10, 'edge-data1-2'); expect(myGraph.isEmpty()).toBe(false); const removedEdge = myGraph.deleteEdgeSrcToDest(1, 2); expect(myGraph.deleteEdgeSrcToDest(2, 10)).toBe(undefined); const edgeAfterRemoval = myGraph.getEdge(1, 2); expect(removedEdge).toBeInstanceOf(MyEdge); if (removedEdge) { if (removedEdge) expect(removedEdge.value).toBe('edge-data1-2'); if (removedEdge) expect(removedEdge.src).toBe(1); } expect(edgeAfterRemoval).toBe(undefined); }); it('should clear', () => { expect(myGraph.isEmpty()).toBe(true); myGraph.addVertex(1, 'data1'); myGraph.addVertex(2, 'data2'); myGraph.addEdge(1, 2, 10, 'edge-data1-2'); expect(myGraph.isEmpty()).toBe(false); myGraph.clear(); expect(myGraph.isEmpty()).toBe(true); }); it('should clone', () => { myGraph.addVertex(1, 'data1'); myGraph.addVertex(2, 'data2'); myGraph.addEdge(1, 2, 10, 'edge-data1-2'); const cloned = myGraph.clone(); expect(cloned.hasVertex(1)).toBe(true); expect(cloned.hasEdge(1, 2)).toBe(true); }); it('Topological sort', () => { const sorted = myGraph.topologicalSort(); expect(sorted).toBeInstanceOf(Array); if (sorted && sorted.length > 0) { expect(sorted.length).toBe(9); if (sorted[0] instanceof MyVertex) expect(sorted[0].data).toBe('data9'); if (sorted[3] instanceof MyVertex) expect(sorted[3].data).toBe('data6'); if (sorted[8] instanceof MyVertex) expect(sorted[8].key).toBe(1); } }); it('Minimum path between vertexMap', () => { myGraph.addVertex(new MyVertex(1, 'data1')); myGraph.addVertex(new MyVertex(2, 'data2')); myGraph.addEdge(new MyEdge(1, 2, 10, 'edge-data1-2')); }); it('All paths between vertexMap', () => { // Add vertexMap and edges as needed for this test myGraph.addVertex(new MyVertex(1, 'data1')); myGraph.addVertex(new MyVertex(2, 'data2')); myGraph.addEdge(new MyEdge(1, 2, 10, 'edge-data1-2')); // Add expect statements here to verify the allPaths }); }); describe('Inherit from DirectedGraph and perform operations test2.', () => { const myGraph = new MyDirectedGraph<string, string>(); it('should test graph operations', () => { const vertex1 = new MyVertex(1, 'data1'); const vertex2 = new MyVertex(2, 'data2'); const vertex3 = new MyVertex(3, 'data3'); const vertex4 = new MyVertex(4, 'data4'); const vertex5 = new MyVertex(5, 'data5'); const vertex6 = new MyVertex(6, 'data6'); const vertex7 = new MyVertex(7, 'data7'); const vertex8 = new MyVertex(8, 'data8'); const vertex9 = new MyVertex(9, 'data9'); myGraph.addVertex(vertex1); myGraph.addVertex(vertex2); myGraph.addVertex(vertex3); myGraph.addVertex(vertex4); myGraph.addVertex(vertex5); myGraph.addVertex(vertex6); myGraph.addVertex(vertex7); myGraph.addVertex(vertex8); myGraph.addVertex(vertex9); myGraph.addEdge(new MyEdge(1, 2, 10, 'edge-data1-2')); myGraph.addEdge(new MyEdge(2, 1, 20, 'edge-data2-1')); expect(myGraph.getEdge(1, 2)).toBeTruthy(); expect(myGraph.getEdge(2, 1)).toBeTruthy(); expect(myGraph.getEdge(1, '100')).toBeFalsy(); myGraph.deleteEdgeSrcToDest(1, 2); expect(myGraph.getEdge(1, 2)).toBeFalsy(); myGraph.addEdge(3, 1, 3, 'edge-data-3-1'); myGraph.addEdge(1, 9, 19, 'edge-data1-9'); myGraph.addEdge(9, 7, 97, 'edge-data9-7'); myGraph.addEdge(7, 9, 79, 'edge-data7-9'); myGraph.addEdge(1, 4, 14, 'edge-data1-4'); myGraph.addEdge(4, 7, 47, 'edge-data4-7'); myGraph.addEdge(1, 2, 12, 'edge-data1-2'); myGraph.addEdge(2, 3, 23, 'edge-data2-3'); myGraph.addEdge(3, 5, 35, 'edge-data3-5'); myGraph.addEdge(5, 7, 57, 'edge-data5-7'); myGraph.addEdge(new MyEdge(7, 3, 73, 'edge-data7-3')); const topologicalSorted = myGraph.topologicalSort(); expect(topologicalSorted).toBe(undefined); const minPath1to7 = myGraph.getMinPathBetween(1, 7); expect(minPath1to7).toBeInstanceOf(Array); if (minPath1to7 && minPath1to7.length > 0) { expect(minPath1to7).toHaveLength(3); expect(minPath1to7[0]).toBeInstanceOf(MyVertex); expect(minPath1to7[0].key).toBe(1); expect(minPath1to7[1].key).toBe(9); expect(minPath1to7[2].key).toBe(7); } const fordResult1 = myGraph.bellmanFord(1); expect(fordResult1).toBeTruthy(); expect(fordResult1.hasNegativeCycle).toBeUndefined(); const { distMap, preMap, paths, min, minPath } = fordResult1; expect(distMap).toBeInstanceOf(Map); expect(distMap.size).toBe(9); expect(distMap.get(vertex1)).toBe(0); expect(distMap.get(vertex2)).toBe(12); expect(distMap.get(vertex3)).toBe(35); expect(distMap.get(vertex4)).toBe(14); expect(distMap.get(vertex5)).toBe(70); expect(distMap.get(vertex6)).toBe(Number.MAX_SAFE_INTEGER); expect(distMap.get(vertex7)).toBe(61); expect(distMap.get(vertex8)).toBe(Number.MAX_SAFE_INTEGER); expect(distMap.get(vertex9)).toBe(19); expect(preMap).toBeInstanceOf(Map); expect(preMap.size).toBe(0); expect(paths).toBeInstanceOf(Array); expect(paths.length).toBe(0); expect(min).toBe(Number.MAX_SAFE_INTEGER); expect(minPath).toBeInstanceOf(Array); const floydResult = myGraph.floydWarshall(); expect(floydResult).toBeTruthy(); if (floydResult) { const { costs, predecessor } = floydResult; expect(costs).toBeInstanceOf(Array); expect(costs.length).toBe(9); expect(costs[0]).toEqual([32, 12, 35, 14, 70, Number.MAX_SAFE_INTEGER, 61, Number.MAX_SAFE_INTEGER, 19]); expect(costs[1]).toEqual([20, 32, 23, 34, 58, Number.MAX_SAFE_INTEGER, 81, Number.MAX_SAFE_INTEGER, 39]); expect(costs[2]).toEqual([3, 15, 38, 17, 35, Number.MAX_SAFE_INTEGER, 64, Number.MAX_SAFE_INTEGER, 22]); expect(costs[3]).toEqual([123, 135, 120, 137, 155, Number.MAX_SAFE_INTEGER, 47, Number.MAX_SAFE_INTEGER, 126]); expect(costs[4]).toEqual([133, 145, 130, 147, 165, Number.MAX_SAFE_INTEGER, 57, Number.MAX_SAFE_INTEGER, 136]); expect(costs[5]).toEqual([ Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER ]); expect(costs[6]).toEqual([76, 88, 73, 90, 108, Number.MAX_SAFE_INTEGER, 137, Number.MAX_SAFE_INTEGER, 79]); expect(costs[7]).toEqual([ Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER ]); expect(costs[8]).toEqual([173, 185, 170, 187, 205, Number.MAX_SAFE_INTEGER, 97, Number.MAX_SAFE_INTEGER, 176]); expect(predecessor).toBeInstanceOf(Array); expect(predecessor.length).toBe(9); expect(predecessor[0]).toEqual([ vertex2, undefined, vertex2, undefined, vertex3, undefined, vertex4, undefined, undefined ]); expect(predecessor[1]).toEqual([ undefined, vertex1, undefined, vertex1, vertex3, undefined, vertex4, undefined, vertex1 ]); expect(predecessor[5]).toEqual([ undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined ]); expect(predecessor[7]).toEqual([ undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined ]); expect(predecessor[8]).toEqual([ vertex7, vertex7, vertex7, vertex7, vertex7, undefined, undefined, undefined, vertex7 ]); } const dijkstraRes12tt = myGraph.dijkstra(1, 2, true, true); expect(dijkstraRes12tt).toBeTruthy(); if (dijkstraRes12tt) { const { distMap, minDist, minPath, paths } = dijkstraRes12tt; expect(distMap).toBeInstanceOf(Map); expect(distMap.size).toBe(9); expect(distMap.get(vertex1)).toBe(0); expect(distMap.get(vertex2)).toBe(12); expect(distMap.get(vertex3)).toBe(Number.MAX_SAFE_INTEGER); expect(distMap.get(vertex4)).toBe(14); expect(distMap.get(vertex5)).toBe(Number.MAX_SAFE_INTEGER); expect(distMap.get(vertex6)).toBe(Number.MAX_SAFE_INTEGER); expect(distMap.get(vertex7)).toBe(Number.MAX_SAFE_INTEGER); expect(distMap.get(vertex8)).toBe(Number.MAX_SAFE_INTEGER); expect(distMap.get(vertex9)).toBe(19); expect(minDist).toBe(12); expect(minPath).toBeInstanceOf(Array); expect(minPath.length).toBe(2); expect(minPath[0]).toBe(vertex1); expect(minPath[1]).toBe(vertex2); expect(paths).toBeInstanceOf(Array); expect(paths.length).toBe(9); expect(paths[0]).toBeInstanceOf(Array); expect(paths[0][0]).toBe(vertex1); expect(paths[1]).toBeInstanceOf(Array); expect(paths[1][0]).toBe(vertex1); expect(paths[1][1]).toBe(vertex2); expect(paths[2]).toBeInstanceOf(Array); expect(paths[2][0]).toBe(vertex3); expect(paths[3]).toBeInstanceOf(Array); expect(paths[3][0]).toBe(vertex1); expect(paths[3][1]).toBe(vertex4); expect(paths[4]).toBeInstanceOf(Array); expect(paths[4][0]).toBe(vertex5); expect(paths[5]).toBeInstanceOf(Array); expect(paths[5][0]).toBe(vertex6); expect(paths[6]).toBeInstanceOf(Array); expect(paths[6][0]).toBe(vertex7); expect(paths[7]).toBeInstanceOf(Array); expect(paths[7][0]).toBe(vertex8); expect(paths[8]).toBeInstanceOf(Array); expect(paths[8][0]).toBe(vertex1); expect(paths[8][1]).toBe(vertex9); } const dijkstraRes1ntt = myGraph.dijkstra(1, undefined, true, true); expect(dijkstraRes1ntt).toBeTruthy(); if (dijkstraRes1ntt) { const { distMap, minDist, minPath, paths } = dijkstraRes1ntt; expect(distMap).toBeInstanceOf(Map); expect(distMap.size).toBe(9); expect(distMap.get(vertex1)).toBe(0); expect(distMap.get(vertex2)).toBe(12); expect(distMap.get(vertex3)).toBe(35); expect(distMap.get(vertex4)).toBe(14); expect(distMap.get(vertex5)).toBe(70); expect(distMap.get(vertex6)).toBe(Number.MAX_SAFE_INTEGER); expect(distMap.get(vertex7)).toBe(61); expect(distMap.get(vertex8)).toBe(Number.MAX_SAFE_INTEGER); expect(distMap.get(vertex9)).toBe(19); expect(minDist).toBe(12); expect(minPath).toBeInstanceOf(Array); expect(minPath.length).toBe(2); expect(minPath[0]).toBe(vertex1); expect(minPath[1]).toBe(vertex2); expect(paths).toBeInstanceOf(Array); expect(paths.length).toBe(9); expect(paths[0]).toBeInstanceOf(Array); expect(paths[0][0]).toBe(vertex1); expect(paths[1]).toBeInstanceOf(Array); expect(paths[1][0]).toBe(vertex1); expect(paths[1][1]).toBe(vertex2); expect(paths[2]).toBeInstanceOf(Array); expect(paths[2][0]).toBe(vertex1); expect(paths[2][1]).toBe(vertex2); expect(paths[2][2]).toBe(vertex3); expect(paths[3]).toBeInstanceOf(Array); expect(paths[3][0]).toBe(vertex1); expect(paths[3][1]).toBe(vertex4); expect(paths[4]).toBeInstanceOf(Array); expect(paths[4][0]).toBe(vertex1); expect(paths[4][1]).toBe(vertex2); expect(paths[4][2]).toBe(vertex3); expect(paths[4][3]).toBe(vertex5); expect(paths[5]).toBeInstanceOf(Array); expect(paths[5][0]).toBe(vertex6); expect(paths[6]).toBeInstanceOf(Array); expect(paths[6][0]).toBe(vertex1); expect(paths[6][1]).toBe(vertex4); expect(paths[6][2]).toBe(vertex7); expect(paths[7]).toBeInstanceOf(Array); expect(paths[7][0]).toBe(vertex8); expect(paths[8]).toBeInstanceOf(Array); expect(paths[8][0]).toBe(vertex1); expect(paths[8][1]).toBe(vertex9); } const dijkstraWithoutHeapRes1ntt = myGraph.dijkstraWithoutHeap(1, undefined, true, true); expect(dijkstraWithoutHeapRes1ntt).toBeTruthy(); if (dijkstraWithoutHeapRes1ntt) { const { distMap, minDist, minPath, paths } = dijkstraWithoutHeapRes1ntt; expect(distMap).toBeInstanceOf(Map); expect(distMap.size).toBe(9); expect(distMap.get(vertex1)).toBe(0); expect(distMap.get(vertex2)).toBe(12); expect(distMap.get(vertex3)).toBe(35); expect(distMap.get(vertex4)).toBe(14); expect(distMap.get(vertex5)).toBe(70); expect(distMap.get(vertex6)).toBe(Number.MAX_SAFE_INTEGER); expect(distMap.get(vertex7)).toBe(61); expect(distMap.get(vertex8)).toBe(Number.MAX_SAFE_INTEGER); expect(distMap.get(vertex9)).toBe(19); expect(minDist).toBe(12); expect(minPath).toBeInstanceOf(Array); expect(minPath.length).toBe(2); expect(minPath[0]).toBe(vertex1); expect(minPath[1]).toBe(vertex2); expect(paths).toBeInstanceOf(Array); expect(paths.length).toBe(9); expect(paths[0]).toBeInstanceOf(Array); expect(paths[0][0]).toBe(vertex1); expect(paths[1]).toBeInstanceOf(Array); expect(paths[1][0]).toBe(vertex1); expect(paths[1][1]).toBe(vertex2); expect(paths[2]).toBeInstanceOf(Array); expect(paths[2][0]).toBe(vertex1); expect(paths[2][1]).toBe(vertex2); expect(paths[2][2]).toBe(vertex3); expect(paths[3]).toBeInstanceOf(Array); expect(paths[3][0]).toBe(vertex1); expect(paths[3][1]).toBe(vertex4); expect(paths[4]).toBeInstanceOf(Array); expect(paths[4][0]).toBe(vertex1); expect(paths[4][1]).toBe(vertex2); expect(paths[4][2]).toBe(vertex3); expect(paths[4][3]).toBe(vertex5); expect(paths[5]).toBeInstanceOf(Array); expect(paths[5][0]).toBe(vertex6); expect(paths[6]).toBeInstanceOf(Array); expect(paths[6][0]).toBe(vertex1); expect(paths[6][1]).toBe(vertex4); expect(paths[6][2]).toBe(vertex7); expect(paths[7]).toBeInstanceOf(Array); expect(paths[7][0]).toBe(vertex8); expect(paths[8]).toBeInstanceOf(Array); expect(paths[8][0]).toBe(vertex1); expect(paths[8][1]).toBe(vertex9); } }); }); describe('cycles, strongly connected components, bridges, articular points in DirectedGraph', () => { const graph = new DirectedGraph(); graph.addVertex('A'); graph.addVertex('B'); graph.addVertex('C'); graph.addVertex('D'); graph.addVertex('E'); graph.addVertex('F'); graph.addVertex('G'); graph.addVertex('H'); graph.addEdge('A', 'B'); graph.addEdge('B', 'D'); graph.addEdge('D', 'C'); graph.addEdge('C', 'A'); graph.addEdge('C', 'B'); graph.addEdge('D', 'E'); graph.addEdge('E', 'G'); graph.addEdge('E', 'H'); graph.addEdge('H', 'F'); const cycles = graph.getCycles(); const scCs = graph.getSCCs(); // const bridges = graph.getBridges(); // const cutVertices = graph.getCutVertices(); const dfnMap = graph.getDFNMap(); const lowMap = graph.getLowMap(); expect(cycles.length).toBe(2); expect(scCs.size).toBe(5); // expect(bridges.length).toBe(4); // expect(cutVertices.length).toBe(4); expect(dfnMap.size).toBe(8); expect(lowMap.size).toBe(8); }); describe('DirectedGraph iterative Methods', () => { let graph: DirectedGraph<string>; let vertexMap: string[]; beforeEach(() => { graph = new DirectedGraph(); vertexMap = ['A', 'B', 'C', 'D']; vertexMap.forEach(vertex => graph.addVertex(vertex)); }); it('[Symbol.iterator] should iterate over all vertexMap', () => { const iteratedVertices = []; for (const vertex of graph) { iteratedVertices.push(vertex[0]); } expect(iteratedVertices).toEqual(vertexMap); }); it('forEach should apply a function to each vertex', () => { const result: VertexKey[] = []; graph.forEach(key => key && result.push(key)); expect(result).toEqual(vertexMap); }); it('filter should return vertexMap that satisfy the condition', () => { const filtered = graph.filter(vertex => vertex === 'A' || vertex === 'B'); expect(filtered).toEqual([ ['A', undefined], ['B', undefined] ]); }); it('map should apply a function to each vertex and return a new array', () => { const mapped = graph.map(vertex => vertex + '_mapped'); expect(mapped).toEqual(vertexMap.map(key => key + '_mapped')); }); it('reduce should accumulate a value based on each vertex', () => { const concatenated = graph.reduce((acc, value, key) => acc + key, ''); expect(concatenated).toBe(vertexMap.join('')); }); it('Removing an edge of a DirectedGraph should delete additional edges', () => { const dg = new DirectedGraph(); dg.addVertex('hello'); dg.addVertex('hi'); dg.addVertex('hey'); dg.addEdge('hello', 'hi'); dg.addEdge('hello', 'hey'); expect(dg.getEdge('hello', 'hi')?.src).toBe('hello'); expect(dg.getEdge('hello', 'hi')?.dest).toBe('hi'); expect(dg.getEdge('hello', 'hey')?.src).toBe('hello'); expect(dg.getEdge('hello', 'hey')?.dest).toBe('hey'); dg.deleteEdge('hello', 'hi'); expect(dg.getEdge('hello', 'hi')).toBe(undefined); expect(dg.getEdge('hello', 'hey')).toBeInstanceOf(DirectedEdge); expect(dg.incomingEdgesOf('Hi')).toEqual([]); }); it('Removing a vertex of a DirectedGraph should delete additional edges', () => { const graph = new DirectedGraph(); graph.addVertex('Hello'); graph.addVertex('Hi'); graph.addEdge('Hello', 'Hi'); graph.deleteVertex('Hello'); expect(graph.incomingEdgesOf('Hi')).toEqual([]); }); it('Removing a vertex from a DirectedGraph should remove its edges', () => { const dg = new DirectedGraph(); dg.addVertex('hello'); dg.addVertex('world'); dg.addVertex('earth'); dg.addEdge('hello', 'world'); dg.addEdge('hello', 'earth'); dg.addEdge('world', 'earth'); expect(dg.getEdge('hello', 'world')?.src).toBe('hello'); expect(dg.edgeSet().length).toBe(3); expect(dg.edgeSet()[0].dest).toBe('world'); dg.deleteVertex('hello'); expect(dg.edgeSet().length).toBe(1); expect(dg.edgeSet()?.[0].src).toBe('world'); expect(dg.getEdge('hello', 'world')).toBe(undefined); }); }); describe('DirectedGraph getCycles', () => { it('should getCycles return correct result', () => { const graph = new DirectedGraph(); graph.addVertex('A'); graph.addVertex('B'); graph.addVertex('C'); graph.addVertex('D'); graph.addVertex('E'); graph.addEdge('A', 'B'); graph.addEdge('A', 'C'); graph.addEdge('B', 'D'); graph.addEdge('C', 'D'); graph.addEdge('D', 'E'); graph.addEdge('E', 'B'); const cycles = graph.getCycles(); expect(cycles.length).toBe(1); expect(cycles[0]).toEqual(['B', 'D', 'E']); }); it('should simple cycles graph getCycles return correct result', () => { const graph = new DirectedGraph(); graph.addVertex('A'); graph.addVertex('B'); graph.addVertex('C'); graph.addVertex('D'); graph.addEdge('A', 'B'); graph.addEdge('B', 'C'); graph.addEdge('C', 'A'); graph.addEdge('A', 'D'); graph.addEdge('D', 'C'); const cycles = graph.getCycles(); expect(cycles.length).toBe(2); expect(cycles).toEqual([ ['A', 'B', 'C'], ['A', 'D', 'C'] ]); }); it('should 3 cycles graph getCycles return correct result', () => { const graph = new DirectedGraph(); graph.addVertex('A'); graph.addVertex('B'); graph.addVertex('C'); graph.addVertex('D'); graph.addVertex('E'); graph.addVertex('F'); graph.addVertex('G'); graph.addEdge('A', 'B'); graph.addEdge('A', 'C'); graph.addEdge('B', 'D'); graph.addEdge('C', 'D'); graph.addEdge('D', 'E'); graph.addEdge('E', 'B'); graph.addEdge('B', 'F'); graph.addEdge('F', 'E'); graph.addEdge('C', 'G'); graph.addEdge('G', 'A'); const cycles = graph.getCycles(); expect(cycles.length).toBe(3); expect(cycles).toEqual([ ['A', 'C', 'G'], ['B', 'D', 'E'], ['B', 'F', 'E'] ]); }); }); describe('DirectedGraph tarjan', () => { it('should simple cycles graph tarjan cycles return correct result', () => { const graph = new DirectedGraph(); graph.addVertex('A'); graph.addVertex('B'); graph.addVertex('C'); graph.addVertex('D'); graph.addEdge('A', 'B'); graph.addEdge('B', 'C'); graph.addEdge('C', 'A'); graph.addEdge('A', 'D'); graph.addEdge('D', 'C'); const cycles = graph.getCycles(); expect(cycles.length).toBe(2); expect(cycles).toEqual([ ['A', 'B', 'C'], ['A', 'D', 'C'] ]); }); function getAsVerticesArrays(vss: Map<number, DirectedVertex<any>[]>) { return [...vss.values()].map(vs => vs.map(vertex => vertex.key)); } function createExampleGraph1() { const graph = new DirectedGraph(); graph.addVertex('A'); graph.addVertex('B'); graph.addVertex('C'); graph.addVertex('D'); graph.addVertex('E'); graph.addEdge('A', 'B'); graph.addEdge('A', 'C'); graph.addEdge('B', 'D'); graph.addEdge('C', 'D'); graph.addEdge('D', 'E'); graph.addEdge('E', 'B'); return graph; } it('should tarjan cycles return correct result', () => { const graph = createExampleGraph1(); const cycles = graph.getCycles(); expect(cycles.length).toBe(1); expect(cycles).toEqual([['B', 'D', 'E']]); }); it('should tarjan SCCs return correct result', () => { const graph = createExampleGraph1(); const sccs = graph.tarjan().SCCs; expect(sccs.size).toBe(3); expect(getAsVerticesArrays(sccs)).toEqual([['E', 'D', 'B'], ['C'], ['A']]); }); function createExampleGraph2() { const graph = createExampleGraph1(); graph.addVertex('F'); graph.addVertex('G'); graph.addEdge('B', 'F'); graph.addEdge('F', 'E'); graph.addEdge('C', 'G'); graph.addEdge('G', 'A'); return graph; } it('should 3 cycles graph tarjan cycles return correct result', () => { const graph = createExampleGraph2(); const cycles = graph.getCycles(); expect(cycles.length).toBe(3); expect(cycles).toEqual([ ['A', 'C', 'G'], ['B', 'D', 'E'], ['B', 'F', 'E'] ]); }); it('should 3 cycles graph tarjan SCCs return correct result', () => { const graph = createExampleGraph2(); const sccs = graph.tarjan().SCCs; expect(sccs.size).toBe(2); expect(getAsVerticesArrays(sccs)).toEqual([ ['F', 'E', 'D', 'B'], ['G', 'C', 'A'] ]); }); function createExampleGraph3() { const graph = new DirectedGraph(); graph.addVertex('A'); graph.addVertex('B'); graph.addVertex('C'); graph.addVertex('D'); graph.addVertex('E'); graph.addVertex('F'); graph.addVertex('G'); graph.addEdge('A', 'B'); graph.addEdge('B', 'C'); graph.addEdge('C', 'D'); graph.addEdge('D', 'B'); graph.addEdge('A', 'E'); graph.addEdge('E', 'F'); graph.addEdge('F', 'G'); graph.addEdge('G', 'E'); return graph; } it('should cuttable graph tarjan cycles return correct result', () => { const graph = createExampleGraph3(); const cycles = graph.getCycles(); expect(cycles.length).toBe(2); expect(cycles).toEqual([ ['B', 'C', 'D'], ['E', 'F', 'G'] ]); }); it('should cuttable graph tarjan SCCs return correct result', () => { const graph = createExampleGraph3(); const sccs = graph.tarjan().SCCs; expect(sccs.size).toBe(3); expect(getAsVerticesArrays(sccs)).toEqual([['D', 'C', 'B'], ['G', 'F', 'E'], ['A']]); }); function createExampleGraph4() { const graph = createExampleGraph3(); graph.addVertex('H'); graph.addVertex('I'); graph.addVertex('J'); graph.addVertex('K'); graph.addEdge('C', 'H'); graph.addEdge('H', 'I'); graph.addEdge('I', 'D'); graph.addEdge('H', 'J'); graph.addEdge('J', 'K'); graph.addEdge('K', 'H'); return graph; } it('should more cuttable graph tarjan cycles return correct result', () => { const graph = createExampleGraph4(); const cycles = graph.getCycles(); expect(cycles.length).toBe(4); expect(cycles).toEqual([ ['B', 'C', 'D'], ['B', 'C', 'H', 'I', 'D'], ['E', 'F', 'G'], ['H', 'J', 'K'] ]); }); it('should more cuttable graph tarjan SCCs return correct result', () => { const graph = createExampleGraph4(); const sccs = graph.tarjan().SCCs; expect(sccs.size).toBe(3); expect(getAsVerticesArrays(sccs)).toEqual([['K', 'J', 'I', 'H', 'D', 'C', 'B'], ['G', 'F', 'E'], ['A']]); }); function createExampleGraph5() { const graph = createExampleGraph4(); graph.addEdge('F', 'H'); return graph; } it('should uncuttable graph tarjan cycles return correct result', () => { const graph = createExampleGraph5(); const cycles = graph.getCycles(); expect(cycles.length).toBe(4); expect(cycles).toEqual([ ['B', 'C', 'D'], ['B', 'C', 'H', 'I', 'D'], ['E', 'F', 'G'], ['H', 'J', 'K'] ]); }); it('should uncuttable graph tarjan SCCs return correct result', () => { const graph = createExampleGraph5(); const sccs = graph.tarjan().SCCs; expect(sccs.size).toBe(3); expect(getAsVerticesArrays(sccs)).toEqual([['K', 'J', 'I', 'H', 'D', 'C', 'B'], ['G', 'F', 'E'], ['A']]); }); }); describe('delete', () => { it(`deleteVertex deletes all of it's neighbors from the inEdge Map`, () => { const graph = new DirectedGraph(); graph.addVertex('A'); graph.addVertex('B'); graph.addVertex('C'); graph.addEdge('B', 'A'); graph.addEdge('C', 'A'); // 'Incoming to A should contain ['B','C'] expect(graph.incomingEdgesOf('A').map(e => e.src)).toEqual(['B', 'C']); // ['B','C'] // Now delete B, which has no direct link to C, only that C -> A. graph.deleteVertex('B'); // Now if we do the same call to incoming edges for we should get only ['C'] expect(graph.incomingEdgesOf('A').map(e => e.src)).toEqual(['C']); // []; // but it only shows an empty array, since we deleted all of `A's edges, not just the one to `B`. // If we check C, it correctly shows A as an outgoing edge, // even though A no longer has any knowledge of C linking to it. expect(graph.outgoingEdgesOf('C').map(e => e.dest)).toEqual(['A']); }); // it('should print', () => { // const graph = new DirectedGraph(); // graph.addVertex('A'); // graph.addVertex('B'); // graph.addVertex('C'); // // graph.addEdge('A', 'B'); // graph.addEdge('A', 'C'); // graph.addEdge('B', 'C'); // graph.addEdge('C', 'B'); // graph.print(); // // }) });