UNPKG

maths.ts

Version:

Math utilities library for TypeScript, JavaScript and Node.js

215 lines (198 loc) 6.64 kB
/** * @author Hector J. Vasquez <ipi.vasquez@gmail.com> * * @licence * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import Graph from '../structures/Graph'; import Vertex from '../structures/Vertex'; import {Logger} from '../algorithms'; export interface GraphSearchSolution { trail?: string[]; cost?: number; depth?: number; reachable: boolean; } /** * An helping Interface for handling the vertex in list. It helps to store * information about the vertexes and the path followed to get there as well * as the cost and depth for each vertex on list. */ export interface VertexElement { id: number; vertex: Vertex; trail: number[]; cost: number; depth: number; } /** * A generic implementation for transversing graph searching for an element. * This algorithm holds a list, defined by the interface VertexList. To be * able to execute graphSearch, an outFunction and an inFunction must be * provided. This functions will determine the list behavior. * * BFS, DFS, Uniform cost search and A* are implemented on this function by * sending their respective in and out functions for each implementation. * e.g. BFS sends Array.shift as next function and DFS send Array.pop instead. * @param graph The graph to be transversed. * @param source The starting vertex. * @param destination The goal vertex. * @param outFunction The out function. * @param inFunction The in function. * @param logger An optional logger to knowing more about the algorithm. * @return A GraphSearchSolution interface with its reachable element false * if the destination element was not found in the graph. The reachable * element will be true if the element was in the graph alongside with more * information about cost, depth and trail to get to the solution. */ export function graphSearch(graph: Graph, source: number, destination: number, outFunction: () => VertexElement, inFunction: (i: VertexElement) => any, logger?: Logger): GraphSearchSolution { // Initializing list with the source element const list: VertexList = { vertexes: [{ vertex: graph.vertexes[source], id: graph.vertexes[source].id, trail: [graph.vertexes[source].id], cost: 0, depth: 0 }], status: graph.vertexes.map(() => VertexStatus.NOT_VISITED), push: inFunction, next: outFunction }; return gs(list, destination, logger); } /** * Represents the list of vertexes. Moreover, it has functions to push and * get next vertex from this (push & next). Push & next are implemented by * their respective algorithms; e.g. bfs sends Array.shift as next function. */ interface VertexList { vertexes: VertexElement[]; status: VertexStatus[]; push: (n: VertexElement) => any; next: () => VertexElement; } /** * Represents the status of each vertex. It is handled internally by * VertexElement and graphSearch. */ enum VertexStatus { NOT_VISITED, IN_QUEUE, VISITED } /** * Transverses the graph looking for destination, besides, it informs about * every name and the final path to get to destination (if there is a solution). * @param list A initial list of vertexes to look for. * @param destination The goal of gs. * @param logger A logger about every name on the execution. * @return A GraphSearchSolution interface with its reachable element false * if the destination element was not found in the graph. The reachable * element will be true if the element was in the graph alongside with more * information about cost, depth and trail to get to the solution. */ function gs(list: VertexList, destination: number, logger: Logger): GraphSearchSolution { if (logger) { logger.push({ name: 'Starting search of ' + destination + ' from ' + list.vertexes[0].vertex.id, info: {idVertexList: getInfo(list)} }); } // Until there is no more elements on list. while (list.vertexes.length) { const v = list.next(); list.status[v.id] = VertexStatus.VISITED; // Update status for this node if (logger) { logger.push({ name: 'Current node: ' + v.id + (v.id === destination ? ' -> its goal!' : ''), info: { addedVertexes: [], ignoredVertexes: [] } }); } // Wuu! Found! if (v.id === destination) { if (logger) { logger[logger.length - 1].info.idVertexList = getInfo(list); logger.push({ name: 'GraphSearchSolution', info: { trail: v.trail, cost: v.cost, depth: v.depth } }); } return { trail: v.trail.map(i => i + ''), cost: v.cost, depth: v.depth, reachable: true }; } v.vertex.edges.forEach(e => { // Add all vertex on neighborhood if they have not been visited if (list.status[e.destination.id] !== VertexStatus.VISITED) { // Update status list.status[e.destination.id] = VertexStatus.IN_QUEUE; if (logger) { logger[logger.length - 1].info .addedVertexes.push(e.destination.id); } // Copy trail const trail = v.trail.slice(); // Add this as new element on trail trail.push(e.destination.id); list.push({ vertex: e.destination, id: e.destination.id, trail: trail, cost: v.cost + e.weight, depth: v.depth + 1 }); } else if (logger) { logger[logger.length - 1].info .ignoredVertexes.push(e.destination.id); } }); if (logger) { logger[logger.length - 1].info.idVertexList = getInfo(list); } } return { reachable: false }; } /** * Returns an object representing the current state of the list. * @param list The list to be logged. * @return Information about the current state of the list. */ function getInfo(list: VertexList): any { return list.vertexes.map(i => { return { id: i.id, trail: i.trail.map(e => e), // Copying it cost: i.cost, depth: i.depth }; }); }