import { TypedNode } from "../typed/TypedNode" ; import * as $SU from "@/utils/StructUtils" ; import { IBaseEdge } from "./BaseEdge" ; export interface NeighborEntry { node : IBaseNode ; edge : IBaseEdge ; best ? : number ; } export interface BaseNodeConfig { label ? : string ; features ? : { [ key ] : any ; } ; } type NodeFeatures = { [ k ] : any ; } ; export interface IBaseNode { readonly id : string ; readonly label : string ; readonly features : NodeFeatures ; setLabel ( label ) : void ; todo getID ( ) : string ; getLabel ( ) : string ; getFeatures ( ) : NodeFeatures ; getFeature ( key ) : any ; f ( key ) : any | undefined ; setFeatures ( features ) : void ; setFeature ( key , value ) : void ; deleteFeature ( key ) : any ; clearFeatures ( ) : void ; readonly deg : number ; readonly in_deg : number ; readonly out_deg : number ; readonly self_deg : number ; readonly self_in_deg : number ; readonly self_out_deg : number ; addEdge ( edge ) : IBaseEdge ; hasEdge ( edge ) : boolean ; hasEdgeID ( id ) : boolean ; getEdge ( id ) : IBaseEdge ; inEdges ( ) : { [ k ] : IBaseEdge ; } ; outEdges ( ) : { [ k ] : IBaseEdge ; } ; undEdges ( ) : { [ k ] : IBaseEdge ; } ; dirEdges ( ) : { [ k ] : IBaseEdge ; } ; allEdges ( ) : { [ k ] : IBaseEdge ; } ; removeEdge ( edge ) : void ; removeEdgeByID ( id ) : void ; clearOutEdges ( ) : void ; clearInEdges ( ) : void ; clearUndEdges ( ) : void ; clearEdges ( ) : void ; prevNodes ( ) : Array < NeighborEntry > ; nextNodes ( ) : Array < NeighborEntry > ; connNodes ( ) : Array < NeighborEntry > ; reachNodes ( identityFunc ? ) : Array < NeighborEntry > ; allNeighbors ( identityFunc ? ) : Array < NeighborEntry > ; clone ( ) : IBaseNode ; } /**
 *
 */ class BaseNode implements IBaseNode { protected _label : string ; protected _in_deg = 0 ; protected _out_deg = 0 ; protected _deg = 0 ; protected _self_in_deg = 0 ; protected _self_out_deg = 0 ; protected _self_deg = 0 ; protected _features : NodeFeatures ; protected _in_edges : { [ k ] : IBaseEdge ; } ; protected _out_edges : { [ k ] : IBaseEdge ; } ; protected _und_edges : { [ k ] : IBaseEdge ; } ; param _id param config constructor ( protected _id , config = { } ) { this . _in_edges = { } ; this . _out_edges = { } ; this . _und_edges = { } ; this . _label = config . label || _id ; this . _features = config . features != null ? $SU . clone ( config . features ) : { } ; } static isTyped ( arg ) { return ! ! arg . type ; } get id ( ) : string { return this . _id ; } get label ( ) : string { return this . _label ; } get features ( ) : NodeFeatures { return this . _features ; } getID ( ) { return this . _id ; } getLabel ( ) { return this . _label ; } setLabel ( label ) { this . _label = label ; } getFeatures ( ) { return this . _features ; } getFeature ( key ) { return this . _features [ key ] ; } f ( key ) { return this . getFeature ( key ) ; } setFeatures ( features ) { this . _features = $SU . clone ( features ) ; } setFeature ( key , value ) { this . _features [ key ] = value ; } deleteFeature ( key ) { let feat = this . _features [ key ] ; delete this . _features [ key ] ; return feat ; } clearFeatures ( ) { this . _features = { } ; } get deg ( ) : number { return this . _deg ; } get in_deg ( ) : number { return this . _in_deg ; } get out_deg ( ) : number { return this . _out_deg ; } get self_deg ( ) : number { return this . _self_deg ; } get self_in_deg ( ) : number { return this . _self_in_deg ; } get self_out_deg ( ) : number { return this . _self_out_deg ; } /**
     * We have to:
     * 1. throw an error if the edge is already attached
     * 2. add it to the edge array
     * 3. check type of edge (directed / undirected)
     * 4. update our degrees accordingly
     */ addEdge ( edge ) { let ends = edge . getNodes ( ) ; if ( ends . a !== this && ends . b !== this ) { throw new Error ( "Cannot add edge that does not connect to this node" ) ; } const id = edge . id ; if ( edge . isDirected ( ) ) { if ( ends . a === this && ! this . _out_edges [ id ] ) { this . _out_edges [ id ] = edge ; this . _out_deg += 1 ; if ( ends . b === this && ! this . _in_edges [ id ] ) { this . _in_edges [ id ] = edge ; this . _in_deg += 1 ; this . _self_in_deg += 1 ; this . _self_out_deg += 1 ; } } else if ( ! this . _in_edges [ id ] ) { this . _in_edges [ id ] = edge ; this . _in_deg += 1 ; } } else { if ( this . _und_edges [ edge . id ] ) { throw new Error ( "Cannot add same undirected edge multiple times." ) ; } this . _und_edges [ id ] = edge ; this . _deg += 1 ; if ( ends . a === ends . b ) { this . _self_deg += 1 ; } } return edge ; } hasEdge ( edge ) { return ( ! ! this . _in_edges [ edge . getID ( ) ] || ! ! this . _out_edges [ edge . getID ( ) ] || ! ! this . _und_edges [ edge . getID ( ) ] ) ; } hasEdgeID ( id ) { return ( ! ! this . _in_edges [ id ] || ! ! this . _out_edges [ id ] || ! ! this . _und_edges [ id ] ) ; } getEdge ( id ) { let edge = this . _in_edges [ id ] || this . _out_edges [ id ] || this . _und_edges [ id ] ; if ( ! edge ) { throw new Error ( "Cannot retrieve non-existing edge." ) ; } return edge ; } inEdges ( ) { return this . _in_edges ; } outEdges ( ) { return this . _out_edges ; } undEdges ( ) { return this . _und_edges ; } dirEdges ( ) { return $SU . mergeObjects ( [ this . _in_edges , this . _out_edges ] ) ; } allEdges ( ) { return $SU . mergeObjects ( [ this . _in_edges , this . _out_edges , this . _und_edges ] ) ; } description param edge removeEdge ( edge ) { if ( ! this . hasEdge ( edge ) ) { throw new Error ( "Cannot remove unconnected edge." ) ; } const id = edge . id ; const ends = edge . getNodes ( ) ; let e = this . _und_edges [ id ] ; if ( e ) { delete this . _und_edges [ id ] ; this . _deg -= 1 ; ends . a === ends . b && ( this . _self_deg -= 1 ) ; } e = this . _in_edges [ id ] ; if ( e ) { delete this . _in_edges [ id ] ; this . _in_deg -= 1 ; ends . a === ends . b && ( this . _self_in_deg -= 1 ) ; } e = this . _out_edges [ id ] ; if ( e ) { delete this . _out_edges [ id ] ; this . _out_deg -= 1 ; ends . a === ends . b && ( this . _self_out_deg -= 1 ) ; } } removeEdgeByID ( id ) { if ( ! this . hasEdgeID ( id ) ) { throw new Error ( "Cannot remove unconnected edge." ) ; } this . removeEdge ( this . getEdge ( id ) ) ; } description clearOutEdges ( ) { for ( let e of Object . values ( this . outEdges ( ) ) ) { this . removeEdge ( e ) ; } } description clearInEdges ( ) { for ( let e of Object . values ( this . inEdges ( ) ) ) { this . removeEdge ( e ) ; } } clearUndEdges ( ) { this . _und_edges = { } ; this . _deg = 0 ; this . _self_deg = 0 ; } clearEdges ( ) { this . clearUndEdges ( ) ; this . _in_edges = { } ; this . _out_edges = { } ; this . _deg = this . _self_deg = this . _in_deg = this . _self_in_deg = this . _out_deg = this . _self_out_deg = 0 ; } /**
     * return the set of all nodes that have
     * directed edges coming into this node
     */ prevNodes ( ) { let prevs = [ ] ; let key , edge ; for ( key in this . _in_edges ) { if ( this . _in_edges . hasOwnProperty ( key ) ) { edge = this . _in_edges [ key ] ; prevs . push ( { node : edge . getNodes ( ) . a , edge : edge , } ) ; } } return prevs ; } /**
     * return the set of all nodes that have
     * directed edges going out from this node
     */ nextNodes ( ) { let nexts = [ ] ; let key , edge ; for ( key in this . _out_edges ) { if ( this . _out_edges . hasOwnProperty ( key ) ) { edge = this . _out_edges [ key ] ; nexts . push ( { node : edge . getNodes ( ) . b , edge : edge , } ) ; } } return nexts ; } /**
     * return the set of all nodes that are
     * connected to this node via undirected edges
     */ connNodes ( ) { let conns = [ ] ; let key , edge ; for ( key in this . _und_edges ) { if ( this . _und_edges . hasOwnProperty ( key ) ) { edge = this . _und_edges [ key ] ; let nodes = edge . getNodes ( ) ; if ( nodes . a === this ) { conns . push ( { node : edge . getNodes ( ) . b , edge : edge , } ) ; } else { conns . push ( { node : edge . getNodes ( ) . a , edge : edge , } ) ; } } } return conns ; } param identityFunc returns { Array } reachNodes ( identityFunc ? ) { let identity = 0 ; return $SU . mergeArrays ( [ this . nextNodes ( ) , this . connNodes ( ) ] , identityFunc || ( ne => identity ++ ) ) ; } param identityFunc returns { Array } allNeighbors ( identityFunc ? ) { let identity = 0 ; return $SU . mergeArrays ( [ this . prevNodes ( ) , this . nextNodes ( ) , this . connNodes ( ) ] , identityFunc || function ( ne ) { return identity ++ ; } ) ; } clone ( ) { let new_node = new BaseNode ( this . _id ) ; new_node . _label = this . _label ; new_node . setFeatures ( $SU . clone ( this . getFeatures ( ) ) ) ; return new_node ; } } export { BaseNode } ;