using System.Diagnostics.CodeAnalysis; namespace InnovEnergy.Lib.Utils; public static class EnumerableUtils { public static T? FirstOrNullableDefault<T>(this IEnumerable<T> ts, Func<T, Boolean> predicate) where T : struct { return ts .Where(predicate) .OfType<T?>() .FirstOrDefault(); } public static Int32 SequenceHash<T>(this IEnumerable<T> 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<T> Push<T>(this IList<T> l, T t) { l.Add(t); return l; } public static T Pop<T>(this IList<T> l) { var i = l.Count - 1; var t = l[i]; l.RemoveAt(i); return t; } // public static async Task<IEnumerable<R>> SelectManyAsync<T, R>(this IEnumerable<T> ts, Func<T, Task<IEnumerable<R>>> func) // { // var whenAll = await Task.WhenAll(ts.Select(func)); // return whenAll.SelectMany(s => s); // } // public static async Task<IEnumerable<R>> SelectManyAsync<T, R>(this IEnumerable<T> ts, // Func<T, Task<IEnumerable<R>>> func) // { // var result = Enumerable.Empty<R>(); // // foreach (var t in ts) // { // result = result.Concat(await func(t)); // } // // return result; // } // public static IEnumerable<Task<T>> SelectAsync<T>(this Task<IEnumerable<T>> 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<T> ToQueue<T>(this IEnumerable<T> ts) { var q = new Queue<T>(); foreach (var t in ts) q.Enqueue(t); return q; } public static Stack<T> ToStack<T>(this IEnumerable<T> ts) { var s = new Stack<T>(); foreach (var t in ts) s.Push(t); return s; } public static async IAsyncEnumerable<R> SelectManyAsync<T, R>(this IEnumerable<T> ts, Func<T, Task<IEnumerable<R>>> func) { foreach (var t in ts) { var rs = await func(t); foreach (var r in rs) yield return r; } } public static IEnumerable<T> SelectTuple<L, R, T>(this IEnumerable<(L l, R r)> tuples, Func<L, R, T> map) { return tuples.Select(tuple => map(tuple.l, tuple.r)); } public static Boolean AllEqual<T, R>(this IEnumerable<T> ts, Func<T, R> map) { return ts.Select(map).AllEqual(); } public static Boolean AllEqual<T>(this IEnumerable<T> ts) { return !ts.Distinct().Skip(1).Any(); } public static IReadOnlyList<T> NullableToReadOnlyList<T>(this T? t) { return t is null ? Array.Empty<T>() : new[] { t }; } public static IEnumerable<T> NullableToEnumerable<T>(this T? t) { if (t is not null) yield return t; } public static IEnumerable<(T left, T right)> Pairwise<T>(this IEnumerable<T> 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<T>(this IEnumerable<T> 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<Int32> 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<UInt32> To(this UInt32 start, UInt32 end) { var d = Math.Sign(end - start).ConvertTo<UInt32>(); for (var i = start; i != end; i += d) yield return i; } public static IReadOnlyList<T> ToReadOnlyList<T>(this IEnumerable<T> src) { return src switch { T[] a => a, List<T> l => l, _ => src.ToList() }; } public static IReadOnlyList<T> ToReadOnlyList<T>(this IEnumerable<T> src, Int32 size) => ToArray(src, size); public static List<T> ToList<T>(this IEnumerable<T> src, Int32 initialSize) { var list = new List<T>(initialSize); list.AddRange(src); return list; } public static IEnumerable<T> PadEnd<T>(this IEnumerable<T> 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<T> PadStart<T>(this IEnumerable<T> src, Int32 length, T padding) { return src .Reverse() .PadEnd(length, padding) .Reverse(); } public static IEnumerator<T> AsSingleEnumerator<T>(this T t) { yield return t; } public static IEnumerable<T> AsSingleEnumerable<T>(this T t) { yield return t; } public static IEnumerable<T> Flatten<T>(this IEnumerable<IEnumerable<T>> src) => src.SelectMany(s => s); [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumerable, Action<T> action) { foreach (var e in enumerable) action(e); return enumerable; } public static IEnumerable<T> Do<T>(this IEnumerable<T> enumerable, Action<T> action) { return enumerable.Select(e => { action(e); return e; }); } public static void ForEach<T, R>(this IEnumerable<T> enumerable, Func<T, R> func) { foreach (var e in enumerable) func(e); } public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T>? ts) { return ts ?? Enumerable.Empty<T>(); } public static T ElementAtOr<T>(this IEnumerable<T> ts, Int32 index, T defaultValue) { return ts.ElementAtOrDefault(index) ?? defaultValue; } public static IEnumerable<T> Unfold<T>(this T? seed, Func<T, T?> next) { var value = seed; while (value is not null) { yield return value; value = next(value); } } public static IEnumerable<T> InfinitelyMany<T>(this T value) { while (true) yield return value; // ReSharper disable once IteratorNeverReturns } public static T[] ToArray<T>(this IEnumerable<T> 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<T>(this IReadOnlyList<T> ts) { return ts as T[] ?? ts.ToArray(ts.Count); } public static IEnumerable<T> Concat<T>(this IEnumerable<T> ts, T last) { foreach (var t in ts) yield return t; yield return last; } public static IEnumerable<T> Many<T>(this T value) { yield return value; } public static IEnumerable<T> PadWith<T>(this IEnumerable<T> enumerable, T padding) { return enumerable.Concat(padding.InfinitelyMany()); } public static IEnumerable<T> PadWith<T>(this IEnumerable<T> enumerable, T padding, Int32 maxLength) { return enumerable .PadWith(padding) .Take(maxLength); } public static IEnumerable<T> Unless<T>(this IEnumerable<T> seq, Func<T, Boolean> filter) { return seq.Where(e => !filter(e)); } public static IEnumerable<T> Intersperse<T>(this IEnumerable<T> 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<T>(this IEnumerator<T> enumerator) { enumerator.MoveNext(); return enumerator.Current; } public static IEnumerable<T> Scan<T>(this IEnumerable<T> src, Func<T, T, T> 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<R> Scan<T, R>(this IEnumerable<T> src, R seed, Func<R, T, R> next) { var current = seed; foreach (var t in src) { yield return current; current = next(current, t); } } [SuppressMessage("ReSharper", "AccessToDisposedClosure")] public static IEnumerable<IEnumerable<T>> GroupUntil<T>(this IEnumerable<T> sequence, Func<T, T, Boolean> 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<T> GetGroup() { do { yield return e.Current; MoveNext(); } while (moreInGroup); } } public static IEnumerable<T> NotNull<T>(this IEnumerable<T?> ts) { return ts.Where(t => t != null)!; } public static IEnumerable<Double> 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<Double> Integrate(this IEnumerable<Double> source, Double initConst = 0) { return source.Scan(initConst, (x, y) => x + y); } public static IEnumerable<Double> IntegrateNormalize(this IEnumerable<Double> source, Double initConst = 0) { return source.Scan(initConst, (x, y) => Math.Tanh(x + y)); } public static IEnumerable<Double> MovingAverage(this IEnumerable<Double> 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<T> ZeroOrOne<T>(this T? t) { if (t is not null) yield return t; } public static IEnumerable<IEnumerable<T>> Transpose<T>(this IEnumerable<IEnumerable<T>> src) { return src .SelectMany(line => line.Select((element, column) => (element, column))) .GroupBy(i => i.column, i => i.element); } public static T GetNext<T>(this IReadOnlyList<T> ts, T current) { return ts .Concat(ts) .SkipWhile(t => !t!.Equals(current)) .Skip(1) .First(); } public static T GetPrevious<T>(this IReadOnlyList<T> ts, T current) { return GetNext(ts.Reverse().ToArray(ts.Count), current); } public static IEnumerable<R> Memoize<T, R>(this IEnumerable<T> src, Func<T, R> map) { var cache = new List<R>(); return src .Select((e, i) => i < cache.Count ? cache[i] : map(e).Apply(cache.Add)); } public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source, IEqualityComparer<T>? comparer = null) { return new HashSet<T>(source, comparer); } public static IEnumerable<Int32> Times(this Int32 n) { return Enumerable.Range(0, n); } }