11/* eslint-disable no-loops/no-loops,unused-imports/no-unused-vars */
2- import { max , min , cloneDeep , sumBy , meanBy } from 'lodash'
2+ import { max , min , cloneDeep , sumBy , meanBy , isEmpty , last } from 'lodash'
3+ import { ErrorInternal } from 'src/helpers/ErrorInternal'
34
45import { PHYLO_GRAPH_NODE_RADIUS } from './constants'
56
@@ -194,11 +195,6 @@ export function traverseBreadthFirst<T>(
194195 return results
195196}
196197
197- export enum DepthFirstTraversalOrder {
198- Pre ,
199- Post ,
200- }
201-
202198// Explore graph in breadth-first fashion, backwards
203199export function traverseBreadthFirstBackwards < T > (
204200 graph : Graph ,
@@ -210,53 +206,87 @@ export function traverseBreadthFirstBackwards<T>(
210206
211207// Explore graph in depth-first pre-order fashion
212208export function traverseDepthFirstPreOrder < T > ( graph : Graph , explorer : ( params : ExplorerParams ) => T ) {
213- const results : T [ ] = [ ]
209+ const roots = getRoots ( graph )
210+ if ( isEmpty ( roots ) ) {
211+ return [ ]
212+ }
213+
214+ const stack : GraphNode [ ] = roots
214215 const explored = new Set < string > ( )
215- return getRoots ( graph ) . forEach ( ( node ) => {
216- traverseDepthFirstRecursively ( graph , node , explorer , results , explored , DepthFirstTraversalOrder . Pre )
217- } )
218- return results
216+ const results = [ ]
217+
218+ // eslint-disable-next-line no-constant-condition
219+ while ( true ) {
220+ const node = stack . pop ( )
221+ if ( ! node ) {
222+ return results
223+ }
224+
225+ const children : [ GraphNode , GraphEdge ] [ ] = getChildren ( graph , node . id )
226+ {
227+ const parents : [ GraphNode , GraphEdge ] [ ] = getParents ( graph , node . id )
228+ const siblings : GraphNode [ ] = getSiblings ( graph , node . id )
229+ const isLeaf = children . length === 0 // Leaf is the node that has no children
230+ const isRoot = parents . length === 0 // Root is the node that has no parents
231+ results . push ( explorer ( { node, children, parents, siblings, isLeaf, isRoot } ) )
232+ explored . add ( node . id )
233+ }
234+
235+ children . reverse ( ) . forEach ( ( [ child ] ) => {
236+ if ( ! explored . has ( child . id ) ) {
237+ stack . push ( child )
238+ explored . add ( child . id )
239+ }
240+ } )
241+ }
219242}
220243
221244// Explore graph in depth-first post-order fashion
222245export function traverseDepthFirstPostOrder < T > ( graph : Graph , explorer : ( params : ExplorerParams ) => T ) {
223- const results : T [ ] = [ ]
224- const explored = new Set < string > ( )
225- return getRoots ( graph ) . forEach ( ( node ) => {
226- traverseDepthFirstRecursively ( graph , node , explorer , results , explored , DepthFirstTraversalOrder . Post )
227- } )
228- return results
229- }
246+ const roots = getRoots ( graph )
230247
231- // Implements graph traversal in depth-first pre-order or post-order fashion. Recursive implementation.
232- function traverseDepthFirstRecursively < T > (
233- graph : Graph ,
234- node : GraphNode ,
235- explorer : ( params : ExplorerParams ) => T ,
236- results : T [ ] ,
237- explored : Set < string > ,
238- order : DepthFirstTraversalOrder ,
239- ) {
240- const parents : [ GraphNode , GraphEdge ] [ ] = getParents ( graph , node . id )
241- const children : [ GraphNode , GraphEdge ] [ ] = getChildren ( graph , node . id )
242- const siblings : GraphNode [ ] = getSiblings ( graph , node . id )
243- const isLeaf = children . length === 0 // Leaf is the node that has no children
244- const isRoot = parents . length === 0 // Root is the node that has no parents
245-
246- // Explore current node if pre-order
247- if ( order === DepthFirstTraversalOrder . Pre ) {
248- explored . add ( node . id )
249- results . push ( explorer ( { node, children, parents, siblings, isLeaf, isRoot } ) )
248+ if ( isEmpty ( roots ) ) {
249+ return [ ]
250250 }
251251
252- // Explore child nodes recursively
253- children . forEach ( ( [ child ] ) => traverseDepthFirstRecursively ( graph , child , explorer , results , explored , order ) )
252+ // TODO: Implement post-order traversal with multiple roots
253+ if ( roots . length > 1 ) {
254+ throw new ErrorInternal ( 'Not implemented: multiple tree roots are not yet implmented' )
255+ }
256+ const root = roots [ 0 ]
254257
255- // Explore current node if post-order
256- if ( order === DepthFirstTraversalOrder . Post ) {
257- explored . add ( node . id )
258- results . push ( explorer ( { node, children, parents, siblings, isLeaf, isRoot } ) )
258+ const stack : GraphNode [ ] = [ ]
259+ const explored = new Set < string > ( )
260+ const results = [ ]
261+ stack . push ( root )
262+
263+ let node
264+ while ( ( node = last ( stack ) ) ) {
265+ const children : [ GraphNode , GraphEdge ] [ ] = getChildren ( graph , node . id )
266+
267+ let isTail = true
268+ for ( const [ child , _ ] of children ) {
269+ if ( ! explored . has ( child . id ) ) {
270+ isTail = false
271+ stack . push ( child )
272+ explored . add ( child . id )
273+ break
274+ }
275+ }
276+
277+ if ( isTail ) {
278+ stack . pop ( )
279+
280+ const parents : [ GraphNode , GraphEdge ] [ ] = getParents ( graph , node . id )
281+ const siblings : GraphNode [ ] = getSiblings ( graph , node . id )
282+ const isLeaf = children . length === 0 // Leaf is the node that has no children
283+ const isRoot = parents . length === 0 // Root is the node that has no parents
284+
285+ results . push ( explorer ( { node, children, parents, siblings, isLeaf, isRoot } ) )
286+ explored . add ( node . id )
287+ }
259288 }
289+ return results
260290}
261291
262292export function getNodesForEdge ( graph : Graph , edge : GraphEdge ) {
0 commit comments