UNPKG

maths.ts

Version:

Math utilities library for TypeScript, JavaScript and Node.js

168 lines (167 loc) 6.38 kB
"use strict"; /** * @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. */ Object.defineProperty(exports, "__esModule", { value: true }); /** * 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. */ function graphSearch(graph, source, destination, outFunction, inFunction, logger) { // Initializing list with the source element const list = { 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); } exports.graphSearch = graphSearch; /** * Represents the status of each vertex. It is handled internally by * VertexElement and graphSearch. */ var VertexStatus; (function (VertexStatus) { VertexStatus[VertexStatus["NOT_VISITED"] = 0] = "NOT_VISITED"; VertexStatus[VertexStatus["IN_QUEUE"] = 1] = "IN_QUEUE"; VertexStatus[VertexStatus["VISITED"] = 2] = "VISITED"; })(VertexStatus || (VertexStatus = {})); /** * 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, destination, logger) { 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) { return list.vertexes.map(i => { return { id: i.id, trail: i.trail.map(e => e), cost: i.cost, depth: i.depth }; }); }