diff --git a/csharp/App/Backend/DataTypes/Methods/Credentials.cs b/csharp/App/Backend/DataTypes/Methods/Credentials.cs index 1a8e24703..2e29149b1 100644 --- a/csharp/App/Backend/DataTypes/Methods/Credentials.cs +++ b/csharp/App/Backend/DataTypes/Methods/Credentials.cs @@ -8,12 +8,14 @@ public static class CredentialsMethods { public static Session? Login(this Credentials credentials) { - if (credentials.Username.IsNullOrEmpty() || credentials.Password.IsNullOrEmpty()) + var (username, password) = credentials; + + if (username.IsNullOrEmpty() || password.IsNullOrEmpty()) return null; - var user = Db.GetUserByEmail(credentials.Username); + var user = Db.GetUserByEmail(username); - if (user is null || !user.VerifyPassword(credentials.Password)) + if (user is null || !user.VerifyPassword(password)) return null; var session = new Session(user); diff --git a/csharp/App/Backend/DataTypes/Methods/Folder.cs b/csharp/App/Backend/DataTypes/Methods/Folder.cs index 321aca9eb..f643856ff 100644 --- a/csharp/App/Backend/DataTypes/Methods/Folder.cs +++ b/csharp/App/Backend/DataTypes/Methods/Folder.cs @@ -21,7 +21,9 @@ public static class FolderMethods public static IEnumerable DescendantFolders(this Folder parent) { - return parent.Traverse(ChildFolders); + return parent + .TraverseDepthFirstPreOrder(ChildFolders) + .Skip(1); // skip self } public static Boolean IsDescendantOf(this Folder folder, Folder ancestor) diff --git a/csharp/App/Backend/DataTypes/Methods/User.cs b/csharp/App/Backend/DataTypes/Methods/User.cs index b7498ccfe..e541dcdd7 100644 --- a/csharp/App/Backend/DataTypes/Methods/User.cs +++ b/csharp/App/Backend/DataTypes/Methods/User.cs @@ -73,7 +73,9 @@ public static class UserMethods public static IEnumerable DescendantUsers(this User parent) { - return parent.Traverse(ChildUsers); + return parent + .TraverseDepthFirstPreOrder(ChildUsers) + .Skip(1); // skip self } public static Boolean IsDescendantOf(this User user, User ancestor) diff --git a/csharp/App/Backend/Program.cs b/csharp/App/Backend/Program.cs index d32326b7a..e7500ff1a 100644 --- a/csharp/App/Backend/Program.cs +++ b/csharp/App/Backend/Program.cs @@ -1,4 +1,3 @@ -using System.Reactive.Linq; using InnovEnergy.App.Backend.Database; using Microsoft.OpenApi.Models; @@ -8,7 +7,6 @@ public static class Program { public static void Main(String[] args) { - Db.CreateFakeRelations(); var builder = WebApplication.CreateBuilder(args); diff --git a/csharp/Lib/Utils/TreeTraversal.cs b/csharp/Lib/Utils/TreeTraversal.cs new file mode 100644 index 000000000..4fef44baf --- /dev/null +++ b/csharp/Lib/Utils/TreeTraversal.cs @@ -0,0 +1,147 @@ +namespace InnovEnergy.Lib.Utils; + +public static class TreeTraversal +{ + // public static IEnumerable TraverseDepthFirstPreOrder(this T root, Func> getChildren) + // { + // var stack = new Stack>(); + // + // var iterator = root.AsSingleEnumerator(); + // + // while (true) + // { + // while (iterator.MoveNext()) + // { + // yield return iterator.Current; + // + // iterator = getChildren(iterator.Current).GetEnumerator(); + // stack.Push(iterator); + // } + // + // iterator.Dispose(); + // if (stack.Count == 0) + // yield break; + // + // iterator = stack.Pop(); + // } + // + // } + + public static IEnumerable TraverseDepthFirstPreOrder(this T root, Func> getChildren) + { + return Traverse(root, + getChildren, + branchOpen: true, + branchClose: false, + leaf: true); + } + + public static IEnumerable TraverseDepthFirstPostOrder(this T root, Func> getChildren) + { + return Traverse(root, + getChildren, + branchOpen: false, + branchClose: true, + leaf: true); + } + + public static IEnumerable TraverseLeaves(this T root, Func> getChildren) + { + return Traverse(root, + getChildren, + branchOpen: false, + branchClose: true, + leaf: false); + } + + + private static IEnumerable Traverse(T root, + Func> getChildren, + Boolean branchOpen, + Boolean branchClose, + Boolean leaf) + { + // the if-checks on the constant booleans are + // almost free because of modern branch predictors + + var stack = new Stack>(); + var it = root.AsSingleEnumerator(); + it.MoveNext(); + + while (true) + { + //////// going down //////// + + while (true) + { + var cit = getChildren(it.Current).GetEnumerator(); + + if (cit.MoveNext()) // node has children, must be a branch + { + if (branchOpen) + yield return it.Current; + + stack.Push(it); + it = cit; + } + else // no children, hence a leaf + { + var node = it.Current; + + if (leaf) + yield return node; + + if (!it.MoveNext()) + break; // no more siblings: goto parent + } + } + + //////// going up //////// + + while (true) + { + it.Dispose(); + if (stack.Count == 0) yield break; // we got to the bottom of the stack, were done + + it = stack.Pop(); + + var node = it.Current; + + if (branchClose) + yield return node; // we've seen all its children: close the branch + + if (it.MoveNext()) + break; + } + } + } + + + public static IEnumerable TraverseBreadthFirst(this T node, Func> getChildren) + { + var queue = new Queue>(); + + var iterator = node.AsSingleEnumerator(); + + while(true) + { + while (iterator.MoveNext()) + { + yield return iterator.Current; + + iterator.Current + .Apply(getChildren) + .GetEnumerator() + .Apply(queue.Enqueue); + } + + iterator.Dispose(); + + if (queue.Count == 0) + yield break; + + iterator = queue.Dequeue(); + } + + } +} \ No newline at end of file diff --git a/csharp/Lib/Utils/Utils.cs b/csharp/Lib/Utils/Utils.cs index 690d6abaa..c76476a35 100644 --- a/csharp/Lib/Utils/Utils.cs +++ b/csharp/Lib/Utils/Utils.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using static System.Runtime.CompilerServices.MethodImplOptions; @@ -6,6 +7,7 @@ namespace InnovEnergy.Lib.Utils; public static class Utils { + public static Boolean IsNull([NotNullWhen(returnValue: false)] this T t) => t is null; public static IEnumerable GetEnumStrings(this T e) where T : Enum { @@ -20,7 +22,7 @@ public static class Utils [DebuggerStepThrough][MethodImpl(AggressiveInlining)] public static T ConvertTo(this IConvertible c) where T : IConvertible { - var t = typeof (T); + var t = typeof(T); var type = t.IsEnum ? Enum.GetUnderlyingType(t) @@ -50,13 +52,13 @@ public static class Utils [DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)] public static R Apply(this T t, Func f) => f(t); - [DebuggerStepThrough] - [MethodImpl(AggressiveInlining | AggressiveOptimization)] + + [DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)] public static R Apply(this (T1 p1, T2 p2) t, Func f) => f(t.p1, t.p2); - [DebuggerStepThrough] - [MethodImpl(AggressiveInlining | AggressiveOptimization)] - public static R Apply(this (T1 p1, T2 p2, T3 p3) t, Func f) => f(t.p1, t.p2, t.p3); + + [DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)] + public static R Apply(this (T1 p1, T2 p2, T3 p3) t, Func f) => f(t.p1, t.p2, t.p3); [DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)] public static R ApplyOrDefault(this T t, Func f, R @default) @@ -79,7 +81,7 @@ public static class Utils ? res : res + length; } - + public static Decimal Modulo(this Decimal dividend, Decimal divisor) { var res = dividend % divisor; @@ -89,111 +91,39 @@ public static class Utils : res + divisor; } - public static IEnumerable Traverse(this T root, Func> getChildren) - { - var stack = new Stack>(); - var it = root.AsSingleEnumerator(); - it.MoveNext(); - - while (true) - { - //////// going down //////// - - while (true) - { - var cit = getChildren(it.Current).GetEnumerator(); - - if (cit.MoveNext()) // node has children, must be a branch - { - yield return it.Current; - - stack.Push(it); - it = cit; - } - else // no children, hence a leaf - { - var node = it.Current; - - yield return node; - - if (!it.MoveNext()) - break; // no more siblings: goto parent - } - } - - //////// going up //////// - - while (true) - { - it.Dispose(); - if (stack.Count == 0) - yield break; // we got to the bottom of the stack, were done - - it = stack.Pop(); - - if (it.MoveNext()) - break; - } - } - - } public static Int32 Clamp(this Int32 value, Int32 minValue, Int32 maxValue) { var clamped = Math.Min(maxValue, value); clamped = Math.Max(minValue, clamped); - + return clamped; } - + public static Double Clamp(this Double value, Double minValue, Double maxValue) { var clamped = Math.Min(maxValue, value); clamped = Math.Max(minValue, clamped); - + return clamped; } - - + + public static Decimal Clamp(this Decimal value, Decimal minValue, Decimal maxValue) { var clamped = Math.Min(maxValue, value); clamped = Math.Max(minValue, clamped); - + return clamped; } - -#pragma warning disable 8714 - - public static async Task ApplyOrDefault(this T t, Func> f, R @default) - { - try - { - return await f(t); - } - catch - { - return @default; - } - } - - public static R ValueOrDefault(this Dictionary dict, T key, R defaultValue) + public static R ValueOrDefault(this Dictionary dict, T key, R defaultValue) where T : notnull { return dict.TryGetValue(key, out var value) - ? value - : defaultValue; + ? value + : defaultValue; } - - public static Dictionary CombineDicts(params IEnumerable>[] dicts) - { - return dicts.Flatten().ToDictionary(kv => kv.Key, kv => kv.Value); - } - -#pragma warning restore 8714 - - public static void CopyFilesRecursively(String source, String target) { CopyFilesRecursively(new DirectoryInfo(source), new DirectoryInfo(target)); @@ -210,27 +140,4 @@ public static class Utils public static String ExecutingProcessName => Process.GetCurrentProcess().ProcessName; - - public static IEnumerable> TraverseWithPath(this T root, Func> getChildren) - { - var stack = new Stack>(); - stack.Push(root.AsSingleEnumerator()); - - do - { - for (var top = stack.Peek(); top.MoveNext(); top = Push(top)) - yield return stack.Select(p => p.Current); - - var popped = stack.Pop(); - popped.Dispose(); - } - while (stack.Count > 0); - - IEnumerator Push(IEnumerator node) - { - var top = getChildren(node.Current).GetEnumerator(); - stack.Push(top); - return top; - } - } } \ No newline at end of file