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;
}
}
}
}
}
Following #3331
Still working on the comments and review of the class.