Skip to content

Comments for Tarjan class #3335

@vncoelho

Description

@vncoelho

Following #3331

Still working on the comments and review of the class.

// Copyright (C) 2015-2024 The Neo Project.
//
// Tarjan.cs file belongs to the neo project and is free
// software distributed under the MIT software license, see the
// accompanying file LICENSE in the main directory of the
// repository or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using System;
using System.Collections.Generic;
using T = Neo.VM.Types.StackItem; // Alias for the StackItem type in Neo.VM.Types

namespace Neo.VM.StronglyConnectedComponents
{
    // Class implementing Tarjan's algorithm for finding SCCs
    class Tarjan
    {
        // List of all vertices in the graph
        private readonly IEnumerable<T> vertices;
        
        // List of all SCCs found
        private readonly LinkedList<HashSet<T>> components = new();
        
        // Stack used to store vertices during the search
        private readonly Stack<T> stack = new();
        
        // Index counter used for assigning DFN (depth-first numbers)
        private int index = 0;

        // Constructor initializing the vertices
        public Tarjan(IEnumerable<T> vertices)
        {
            this.vertices = vertices;
        }

        // Method to invoke the algorithm and return the SCCs
        public LinkedList<HashSet<T>> Invoke()
        {
            // Iterate over all vertices and start the search if the vertex is unvisited (DFN < 0)
            foreach (var v in vertices)
            {
                if (v.DFN < 0)
                {
                    StrongConnectNonRecursive(v);
                }
            }
            return components;
        }

        // Recursive method to find SCCs starting from vertex v
        private void StrongConnect(T v)
        {
            // Initialize DFN and LowLink values for v
            v.DFN = v.LowLink = ++index;
            stack.Push(v);
            v.OnStack = true;

            // Visit all successors of v
            foreach (T w in v.Successors)
            {
                if (w.DFN < 0)
                {
                    // If w is not visited, recursively visit it
                    StrongConnect(w);
                    v.LowLink = Math.Min(v.LowLink, w.LowLink);
                }
                else if (w.OnStack)
                {
                    // If w is on the stack, it means it is part of the current SCC
                    v.LowLink = Math.Min(v.LowLink, w.DFN);
                }
            }

            // If v is a root node, pop the stack and generate an SCC
            if (v.LowLink == v.DFN)
            {
                HashSet<T> scc = new(ReferenceEqualityComparer.Instance);
                T w;
                do
                {
                    w = stack.Pop();
                    w.OnStack = false;
                    scc.Add(w);
                } while (v != w);
                components.AddLast(scc);
            }
        }

        // Non-recursive method to find SCCs starting from vertex v
        private void StrongConnectNonRecursive(T v)
        {
            // Stack to simulate the call stack for the non-recursive approach
            Stack<(T node, T? successor, IEnumerator<T>? enumerator, int state)> sstack = new();
            sstack.Push((v, null, null, 0));
            
            while (sstack.TryPop(out var state))
            {
                v = state.node;
                var (_, w, s, n) = state;
                
                switch (n)
                {
                    case 0:
                        // Initialize DFN and LowLink values for v
                        v.DFN = v.LowLink = ++index;
                        stack.Push(v);
                        v.OnStack = true;
                        s = v.Successors.GetEnumerator();
                        goto case 2;

                    case 1:
                        // Update LowLink value for v after visiting successor w
                        v.LowLink = Math.Min(v.LowLink, w!.LowLink);
                        goto case 2;

                    case 2:
                        // Iterate through all successors of v
                        while (s!.MoveNext())
                        {
                            w = s.Current;
                            if (w.DFN < 0)
                            {
                                // If w is not visited, push the current state and visit w
                                sstack.Push((v, w, s, 1));
                                v = w;
                                goto case 0;
                            }
                            else if (w.OnStack)
                            {
                                // If w is on the stack, it is part of the current SCC
                                v.LowLink = Math.Min(v.LowLink, w.DFN);
                            }
                        }

                        // If v is a root node, pop the stack and generate an SCC
                        if (v.LowLink == v.DFN)
                        {
                            HashSet<T> scc = new(ReferenceEqualityComparer.Instance);
                            do
                            {
                                w = stack.Pop();
                                w.OnStack = false;
                                scc.Add(w);
                            } while (v != w);
                            components.AddLast(scc);
                        }
                        break;
                }
            }
        }
    }
}

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions