PowerShellUtils/Lib/Extensions/Traversal.cs

using System;
using System.Collections.Generic;
using System.Linq;
using PowerShellStandardModule1.Models;
 
namespace PowerShellStandardModule1.Lib.Extensions;
 
public static class Traversal
{
    public static IEnumerable<T> Bfs<T>(T root, Func<T, IEnumerable<T>> getChildren)
    {
        var queue = new Queue<T>();
        queue.Enqueue(root);
 
        while (queue.Count > 0)
        {
            var dir = queue.Dequeue();
            yield return dir;
            foreach (var subDir in getChildren(dir))
            {
                queue.Enqueue(subDir);
            }
        }
    }
 
    public static IEnumerable<TreeNode<T>> BfsDetailed<T>(T root, Func<T, IEnumerable<T>> getChildren) =>
        BfsDetailed(root, getChildren.WithTreeNodeAdapter());
 
    public static IEnumerable<TreeNode<T>> BfsDetailed<T>(T root, Func<TreeNode<T>, IEnumerable<T>> getChildren) =>
        BfsDetailed(root, getChildren.WithTreeNodeAdapter());
 
    private static IEnumerable<TreeNode<T>> BfsDetailed<T>(
        T root,
        Func<TreeNode<T>, IEnumerable<TreeNode<T>>> adaptedGetter
    )
    {
        var item = new TreeNode<T> { Value = root };
        var queue = new Queue<TreeNode<T>>();
        queue.Enqueue(item);
 
        while (queue.Count > 0)
        {
            var node = queue.Dequeue();
 
            yield return node;
             
 
            var children = adaptedGetter(node).ToList();
 
            node.Children = children;
            queue.EnqueueRange(children);
        }
    }
 
    public static IEnumerable<T> Dfs<T>(T root, Func<T, IEnumerable<T>> getChildren)
    {
        var stack = Stack.From([root]);
 
        while (stack.NotEmpty())
        {
            var current = stack.Pop();
            yield return current;
 
            stack.PushRange(getChildren(current));
        }
    }
 
    public static IEnumerable<T> DfsDetailed<T>(T root, Func<T, IEnumerable<T>> getChildren)
    {
        var stack = Stack.From([new TreeNode<T> { Value = root }]);
 
        var adaptedGetter = getChildren.WithTreeNodeAdapter();
 
        while (stack.NotEmpty())
        {
            var current = stack.Pop();
            yield return current.Value;
 
            var children = adaptedGetter(current).ToList();
 
            current.Children = children;
            stack.PushRange(children);
        }
    }
 
    public static Func<TreeNode<T>, IEnumerable<TreeNode<T>>> WithTreeNodeAdapter<T>(
        this Func<TreeNode<T>, IEnumerable<T>> getChildrenFromTreeNode
    ) =>
        node => TreeNodeAdapter(node, getChildrenFromTreeNode);
 
    public static Func<TreeNode<T>, IEnumerable<TreeNode<T>>> WithTreeNodeAdapter<T>(
        this Func<T, IEnumerable<T>> getChildrenFromValue
    ) =>
        node => TreeNodeAdapter(node, n => getChildrenFromValue(n.Value));
 
    private static IEnumerable<TreeNode<T>> TreeNodeAdapter<T>(
        TreeNode<T> node,
        Func<TreeNode<T>, IEnumerable<T>> getChildren
    )
    {
        var parentHeightPlusOne = node.Height + 1;
 
        return getChildren(node)
           .Select(
                (x, i) => new TreeNode<T>
                {
                    Value = x,
                    Height = parentHeightPlusOne,
                    Parent = node,
                    Index = i
                }
            );
    }
}