UNPKG

data-structure-typed

Version:
616 lines (534 loc) 19.8 kB
import { UndirectedEdge, UndirectedGraph, UndirectedVertex } from '../../../../src'; import saltyVertices from './salty-vertexes.json'; import saltyEdges from './salty-edges.json'; describe('UndirectedGraph Operation Test', () => { let graph: UndirectedGraph; beforeEach(() => { graph = new UndirectedGraph(); }); it('should edge cases', () => { expect(graph.deleteEdge(new UndirectedEdge('c', 'd'))).toBe(undefined); expect(graph.deleteEdgeBetween('c', 'd')).toBe(undefined); expect(graph.degreeOf('c')).toBe(0); expect(graph.edgesOf('c').length).toBe(0); expect(graph.getEndsOfEdge(new UndirectedEdge('c', 'd'))).toBe(undefined); }); it('should add vertices', () => { expect(graph.isEmpty()).toBe(true); const vertex1 = new UndirectedVertex('A'); const vertex2 = new UndirectedVertex('B'); graph.addVertex(vertex1); expect(graph.isEmpty()).toBe(false); graph.addVertex(vertex2); expect(graph.hasVertex(vertex1)).toBe(true); expect(graph.hasVertex(vertex2)).toBe(true); expect(graph.isEmpty()).toBe(false); graph.clear(); expect(graph.isEmpty()).toBe(true); }); it('should add edges', () => { const vertex1 = new UndirectedVertex('A'); const vertex2 = new UndirectedVertex('B'); const edge = new UndirectedEdge('A', 'B'); graph.addVertex(vertex1); graph.addVertex(vertex2); graph.addEdge(edge); expect(graph.hasEdge('A', 'B')).toBe(true); expect(graph.hasEdge('B', 'A')).toBe(true); expect(graph.has('A')).toBe(true); expect(graph.get('A')).toBe(undefined); }); it('should delete edges', () => { const vertex1 = new UndirectedVertex('A'); const vertex2 = new UndirectedVertex('B'); const edge = new UndirectedEdge('A', 'B'); graph.addVertex(vertex1); graph.addVertex(vertex2); graph.addEdge(edge); expect(graph.deleteEdge(edge)).toBe(edge); expect(graph.hasEdge('A', 'B')).toBe(false); }); it('should perform topological sort', () => { graph.addVertex('A'); graph.addVertex('B'); graph.addVertex('C'); graph.addVertex('D'); graph.deleteVertex('C'); graph.addEdge('A', 'B'); graph.addEdge('B', 'D'); const dijkstraResult = graph.dijkstra('A'); expect(Array.from(dijkstraResult?.seen ?? []).map(vertex => vertex.key)).toEqual(['A', 'B', 'D']); }); }); describe('UndirectedGraph', () => { let undirectedGraph: UndirectedGraph<string, string>; beforeEach(() => { // Create a new UndirectedGraph instance before each test undirectedGraph = new UndirectedGraph<string, string>(); }); // Test adding vertices to the graph it('should add vertices to the graph', () => { const vertexA = new UndirectedVertex('A', 'Location A'); const vertexB = new UndirectedVertex('B', 'Location B'); undirectedGraph.addVertex(vertexA); undirectedGraph.addVertex(vertexB); expect(undirectedGraph.hasVertex('A')).toBe(true); expect(undirectedGraph.hasVertex('B')).toBe(true); }); // Test adding edges to the graph it('should add edges to the graph', () => { const vertexA = new UndirectedVertex('A', 'Location A'); const vertexB = new UndirectedVertex('B', 'Location B'); const edgeAB = new UndirectedEdge('A', 'B', 1, 'Edge between A and B'); undirectedGraph.addVertex(vertexA); undirectedGraph.addVertex(vertexB); undirectedGraph.addEdge(edgeAB); expect(undirectedGraph.hasEdge('A', 'B')).toBe(true); }); // Test getting neighbors of a vertex it('should return the neighbors of a vertex', () => { const vertexA = new UndirectedVertex('A', 'Location A'); const vertexB = new UndirectedVertex('B', 'Location B'); const vertexC = new UndirectedVertex('C', 'Location C'); const edgeAB = new UndirectedEdge('A', 'B', 1, 'Edge between A and B'); const edgeBC = new UndirectedEdge('B', 'C', 2, 'Edge between B and C'); undirectedGraph.addVertex(vertexA); undirectedGraph.addVertex(vertexB); undirectedGraph.addVertex(vertexC); undirectedGraph.addEdge(edgeAB); undirectedGraph.addEdge(edgeBC); const neighborsOfA = undirectedGraph.getNeighbors('A'); const neighborsOfB = undirectedGraph.getNeighbors('B'); expect(neighborsOfA).toEqual([vertexB]); expect(neighborsOfB).toEqual([vertexA, vertexC]); }); // Test degree of a vertex it('should return the degree of a vertex', () => { const vertexA = new UndirectedVertex('A', 'Location A'); const vertexB = new UndirectedVertex('B', 'Location B'); const vertexC = new UndirectedVertex('C', 'Location C'); const edgeAB = new UndirectedEdge('A', 'B', 3, 'Edge between A and B'); const edgeBC = new UndirectedEdge('B', 'C', 4, 'Edge between B and C'); edgeAB.endpoints = edgeAB.endpoints; expect(undirectedGraph.edgeMap.size).toBe(0); undirectedGraph.addVertex(vertexA); undirectedGraph.addVertex(vertexB); undirectedGraph.addVertex(vertexC); undirectedGraph.addEdge(edgeAB); undirectedGraph.addEdge(edgeBC); const degreeOfA = undirectedGraph.degreeOf('A'); const degreeOfB = undirectedGraph.degreeOf('B'); const degreeOfC = undirectedGraph.degreeOf('C'); expect(undirectedGraph.edgeSet().length).toBe(2); expect(undirectedGraph.getEndsOfEdge(edgeBC)?.length).toBe(2); expect(degreeOfA).toBe(1); expect(degreeOfB).toBe(2); expect(degreeOfC).toBe(1); }); it('should getAllPathsBetween work well in 66 vertexes 97 edges graph', () => { const graph = new UndirectedGraph<{ name: string }, number>(); for (const v of saltyVertices) { graph.addVertex(v.name, v); } for (const e of saltyEdges) { const [s, d] = e; graph.addEdge(s.name, d.name, d.weight); } const allPaths = graph.getAllPathsBetween('Intersection_1', 'Intersection_5'); expect(allPaths.length).toBe(1000); const minWeightedPathDFS = graph.getMinPathBetween('Intersection_1', 'Intersection_5', true, true); expect(minWeightedPathDFS?.[0]?.key).toBe('Intersection_1'); expect(minWeightedPathDFS?.[5]?.key).toBe('Intersection_42'); expect(minWeightedPathDFS?.[8]?.key).toBe('Intersection_18'); expect(minWeightedPathDFS?.[27]?.key).toBe('Intersection_6'); const minWeightedPath = graph.dijkstra('Intersection_1', 'Intersection_5', true, true); expect(minWeightedPath?.minPath?.[0]?.key).toBe('Intersection_1'); expect(minWeightedPath?.minPath?.[1]?.key).toBe('Intersection_2'); expect(minWeightedPath?.minPath?.[2]?.key).toBe('Intersection_3'); expect(minWeightedPath?.minPath?.[3]?.key).toBe('Intersection_4'); expect(minWeightedPath?.minPath?.[4]?.key).toBe('Intersection_5'); }); it('Removing an edge of a UndirectedGraph should not delete additional edges', () => { const dg = new UndirectedGraph(); dg.addVertex('hello'); dg.addVertex('hi'); dg.addVertex('hey'); dg.addEdge('hello', 'hi'); dg.addEdge('hello', 'hey'); expect(dg.getEdge('hello', 'hi')?.endpoints[0]).toBe('hello'); expect(dg.getEdge('hello', 'hi')?.endpoints[1]).toBe('hi'); expect(dg.getEdge('hello', 'hey')?.endpoints[0]).toBe('hello'); expect(dg.getEdge('hello', 'hey')?.endpoints[1]).toBe('hey'); dg.deleteEdge('hello', 'hi'); expect(dg.getEdge('hello', 'hi')).toBe(undefined); expect(dg.getEdge('hello', 'hey')).toBeInstanceOf(UndirectedEdge); }); it('Removing a vertex of a UndirectedGraph should delete additional edges', () => { const graph = new UndirectedGraph(); graph.addVertex('Hello'); graph.addVertex('Hi'); graph.addEdge('Hello', 'Hi'); graph.deleteVertex('Hello'); expect(graph.edgesOf('Hi')).toEqual([]); }); it('Removing a vertex from a UndirectedGraph should remove its edges', () => { const dg = new UndirectedGraph(); 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')?.endpoints[0]).toBe('hello'); expect(dg.edgeSet().length).toBe(3); expect(dg.edgeSet()[0].endpoints).toEqual(['hello', 'world']); dg.deleteVertex('hello'); expect(dg.edgeSet().length).toBe(1); expect(dg.edgeSet()?.[0].endpoints[0]).toBe('world'); expect(dg.getEdge('hello', 'world')).toBe(undefined); }); }); describe('cycles, strongly connected components, bridges, articular points in UndirectedGraph', () => { const graph = new UndirectedGraph(); 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 cCs = graph.getCCs(); const bridges = graph.getBridges(); const cutVertices = graph.getCutVertices(); const dfnMap = graph.getDFNMap(); const lowMap = graph.getLowMap(); expect(cycles.length).toBe(3); // expect(cCs.size).toBe(5); expect(bridges.length).toBe(4); expect(cutVertices.length).toBe(4); expect(dfnMap.size).toBe(8); expect(lowMap.size).toBe(8); }); it('Should return Number.MAX_SAFE_INTEGER if dest is not found', () => { const graph = new UndirectedGraph<string>(); for (let i = 0; i < 3; ++i) { graph.addVertex(graph.createVertex(i, `${i}`)); } graph.addEdge(0, 1, 1); const minCost02 = graph.getMinCostBetween(0, 2, true); expect(minCost02).toBe(Number.MAX_SAFE_INTEGER); const minCost01 = graph.getMinCostBetween(0, 1, true); expect(minCost01).toBe(1); }); describe('UndirectedGraph getCycles', () => { it('should getCycles return correct result', () => { const graph = new UndirectedGraph(); 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(3); expect(cycles).toEqual([ ['A', 'B', 'D', 'C'], ['A', 'B', 'E', 'D', 'C'], ['B', 'D', 'E'] ]); }); it('should simple cycles graph getCycles return correct result', () => { const graph = new UndirectedGraph(); 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(3); expect(cycles).toEqual([ ['A', 'B', 'C'], ['A', 'B', 'C', 'D'], ['A', 'C', 'D'] ]); }); it('should 3 cycles graph getCycles return correct result', () => { const graph = new UndirectedGraph(); 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(10); expect(cycles).toEqual([ ['A', 'B', 'D', 'C'], ['A', 'B', 'D', 'C', 'G'], ['A', 'B', 'E', 'D', 'C'], ['A', 'B', 'E', 'D', 'C', 'G'], ['A', 'B', 'F', 'E', 'D', 'C'], ['A', 'B', 'F', 'E', 'D', 'C', 'G'], ['A', 'C', 'G'], ['B', 'D', 'E'], ['B', 'D', 'E', 'F'], ['B', 'E', 'F'] ]); }); }); describe('UndirectedGraph tarjan', () => { it('should simple cycles graph tarjan cycles return correct result', () => { const graph = new UndirectedGraph(); 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(3); expect(cycles).toEqual([ ['A', 'B', 'C'], ['A', 'B', 'C', 'D'], ['A', 'C', 'D'] ]); }); function createExampleGraph1() { const graph = new UndirectedGraph(); 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 cut vertexes return correct result', () => { const graph = createExampleGraph1(); const cutVertices = graph.tarjan().cutVertices; expect(cutVertices.length).toBe(0); }); it('should tarjan bridges return correct result', () => { const graph = createExampleGraph1(); const bridges = graph.tarjan().bridges; expect(bridges.length).toBe(0); }); it('should 3 cycles graph tarjan cut vertexes return correct result', () => { const graph = createExampleGraph1(); const cutVertices = graph.tarjan().cutVertices; expect(cutVertices.length).toBe(0); }); it('should 3 cycles graph tarjan bridges return correct result', () => { const graph = createExampleGraph1(); const bridges = graph.tarjan().bridges; expect(bridges.length).toBe(0); }); it('should cuttable graph tarjan cut vertexes return correct result', () => { const graph = createExampleGraph3(); const cutVertices = graph.tarjan().cutVertices; expect(cutVertices.length).toBe(3); expect(cutVertices.map(cv => cv.key)).toEqual(['B', 'E', 'A']); }); it('should cuttable graph tarjan bridges return correct result', () => { const graph = createExampleGraph3(); const bridges = graph.tarjan().bridges; expect(bridges.length).toBe(2); expect(bridges.map(edge => edge.endpoints)).toEqual([ ['A', 'B'], ['A', 'E'] ]); }); it('should more cuttable graph tarjan cut vertexes return correct result', () => { const graph = createExampleGraph4(); const cutVertices = graph.tarjan().cutVertices; expect(cutVertices.length).toBe(4); expect(cutVertices.map(cv => cv.key)).toEqual(['H', 'B', 'E', 'A']); }); it('should more cuttable graph tarjan bridges return correct result', () => { const graph = createExampleGraph4(); const bridges = graph.tarjan().bridges; expect(bridges.length).toBe(2); expect(bridges.map(edge => edge.endpoints)).toEqual([ ['A', 'B'], ['A', 'E'] ]); }); it('should uncuttable graph tarjan cut vertexes return correct result', () => { const graph = createExampleGraph5(); const cutVertices = graph.tarjan().cutVertices; expect(cutVertices.length).toBe(1); }); it('should uncuttable graph tarjan bridges return correct result', () => { const graph = createExampleGraph5(); const bridges = graph.tarjan().bridges; expect(bridges.length).toBe(0); }); 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(10); expect(cycles).toEqual([ ['A', 'B', 'D', 'C'], ['A', 'B', 'D', 'C', 'G'], ['A', 'B', 'E', 'D', 'C'], ['A', 'B', 'E', 'D', 'C', 'G'], ['A', 'B', 'F', 'E', 'D', 'C'], ['A', 'B', 'F', 'E', 'D', 'C', 'G'], ['A', 'C', 'G'], ['B', 'D', 'E'], ['B', 'D', 'E', 'F'], ['B', 'E', 'F'] ]); }); function createExampleGraph3() { const graph = new UndirectedGraph(); 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 CCs return correct result', () => { // const graph = createExampleGraph3(); // const ccs = graph.tarjan().CCs; // expect(ccs.size).toBe(3); // expect(getAsVerticesArrays(ccs)).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(5); expect(cycles).toEqual([ ['B', 'C', 'D'], ['B', 'C', 'H', 'I', 'D'], ['C', 'D', 'I', 'H'], ['E', 'F', 'G'], ['H', 'J', 'K'] ]); }); // it('should more cuttable graph tarjan SCCs return correct result', () => { // const graph = createExampleGraph4(); // const ccs = graph.tarjan().CCs; // expect(ccs.size).toBe(3); // expect(getAsVerticesArrays(ccs)).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(13); const expectedCycles = [ ['A', 'B', 'C', 'D', 'I', 'H', 'F', 'E'], ['A', 'B', 'C', 'D', 'I', 'H', 'F', 'G', 'E'], ['A', 'B', 'C', 'H', 'F', 'E'], ['A', 'B', 'C', 'H', 'F', 'G', 'E'], ['A', 'B', 'D', 'C', 'H', 'F', 'E'], ['A', 'B', 'D', 'C', 'H', 'F', 'G', 'E'], ['A', 'B', 'D', 'I', 'H', 'F', 'E'], ['A', 'B', 'D', 'I', 'H', 'F', 'G', 'E'], ['B', 'C', 'D'], ['B', 'C', 'H', 'I', 'D'], ['C', 'D', 'I', 'H'], ['E', 'F', 'G'], ['H', 'J', 'K'] ]; expect(cycles).toEqual(expectedCycles); const cloned = graph.clone(); const clonedCycles = cloned.getCycles(); expect(clonedCycles.length).toBe(13); expect(clonedCycles).toEqual(expectedCycles); }); // it('should uncuttable graph tarjan SCCs return correct result', () => { // const graph = createExampleGraph5(); // const ccs = graph.tarjan().CCs; // expect(ccs.size).toBe(3); // expect(getAsVerticesArrays(ccs)).toEqual([["K", "J", "I", "H", "D", "C", "B"], ["G", "F", "E"], ["A"]]); // }); });