From f4989f0c6f76cf8a7a159ca79625e0d24d24b75e Mon Sep 17 00:00:00 2001 From: ig Date: Tue, 13 Jun 2023 13:04:07 +0200 Subject: [PATCH] update Utils --- csharp/Lib/Utils/Combinator.cs | 15 +++ csharp/Lib/Utils/EnumerableUtils.cs | 120 ++++++++++++------- csharp/Lib/Utils/StringUtils.cs | 15 +-- csharp/Lib/Utils/TextBlock.cs | 64 +++++----- csharp/Lib/Utils/TreeTraversal.cs | 87 ++++++++++++-- csharp/Lib/Utils/Utils.cs | 23 ++-- csharp/Lib/Utils/WIP/ObservablePipeTarget.cs | 1 - csharp/Lib/Utils/WIP/Result.cs | 9 ++ 8 files changed, 233 insertions(+), 101 deletions(-) create mode 100644 csharp/Lib/Utils/Combinator.cs create mode 100644 csharp/Lib/Utils/WIP/Result.cs diff --git a/csharp/Lib/Utils/Combinator.cs b/csharp/Lib/Utils/Combinator.cs new file mode 100644 index 000000000..68b5c09bb --- /dev/null +++ b/csharp/Lib/Utils/Combinator.cs @@ -0,0 +1,15 @@ +namespace InnovEnergy.Lib.Utils; + +internal delegate Func Recursive(Recursive r); + + + +public static class Combinator +{ + public static Func Y(Func, Func> f) + { + Func Rec(Recursive r) => t => f(r(r))(t); + + return Rec(Rec); + } +} \ No newline at end of file diff --git a/csharp/Lib/Utils/EnumerableUtils.cs b/csharp/Lib/Utils/EnumerableUtils.cs index 6811c7e30..10d777f5b 100644 --- a/csharp/Lib/Utils/EnumerableUtils.cs +++ b/csharp/Lib/Utils/EnumerableUtils.cs @@ -4,13 +4,12 @@ namespace InnovEnergy.Lib.Utils; public static class EnumerableUtils { - - public static T? FirstOrNullableDefault(this IEnumerable ts, Func predicate) where T : struct + public static T? FirstOrNullableDefault(this IEnumerable ts, Func predicate) where T : struct { return ts - .Where(predicate) - .OfType() - .FirstOrDefault(); + .Where(predicate) + .OfType() + .FirstOrDefault(); } public static Int32 SequenceHash(this IEnumerable ts) @@ -44,7 +43,6 @@ public static class EnumerableUtils // } - // public static async Task> SelectManyAsync(this IEnumerable ts, // Func>> func) // { @@ -68,8 +66,6 @@ public static class EnumerableUtils // } - - public static Queue ToQueue(this IEnumerable ts) { var q = new Queue(); @@ -78,7 +74,7 @@ public static class EnumerableUtils return q; } - + public static Stack ToStack(this IEnumerable ts) { var s = new Stack(); @@ -106,7 +102,7 @@ public static class EnumerableUtils } - public static Boolean AllEqual(this IEnumerable ts, Func map) + public static Boolean AllEqual(this IEnumerable ts, Func map) { return ts.Select(map).AllEqual(); } @@ -118,7 +114,7 @@ public static class EnumerableUtils public static IReadOnlyList NullableToReadOnlyList(this T? t) { - return t is null ? Array.Empty() : new[] {t}; + return t is null ? Array.Empty() : new[] { t }; } public static IEnumerable NullableToEnumerable(this T? t) @@ -143,7 +139,7 @@ public static class EnumerableUtils } } - + public static IEnumerable<(T left, T right)> Pairwise(this IEnumerable ts, T seed) { using var e = ts.GetEnumerator(); @@ -158,13 +154,11 @@ public static class EnumerableUtils } - - public static IEnumerable To(this Int32 start, Int32 end) { var d = Math.Sign(end - start); - for (var i = start; i != end; i+= d) + for (var i = start; i != end; i += d) yield return i; } @@ -172,7 +166,7 @@ public static class EnumerableUtils { var d = Math.Sign(end - start).ConvertTo(); - for (var i = start; i != end; i+= d) + for (var i = start; i != end; i += d) yield return i; } @@ -180,9 +174,9 @@ public static class EnumerableUtils { return src switch { - T[] a => a, + T[] a => a, List l => l, - _ => src.ToList() + _ => src.ToList() }; } @@ -210,9 +204,9 @@ public static class EnumerableUtils public static IEnumerable PadStart(this IEnumerable src, Int32 length, T padding) { return src - .Reverse() - .PadEnd(length, padding) - .Reverse(); + .Reverse() + .PadEnd(length, padding) + .Reverse(); } @@ -246,7 +240,7 @@ public static class EnumerableUtils }); } - public static void ForEach(this IEnumerable enumerable, Func func) + public static void ForEach(this IEnumerable enumerable, Func func) { foreach (var e in enumerable) func(e); @@ -262,7 +256,7 @@ public static class EnumerableUtils return ts.ElementAtOrDefault(index) ?? defaultValue; } - public static IEnumerable Unfold(this T seed, Func next) + public static IEnumerable Unfold(this T? seed, Func next) { var value = seed; while (value is not null) @@ -323,8 +317,8 @@ public static class EnumerableUtils public static IEnumerable PadWith(this IEnumerable enumerable, T padding, Int32 maxLength) { return enumerable - .PadWith(padding) - .Take(maxLength); + .PadWith(padding) + .Take(maxLength); } @@ -337,7 +331,7 @@ public static class EnumerableUtils { using var e = enumerable.GetEnumerator(); - if (! e.MoveNext()) yield break; + if (!e.MoveNext()) yield break; yield return e.Current; @@ -354,30 +348,45 @@ public static class EnumerableUtils return enumerator.Current; } + public static IEnumerable Scan(this IEnumerable src, Func next) + { + using var e = src.GetEnumerator(); + + if (!e.MoveNext()) + yield break; + + var state = e.Current; + yield return state; + + while(e.MoveNext()) + { + state = next(state, e.Current); + yield return state; + } + + } + public static IEnumerable Scan(this IEnumerable src, R seed, Func next) { var current = seed; foreach (var t in src) { - current = next(current, t); yield return current; + current = next(current, t); } } - - - [SuppressMessage("ReSharper", "AccessToDisposedClosure")] public static IEnumerable> GroupUntil(this IEnumerable sequence, Func splitBetween) { using var e = sequence.GetEnumerator(); - - if(!e.MoveNext()) - yield break; // empty sequence - var moreInSeq = false; + if (!e.MoveNext()) + yield break; // empty sequence + + var moreInSeq = false; var moreInGroup = false; do @@ -390,7 +399,7 @@ public static class EnumerableUtils void MoveNext() { - var prev = e.Current; + var prev = e.Current; moreInSeq = e.MoveNext(); moreInGroup = moreInSeq && !splitBetween(prev, e.Current); } @@ -400,7 +409,7 @@ public static class EnumerableUtils do { yield return e.Current; - MoveNext(); + MoveNext(); } while (moreInGroup); } @@ -464,26 +473,49 @@ public static class EnumerableUtils if (t is not null) yield return t; } - + public static IEnumerable> Transpose(this IEnumerable> src) { return src - .SelectMany(line => line.Select((element, column) => (element, column))) - .GroupBy(i => i.column, i => i.element); + .SelectMany(line => line.Select((element, column) => (element, column))) + .GroupBy(i => i.column, i => i.element); } - + public static T GetNext(this IReadOnlyList ts, T current) { return ts - .Concat(ts) - .SkipWhile(t => !t!.Equals(current)) - .Skip(1) - .First(); + .Concat(ts) + .SkipWhile(t => !t!.Equals(current)) + .Skip(1) + .First(); } public static T GetPrevious(this IReadOnlyList ts, T current) { return GetNext(ts.Reverse().ToArray(ts.Count), current); } + + + public static IEnumerable Memoize(this IEnumerable src, Func map) + { + var cache = new List(); + + return src + .Select((e, i) => i < cache.Count + ? cache[i] + : map(e).Apply(cache.Add)); + } + + + public static HashSet ToHashSet(this IEnumerable source, IEqualityComparer? comparer = null) + { + return new HashSet(source, comparer); + } + + public static IEnumerable Times(this Int32 n) + { + return Enumerable.Range(0, n); + } + } \ No newline at end of file diff --git a/csharp/Lib/Utils/StringUtils.cs b/csharp/Lib/Utils/StringUtils.cs index b98fbf53c..763a164a0 100644 --- a/csharp/Lib/Utils/StringUtils.cs +++ b/csharp/Lib/Utils/StringUtils.cs @@ -1,4 +1,3 @@ -using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Runtime.CompilerServices; using static System.Math; @@ -69,21 +68,12 @@ public static class StringUtils .Aggregate(s, (a, b) => a + b); } - public static String AppendSpaces(this String s, Int32 count = 4) - { - return s.PadRight(s.Length + count); - } public static String Append(this String s, String other) { return s + other; } - public static String Concat(this IEnumerable ss) - { - return String.Join("", ss); - } - public static String SurroundWith(this String s, String beforeAfter) { return beforeAfter + s + beforeAfter; @@ -136,7 +126,10 @@ public static class StringUtils public static String Indent(this String text, String indent) { - return indent.SideBySideWith(text, ""); + return Enumerable + .Repeat(indent, text.SplitLines().Count) + .JoinLines() + .SideBySideWith(text, ""); } public static IReadOnlyList SplitLines(this String s) diff --git a/csharp/Lib/Utils/TextBlock.cs b/csharp/Lib/Utils/TextBlock.cs index d5bfa2869..e3a11f1c6 100644 --- a/csharp/Lib/Utils/TextBlock.cs +++ b/csharp/Lib/Utils/TextBlock.cs @@ -6,48 +6,56 @@ public class TextBlock public IReadOnlyList Lines { get; } - public Int32 Width => Lines.FirstOrDefault()?.Length ?? 0; + public Int32 Width => Lines.Count == 0 + ? 0 + : Lines.Max(l=>l.Length); + public Int32 Height => Lines.Count; - private TextBlock(IReadOnlyList lines) => Lines = lines; + public TextBlock(IReadOnlyList lines) => Lines = lines; - public static TextBlock AlignLeft (params Object[] lines) => AlignHorizontal((l, w) => l.PadRight(w), lines); - public static TextBlock AlignRight(params Object[] lines) => AlignHorizontal((l, w) => l.PadLeft(w), lines); - public static TextBlock AlignHCenter(params Object[] lines) => AlignHorizontal((l,w) => l.PadLeft((w + l.Length) / 2).PadRight(w), lines); + public TextBlock AlignLeft (Int32 width = -1) => AlignHorizontal((l, w) => l.PadRight(w), width); + public TextBlock AlignRight (Int32 width = -1) => AlignHorizontal((l, w) => l.PadLeft(w), width); + public TextBlock AlignHCenter(Int32 width = -1) => AlignHorizontal((l, w) => l.PadLeft((w + l.Length) / 2).PadRight(w), width); + + public TextBlock AlignTop (Int32 height) => Lines.Concat(EmptyLines(height)).Take(height).ToArray(height); + public TextBlock AlignBottom (Int32 height) => EmptyLines(Lines.Count - height).Concat(Lines).Take(height).ToArray(height); + //public TextBlock AlignVCenter(Int32 height) => EmptyLines(height/2).Concat(Lines).Take(height).ToArray(height); + //public TextBlock AlignVCenter(Int32 height) => AlignHorizontal((l, w) => l.PadLeft((w + l.Length) / 2).PadRight(w), width); - // public static TextBlock AlignTop(params Object[] columns) - // { - // if (!columns.Any()) - // return Empty; - // - // var cs = columns.Select(GetLines).ToArray(columns.Length); - // var ws = cs.Select(c => c.Count).ToArray(cs.Length); - // - // - // - // - // } - - - public static TextBlock HSpace(Int32 width) => new TextBlock(new []{"".PadRight(width)}); - public static TextBlock VSpace(Int32 height) => new TextBlock(Enumerable.Repeat("", height).ToArray(height)); - public static TextBlock Space(Int32 width, Int32 height) + private static IEnumerable EmptyLines(Int32 height) { - return new TextBlock(Enumerable.Repeat("".PadRight(width), height).ToArray(height)); + return height > 0 + ? Enumerable.Repeat("", height) + : Enumerable.Empty(); } - private static TextBlock AlignHorizontal(Func alignLine, Object[] lines) + + public static TextBlock HSpace(Int32 width ) => new TextBlock(new []{"".PadRight(width)}); + public static TextBlock VSpace(Int32 height) => new TextBlock(Enumerable.Repeat("", height).ToArray(height)); + + public static TextBlock Space(Int32 width, Int32 height) { - if (!lines.Any()) + var lines = Enumerable + .Repeat("".PadRight(width), height) + .ToArray(height); + + return new TextBlock(lines); + } + + private TextBlock AlignHorizontal(Func alignLine, Int32 width = -1) + { + if (!Lines.Any()) return Empty; - var strings = lines + var strings = Lines .SelectMany(GetLines) .ToList(); - var width = strings.Max(l => l.Length); + width = width < 0 ? strings.Max(l => l.Length) : width; var aligned = strings + .Select(l => l.Length > width ? l.Substring(0, width) : l) .Select(l => alignLine(l, width)) .ToArray(strings.Count); @@ -63,4 +71,6 @@ public class TextBlock } public override String ToString() => String.Join(Environment.NewLine, Lines); + + public static implicit operator TextBlock(String[] lines) => new TextBlock(lines); } diff --git a/csharp/Lib/Utils/TreeTraversal.cs b/csharp/Lib/Utils/TreeTraversal.cs index 4fef44baf..2a23eb887 100644 --- a/csharp/Lib/Utils/TreeTraversal.cs +++ b/csharp/Lib/Utils/TreeTraversal.cs @@ -24,7 +24,6 @@ public static class TreeTraversal // // iterator = stack.Pop(); // } - // // } public static IEnumerable TraverseDepthFirstPreOrder(this T root, Func> getChildren) @@ -50,16 +49,27 @@ public static class TreeTraversal return Traverse(root, getChildren, branchOpen: false, - branchClose: true, - leaf: false); + branchClose: false, + leaf: true); + } + + + public static IEnumerable<(T node, IEnumerable path)> TraverseLeavesWithPath(this T root, Func> getChildren) + { + return TraverseWithPath(root, + getChildren, + branchOpen: false, + branchClose: false, + leaf: true); } - private static IEnumerable Traverse(T root, - Func> getChildren, - Boolean branchOpen, - Boolean branchClose, - Boolean leaf) + + public 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 @@ -76,7 +86,7 @@ public static class TreeTraversal { var cit = getChildren(it.Current).GetEnumerator(); - if (cit.MoveNext()) // node has children, must be a branch + if (cit.MoveNext()) // node has children, must be a branch (and not a leaf) { if (branchOpen) yield return it.Current; @@ -101,7 +111,7 @@ public static class TreeTraversal while (true) { it.Dispose(); - if (stack.Count == 0) yield break; // we got to the bottom of the stack, were done + if (stack.Count == 0) yield break; // we got to the bottom of the stack, we're done it = stack.Pop(); @@ -116,6 +126,63 @@ public static class TreeTraversal } } + public static IEnumerable<(T node, IEnumerable path)> TraverseWithPath(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 (and not a leaf) + { + if (branchOpen) + yield return (node: it.Current, path: stack.Select(e => e.Current)); + + stack.Push(it); + it = cit; + } + else // no children, hence a leaf + { + if (leaf) + yield return (node: it.Current, path: stack.Select(e => e.Current)); + + 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, we're done + + it = stack.Pop(); + + if (branchClose) + yield return (node: it.Current, path: stack.Select(e => e.Current)); // we've seen all its children: close the branch + + if (it.MoveNext()) + break; + } + } + } + public static IEnumerable TraverseBreadthFirst(this T node, Func> getChildren) { diff --git a/csharp/Lib/Utils/Utils.cs b/csharp/Lib/Utils/Utils.cs index 6df10758b..c98953484 100644 --- a/csharp/Lib/Utils/Utils.cs +++ b/csharp/Lib/Utils/Utils.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Runtime.CompilerServices; using static System.Runtime.CompilerServices.MethodImplOptions; @@ -20,15 +21,19 @@ public static class Utils } [DebuggerStepThrough][MethodImpl(AggressiveInlining)] - public static T ConvertTo(this IConvertible c) where T : IConvertible + public static T ConvertTo(this IConvertible convertible) where T : IConvertible { - var t = typeof(T); + return (T)ConvertTo(convertible, typeof(T)); + } - var type = t.IsEnum - ? Enum.GetUnderlyingType(t) - : t; + [DebuggerStepThrough][MethodImpl(AggressiveInlining)] + public static Object ConvertTo(this IConvertible convertible, Type type) + { + var t = type.IsEnum + ? Enum.GetUnderlyingType(type) + : type; - return (T) Convert.ChangeType(c, type); + return Convert.ChangeType(convertible, t, CultureInfo.InvariantCulture); } public static T Do(this T t, Action action) @@ -57,11 +62,10 @@ public static class Utils [DebuggerStepThrough][MethodImpl(AggressiveInlining | AggressiveOptimization)] public static R Apply(this T t, Func f) => f(t); - + [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); @@ -146,4 +150,7 @@ public static class Utils public static String ExecutingProcessName => Process.GetCurrentProcess().ProcessName; + + + } \ No newline at end of file diff --git a/csharp/Lib/Utils/WIP/ObservablePipeTarget.cs b/csharp/Lib/Utils/WIP/ObservablePipeTarget.cs index ba7e41a50..37b5888a9 100644 --- a/csharp/Lib/Utils/WIP/ObservablePipeTarget.cs +++ b/csharp/Lib/Utils/WIP/ObservablePipeTarget.cs @@ -6,7 +6,6 @@ namespace InnovEnergy.Lib.Utils.WIP; using Data = IReadOnlyList; - public class ObservablePipeTarget : PipeTarget, IObservable { private readonly Subject _Data = new(); diff --git a/csharp/Lib/Utils/WIP/Result.cs b/csharp/Lib/Utils/WIP/Result.cs new file mode 100644 index 000000000..ea0011505 --- /dev/null +++ b/csharp/Lib/Utils/WIP/Result.cs @@ -0,0 +1,9 @@ +namespace InnovEnergy.Lib.Utils.WIP; + + +// TODO: discriminated union +public abstract record Result +{ + public sealed record Ok(Object Result) : Result; + public sealed record Error(String Message) : Result; +} \ No newline at end of file