using System.Diagnostics.CodeAnalysis; namespace InnovEnergy.Lib.Utils; public static class EnumerableUtils { public static T? FirstOrNullableDefault(this IEnumerable ts, Func predicate) where T : struct { return ts .Where(predicate) .OfType() .FirstOrDefault(); } public static Int32 SequenceHash(this IEnumerable ts) { var hash = new HashCode(); hash.Add(typeof(T).GetHashCode()); foreach (var t in ts) hash.Add(t?.GetHashCode()); return hash.ToHashCode(); } public static IList Push(this IList l, T t) { l.Add(t); return l; } public static T Pop(this IList l) { var i = l.Count - 1; var t = l[i]; l.RemoveAt(i); return t; } // public static async Task> SelectManyAsync(this IEnumerable ts, Func>> func) // { // var whenAll = await Task.WhenAll(ts.Select(func)); // return whenAll.SelectMany(s => s); // } // public static async Task> SelectManyAsync(this IEnumerable ts, // Func>> func) // { // var result = Enumerable.Empty(); // // foreach (var t in ts) // { // result = result.Concat(await func(t)); // } // // return result; // } // public static IEnumerable> SelectAsync(this Task> ts) // { // yield return ts.ContinueWith(r => r.Result.First()); // // foreach (var t in ts.Result.Skip(1)) // yield return Task.FromResult(t); // } public static Queue ToQueue(this IEnumerable ts) { var q = new Queue(); foreach (var t in ts) q.Enqueue(t); return q; } public static Stack ToStack(this IEnumerable ts) { var s = new Stack(); foreach (var t in ts) s.Push(t); return s; } public static async IAsyncEnumerable SelectManyAsync(this IEnumerable ts, Func>> func) { foreach (var t in ts) { var rs = await func(t); foreach (var r in rs) yield return r; } } public static IEnumerable SelectTuple(this IEnumerable<(L l, R r)> tuples, Func map) { return tuples.Select(tuple => map(tuple.l, tuple.r)); } public static Boolean AllEqual(this IEnumerable ts, Func map) { return ts.Select(map).AllEqual(); } public static Boolean AllEqual(this IEnumerable ts) { return !ts.Distinct().Skip(1).Any(); } public static IReadOnlyList NullableToReadOnlyList(this T? t) { return t is null ? Array.Empty() : new[] { t }; } public static IEnumerable NullableToEnumerable(this T? t) { if (t is not null) yield return t; } public static IEnumerable<(T left, T right)> Pairwise(this IEnumerable ts) { using var e = ts.GetEnumerator(); if (!e.MoveNext()) yield break; var left = e.Current; while (e.MoveNext()) { yield return (left, e.Current); left = e.Current; } } public static IEnumerable<(T left, T right)> Pairwise(this IEnumerable ts, T seed) { using var e = ts.GetEnumerator(); var left = seed; while (e.MoveNext()) { yield return (left, e.Current); left = e.Current; } } public static IEnumerable To(this Int32 start, Int32 end) { var d = Math.Sign(end - start); for (var i = start; i != end; i += d) yield return i; } public static IEnumerable To(this UInt32 start, UInt32 end) { var d = Math.Sign(end - start).ConvertTo(); for (var i = start; i != end; i += d) yield return i; } public static IReadOnlyList ToReadOnlyList(this IEnumerable src) { return src switch { T[] a => a, List l => l, _ => src.ToList() }; } public static IReadOnlyList ToReadOnlyList(this IEnumerable src, Int32 size) => ToArray(src, size); public static List ToList(this IEnumerable src, Int32 initialSize) { var list = new List(initialSize); list.AddRange(src); return list; } public static IEnumerable PadEnd(this IEnumerable src, Int32 length, T padding) { using var enumerator = src.GetEnumerator(); while (enumerator.MoveNext() && length-- > 0) yield return enumerator.Current; while (length-- > 0) yield return padding; } public static IEnumerable PadStart(this IEnumerable src, Int32 length, T padding) { return src .Reverse() .PadEnd(length, padding) .Reverse(); } public static IEnumerator AsSingleEnumerator(this T t) { yield return t; } public static IEnumerable AsSingleEnumerable(this T t) { yield return t; } public static IEnumerable Flatten(this IEnumerable> src) => src.SelectMany(s => s); [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] public static IEnumerable ForEach(this IEnumerable enumerable, Action action) { foreach (var e in enumerable) action(e); return enumerable; } public static IEnumerable Do(this IEnumerable enumerable, Action action) { return enumerable.Select(e => { action(e); return e; }); } public static void ForEach(this IEnumerable enumerable, Func func) { foreach (var e in enumerable) func(e); } public static IEnumerable EmptyIfNull(this IEnumerable? ts) { return ts ?? Enumerable.Empty(); } public static T ElementAtOr(this IEnumerable ts, Int32 index, T defaultValue) { return ts.ElementAtOrDefault(index) ?? defaultValue; } public static IEnumerable Unfold(this T? seed, Func next) { var value = seed; while (value is not null) { yield return value; value = next(value); } } public static IEnumerable InfinitelyMany(this T value) { while (true) yield return value; // ReSharper disable once IteratorNeverReturns } public static T[] ToArray(this IEnumerable ts, Int32 n) { if (ts is T[] ta) return ta; var array = new T[n]; var i = 0; using var enumerator = ts.GetEnumerator(); while (i < n && enumerator.MoveNext()) array[i++] = enumerator.Current; if (enumerator.MoveNext()) throw new ArgumentOutOfRangeException(nameof(ts)); return array; } public static T[] ToArray(this IReadOnlyList ts) { return ts as T[] ?? ts.ToArray(ts.Count); } public static IEnumerable Concat(this IEnumerable ts, T last) { foreach (var t in ts) yield return t; yield return last; } public static IEnumerable Many(this T value) { yield return value; } public static IEnumerable PadWith(this IEnumerable enumerable, T padding) { return enumerable.Concat(padding.InfinitelyMany()); } public static IEnumerable PadWith(this IEnumerable enumerable, T padding, Int32 maxLength) { return enumerable .PadWith(padding) .Take(maxLength); } public static IEnumerable Unless(this IEnumerable seq, Func filter) { return seq.Where(e => !filter(e)); } public static IEnumerable Intersperse(this IEnumerable enumerable, T interspersed) { using var e = enumerable.GetEnumerator(); if (!e.MoveNext()) yield break; yield return e.Current; while (e.MoveNext()) { yield return interspersed; yield return e.Current; } } public static T Next(this IEnumerator enumerator) { enumerator.MoveNext(); 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) { 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; var moreInGroup = false; do { yield return GetGroup(); while (moreInGroup) MoveNext(); } while (moreInSeq); void MoveNext() { var prev = e.Current; moreInSeq = e.MoveNext(); moreInGroup = moreInSeq && !splitBetween(prev, e.Current); } IEnumerable GetGroup() { do { yield return e.Current; MoveNext(); } while (moreInGroup); } } public static IEnumerable NotNull(this IEnumerable ts) { return ts.Where(t => t != null)!; } public static IEnumerable RandomWalk() { var rng = new Random(); var x = NextDx(); while (true) { yield return x; x = (x + NextDx()) / 2; } Double NextDx() => rng.NextDouble() * 2 - 1; } public static IEnumerable Integrate(this IEnumerable source, Double initConst = 0) { return source.Scan(initConst, (x, y) => x + y); } public static IEnumerable IntegrateNormalize(this IEnumerable source, Double initConst = 0) { return source.Scan(initConst, (x, y) => Math.Tanh(x + y)); } public static IEnumerable MovingAverage(this IEnumerable source, Int32 windowSize) { if (windowSize < 1) throw new ArgumentException(nameof(windowSize) + " must be positive", nameof(windowSize)); using var e = source.GetEnumerator(); if (!e.MoveNext()) yield break; var state = e.Current; do { state = ((windowSize - 1) * state + e.Current) / windowSize; yield return state; } while (e.MoveNext()); } public static IEnumerable ZeroOrOne(this T? t) { 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); } public static T GetNext(this IReadOnlyList ts, T current) { return ts .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); } }