From 7c6c2f20e63cdbb599e2acff1f6751b72b1f8a6c Mon Sep 17 00:00:00 2001 From: Brian Fiete Date: Mon, 28 Jun 2021 11:43:24 -0700 Subject: [PATCH] Added Linq to automated tests --- IDEHelper/Tests/BeefLinq/BeefSpace.toml | 2 + IDEHelper/Tests/BeefLinq/src/BeefProj.toml | 7 + .../Tests/BeefLinq/src/src/Enumerable.bf | 2868 +++++++++++++++++ .../Tests/BeefLinq/src/test/BeefProj.toml | 8 + .../BeefLinq/src/test/src/EnumerableTests.bf | 777 +++++ IDEHelper/Tests/src/Generics.bf | 12 + bin/test_build.bat | 2 + 7 files changed, 3676 insertions(+) create mode 100644 IDEHelper/Tests/BeefLinq/BeefSpace.toml create mode 100644 IDEHelper/Tests/BeefLinq/src/BeefProj.toml create mode 100644 IDEHelper/Tests/BeefLinq/src/src/Enumerable.bf create mode 100644 IDEHelper/Tests/BeefLinq/src/test/BeefProj.toml create mode 100644 IDEHelper/Tests/BeefLinq/src/test/src/EnumerableTests.bf diff --git a/IDEHelper/Tests/BeefLinq/BeefSpace.toml b/IDEHelper/Tests/BeefLinq/BeefSpace.toml new file mode 100644 index 00000000..c8a1ef89 --- /dev/null +++ b/IDEHelper/Tests/BeefLinq/BeefSpace.toml @@ -0,0 +1,2 @@ +FileVersion = 1 +Projects = {"Beef.Linq" = {Path = "src"}, "Beef.Linq.Tests" = {Path = "src/test"}} diff --git a/IDEHelper/Tests/BeefLinq/src/BeefProj.toml b/IDEHelper/Tests/BeefLinq/src/BeefProj.toml new file mode 100644 index 00000000..858ac746 --- /dev/null +++ b/IDEHelper/Tests/BeefLinq/src/BeefProj.toml @@ -0,0 +1,7 @@ +FileVersion = 1 + +[Project] +Name = "Beef.Linq" +TargetType = "BeefLib" +StartupObject = "Beef.Linq.Program" +DefaultNamespace = "System.Linq" diff --git a/IDEHelper/Tests/BeefLinq/src/src/Enumerable.bf b/IDEHelper/Tests/BeefLinq/src/src/Enumerable.bf new file mode 100644 index 00000000..f855bcc7 --- /dev/null +++ b/IDEHelper/Tests/BeefLinq/src/src/Enumerable.bf @@ -0,0 +1,2868 @@ +using System.Collections; +using System; +using internal System.Linq; + +namespace System.Linq +{ + public static + { + public static class Enumerable + { + public struct EmptyEnumerable : IEnumerable + { + public Enumerator GetEnumerator() => .(); + + public struct Enumerator : IEnumerator + { + public Result GetNext() + { + return .Err; + } + } + } + + public static EmptyEnumerable Empty() => .(); + + public struct RangeEnumerable : IEnumerable where TSource : operator TSource + int + { + TSource mStart; + TSource mEnd; + + public this(TSource start, TSource end) + { + mStart = start; + mEnd = end; + } + + public Enumerator GetEnumerator() => .(mStart, mEnd); + + public struct Enumerator : IEnumerator + { + TSource mCurrent; + TSource mEnd; + + public this(TSource start, TSource end) + { + mCurrent = start; + mEnd = end; + } + + public Result GetNext() mut + { + if (mCurrent == mEnd) + return .Err; + + let next = mCurrent; + mCurrent = mCurrent + 1; + return .Ok(next); + } + } + } + + public static RangeEnumerable + Range(TSource count) + where TSource : operator TSource + int + { + return .(default, count); + } + + public static RangeEnumerable + Range(TSource start, TSource end) + where TSource : operator TSource + int + where TSource : operator TSource + TSource + { + return .(start, end); + } + + public struct RepeatEnumerable : IEnumerable + { + TSource mValue; + int mCount; + + public this(TSource value, int count) + { + mValue = value; + mCount = count; + } + + public Enumerator GetEnumerator() => .(mValue, mCount); + + public struct Enumerator : IEnumerator + { + TSource mValue; + int mCount; + + public this(TSource value, int count) + { + mValue = value; + mCount = count; + } + + public Result GetNext() mut + { + if (--mCount >= 0) + return .Ok(mValue); + + return .Err; + } + } + } + + public static RepeatEnumerable + Repeat(TSource value, int count) + { + return .(value, count); + } + } + + + #region Matching + public static bool All(this TEnum items, TPredicate predicate) + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + { + return InternalAll(items, predicate); + } + + public static bool All(this TCollection items, TPredicate predicate) + where TCollection : concrete, IEnumerable + where TPredicate : delegate bool(TSource) + { + return InternalAll(items.GetEnumerator(), predicate); + } + + static bool InternalAll(TEnum items, TPredicate predicate) + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + { + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + switch (enumerator.GetNext()) + { + case .Ok(let val): if (!predicate(val)) return false; + case .Err: return false; + } + + while (enumerator.GetNext() case .Ok(let val)) + if (!predicate(val)) + return false; + + return true; + } + } + + public static bool Any(this TCollection items) + where TCollection : concrete, IEnumerable + { + for (var it in items) + return true; + + return false; + } + + public static bool Any(this TCollection items, TPredicate predicate) + where TCollection : concrete, IEnumerable + where TPredicate : delegate bool(TSource) + { + for (var it in items) + if (predicate(it)) + return true; + + return false; + } + + public static bool Any(this TEnum items) + where TEnum : concrete, IEnumerator + { + for (var it in items) + return true; + + return false; + } + + public static bool Any(this TEnum items, TPredicate predicate) + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + { + for (var it in items) + if (predicate(it)) + return true; + + return false; + } + + public static bool Contains(this TCollection items, TSource source) + where TCollection : concrete, IEnumerable + where bool : operator TSource == TSource + { + for (var it in items) + if (it == source) + return true; + + return false; + } + + public static bool Contains(this TEnum items, TSource source) + where TEnum : concrete, IEnumerator + where bool : operator TSource == TSource + { + for (var it in items) + if (it == source) + return true; + + return false; + } + + + public static bool SequenceEquals(this TLeft left, TRight right) + where TLeft : concrete, IEnumerable + where TRight : concrete, IEnumerable + where bool : operator TSource == TSource + { + return InternalSequenceEquals< + decltype(default(TLeft).GetEnumerator()), + decltype(default(TRight).GetEnumerator()), + TSource>(left.GetEnumerator(), right.GetEnumerator()); + } + + public static bool SequenceEquals(this TLeft left, TRight right) + where TLeft : concrete, IEnumerable + where TRight : concrete, IEnumerator + where bool : operator TSource == TSource + { + return InternalSequenceEquals< + decltype(default(TLeft).GetEnumerator()), + TRight, + TSource>(left.GetEnumerator(), right); + } + + public static bool SequenceEquals(this TLeft left, TRight right) + where TLeft : concrete, IEnumerator + where TRight : concrete, IEnumerable + where bool : operator TSource == TSource + { + return InternalSequenceEquals< + TLeft, + decltype(default(TRight).GetEnumerator()), + TSource>(left, right.GetEnumerator()); + } + + public static bool SequenceEquals(this TLeft left, TRight right) + where TLeft : concrete, IEnumerator + where TRight : concrete, IEnumerator + where bool : operator TSource == TSource + { + return InternalSequenceEquals(left, right); + } + + static bool InternalSequenceEquals(TLeft left, TRight right) + where TLeft : concrete, IEnumerator + where TRight : concrete, IEnumerator + where bool : operator TSource == TSource + { + using (var iterator0 = Iterator.Wrap(left)) + { + var e0 = iterator0.mEnum; + using (var iterator1 = Iterator.Wrap(right)) + { + var e1 = iterator1.mEnum; + while (true) + { + switch (e0.GetNext()) + { + case .Ok(let i0): + switch (e1.GetNext()) + { + case .Ok(let i1): + if (i0 != i1) + return false; + case .Err: + return false; + } + case .Err: + return e1.GetNext() case .Err; + } + } + } + } + } + + #endregion + + #region Aggregates + + public static TSource Average(this TCollection items) + where TCollection : concrete, IEnumerable + where TSource : operator TSource / int + where TSource : operator TSource + TSource + { + return InternalAverage(items.GetEnumerator()); + } + + public static TSource Average(this TEnum items) + where TEnum : concrete, IEnumerator + where TSource : operator TSource / int + where TSource : operator TSource + TSource + { + return InternalAverage(items); + } + + static TSource InternalAverage(TEnum items) + where TEnum : concrete, IEnumerator + where TSource : operator TSource / int + where TSource : operator TSource + TSource + { + var count = 0; + TSource sum = ?; + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + + switch (enumerator.GetNext()) + { + case .Ok(let val): + sum = val; + count++; + case .Err: return default; + } + + while (enumerator.GetNext() case .Ok(let val)) + { + sum += val; + count++; + } + + return sum / count; + } + } + + public static TSource Max(this TCollection items) + where TCollection : concrete, IEnumerable + where bool : operator TSource < TSource + { + return InternalMax(items.GetEnumerator()); + } + + public static TSource Max(this TEnum items) + where TEnum : concrete, IEnumerator + where bool : operator TSource < TSource + { + return InternalMax(items); + } + + static TSource InternalMax(TEnum items) + where TEnum : concrete, IEnumerator + where bool : operator TSource < TSource + { + TSource max = ?; + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + switch (enumerator.GetNext()) + { + case .Ok(let val): max = val; + case .Err: return default; + } + + while (enumerator.GetNext() case .Ok(let val)) + { + let next = val; + if (max < next) + max = next; + } + } + return max; + } + + public static TSource Min(this TCollection items) + where TCollection : concrete, IEnumerable + where bool : operator TSource < TSource + { + return InternalMin(items.GetEnumerator()); + } + + public static TSource Min(this TEnum items) + where TEnum : concrete, IEnumerator + where bool : operator TSource < TSource + { + return InternalMin(items); + } + + static TSource InternalMin(TEnum items) + where TEnum : concrete, IEnumerator + where bool : operator TSource < TSource + { + TSource min = ?; + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + switch (enumerator.GetNext()) + { + case .Ok(let val): min = val; + case .Err: return default; + } + + while (enumerator.GetNext() case .Ok(let val)) + { + let next = val; + if (next < min) + min = next; + } + } + return min; + } + + public static TSource Sum(this TCollection items) + where TCollection : concrete, IEnumerable + where TPredicate : delegate bool(TSource) + where TSource : operator TSource + TSource + { + return InternalSum(items.GetEnumerator()); + } + + public static TSource Sum(this TEnum items) + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + where TSource : operator TSource + TSource + { + return InternalSum(items); + } + + static TSource InternalSum(TEnum items) + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + where TSource : operator TSource + TSource + { + TSource sum = ?; + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + switch (enumerator.GetNext()) + { + case .Ok(let val): sum = val; + case .Err: return default; + } + + while (enumerator.GetNext() case .Ok(let val)) + sum += val; + } + return sum; + } + + public static int Count(this TCollection items) + where TCollection : concrete, IEnumerable + { + if (typeof(TCollection) == typeof(List)) + return (items as List).Count; + if (typeof(TCollection) == typeof(TSource[])) + return (items as TSource[]).Count; + + return InternalCount(items.GetEnumerator()); + } + + + public static int Count(this TEnum items) + where TEnum : concrete, IEnumerator + { + return InternalCount(items); + } + + public static int InternalCount(TEnum items) + where TEnum : concrete, IEnumerator + { + var count = 0; + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + while (enumerator.GetNext() case .Ok) + count++; + } + return count; + } + + #endregion + + #region Find in enumerable + + internal static bool InternalElementAt(TEnum items, int index, out TSource val) + where TEnum : concrete, IEnumerator + { + var index; + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + while (--index > 0) + { + if (enumerator.GetNext() case .Err) + break; + } + + if (index == 0 && enumerator.GetNext() case .Ok(out val)) + return true; + } + val = default; + return false; + } + + public static TSource ElementAt(this TCollection items, int index) + where TCollection : concrete, IEnumerable + { + if (InternalElementAt(items.GetEnumerator(), index, let val)) + return val; + + Runtime.FatalError("Not enough elements in the sequence."); + } + + public static TSource ElementAt(this TEnum items, int index) + where TEnum : concrete, IEnumerator + { + if (InternalElementAt(items, index, let val)) + return val; + + Runtime.FatalError("Not enough elements in the sequence."); + } + + public static TSource ElementAtOrDefault(this TCollection items, int index) + where TCollection : concrete, IEnumerable + { + if (InternalElementAt(items.GetEnumerator(), index, let val)) + return val; + + return default; + } + + public static TSource ElementAtOrDefault(this TEnum items, int index) + where TEnum : concrete, IEnumerator + { + if (InternalElementAt(items, index, let val)) + return val; + + return default; + } + + public static bool InternalFirst(TEnum items, out TSource val) + where TEnum : concrete, IEnumerator + { + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + if (enumerator.GetNext() case .Ok(out val)) + return true; + } + + return false; + } + + public static TSource First(this TCollection items) + where TCollection : concrete, IEnumerable + { + if (InternalFirst(items.GetEnumerator(), let val)) + return val; + Runtime.FatalError("Sequence contained no elements."); + } + + public static TSource First(this TEnum items) + where TEnum : concrete, IEnumerator + { + if (InternalFirst(items, let val)) + return val; + Runtime.FatalError("Sequence contained no elements."); + } + + public static TSource FirstOrDefault(this TCollection items) + where TCollection : concrete, IEnumerable + { + if (InternalFirst(items.GetEnumerator(), let val)) + return val; + + return default; + } + + public static TSource FirstOrDefault(this TEnum items) + where TEnum : concrete, IEnumerator + { + if (InternalFirst(items, let val)) + return val; + + return default; + } + + internal static bool InternalLast(TEnum items, out TSource val) + where TEnum : concrete, IEnumerator + { + var found = false; + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + if (enumerator.GetNext() case .Ok(out val)) + found = true; + + while (enumerator.GetNext() case .Ok(let temp)) + val = temp; + } + + return found; + } + + public static TSource Last(this TCollection items) + where TCollection : concrete, IEnumerable + { + if (InternalLast(items.GetEnumerator(), let val)) + return val; + + Runtime.FatalError("Sequence contained no elements."); + } + + public static TSource Last(this TEnum items) + where TEnum : concrete, IEnumerator + { + if (InternalLast(items, let val)) + return val; + + Runtime.FatalError("Sequence contained no elements."); + } + + public static TSource LastOrDefault(this TCollection items) + where TCollection : concrete, IEnumerable + { + if (InternalLast(items.GetEnumerator(), let val)) + return val; + + return default; + } + + public static TSource LastOrDefault(this TEnum items) + where TEnum : concrete, IEnumerator + { + if (InternalLast(items, let val)) + return val; + + return default; + } + + internal static bool InternalSingle(TEnum items, out TSource val) + where TEnum : concrete, IEnumerator + { + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + + if (enumerator.GetNext() case .Ok(out val)) + { + if (enumerator.GetNext() case .Err) + return true; + + Runtime.FatalError("Sequence matched more than one element."); + } + } + + return false; + } + + public static TSource Single(this TCollection items) + where TCollection : concrete, IEnumerable + { + if (InternalSingle(items.GetEnumerator(), let val)) + return val; + + Runtime.FatalError("Sequence contained no elements."); + } + + public static TSource Single(this TEnum items) + where TEnum : concrete, IEnumerator + { + if (InternalSingle(items, let val)) + return val; + + Runtime.FatalError("Sequence contained no elements."); + } + + public static TSource SingleOrDefault(this TCollection items) + where TCollection : concrete, IEnumerable + { + if (InternalSingle(items.GetEnumerator(), let val)) + return val; + + return default; + } + + public static TSource SingleOrDefault(this TEnum items) + where TEnum : concrete, IEnumerator + { + if (InternalSingle(items, let val)) + return val; + + return default; + } + + #endregion + +#region Enumerable Chains + struct Iterator + { + public static Iterator Wrap(TEnum items) + where TEnum : concrete, IEnumerator + { + return .(items); + } + + public static Iterator Wrap(TCollection items) + where TCollection : concrete, IEnumerable + { + return .(items.GetEnumerator()); + } + } + + struct Iterator : IDisposable + where TEnum : concrete, IEnumerator + { + internal TEnum mEnum; + + public this(TEnum items) + { + mEnum = items; + } + + [SkipCall] + public void Dispose() mut { } + + public static implicit operator Iterator(TEnum enumerator) => .(enumerator); + } + + extension Iterator : IDisposable where TEnum : IDisposable + { + public void Dispose() mut => mEnum.Dispose(); + } + + struct SelectEnumerable : Iterator, IEnumerable + where TSelect : delegate TResult(TSource) + where TEnum : concrete, IEnumerator + { + TSelect mDlg; + + public this(TEnum e, TSelect dlg) : base(e) + { + mDlg = dlg; + } + + Result GetNext() mut + { + if (mEnum.GetNext() case .Ok(let val)) + return mDlg(val); + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator + { + SelfOuter mEnum; + public this(SelfOuter enumerator) + { + mEnum = enumerator; + } + public Result GetNext() mut => mEnum.GetNext(); + } + } + + public static SelectEnumerable + Select(this TCollection items, TSelect select) + where TCollection : concrete, IEnumerable + where TSelect : delegate TResult(TSource) + { + return .(items.GetEnumerator(), select); + } + + public static SelectEnumerable + Select(this TEnum items, TSelect select) + where TEnum : concrete, IEnumerator + where TSelect : delegate TResult(TSource) + { + return .(items, select); + } + + struct WhereEnumerable : IEnumerable, IDisposable + where TPredicate : delegate bool(TSource) + where TEnum : concrete, IEnumerator + { + TPredicate mPredicate; + Iterator mIterator; + + public this(TEnum enumerator, TPredicate predicate) + { + mIterator = enumerator; + mPredicate = predicate; + } + + Result GetNext() mut + { + while (mIterator.mEnum.GetNext() case .Ok(let val)) + if (mPredicate(val)) + return .Ok(val); + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mSelf; + public this(SelfOuter self) + { + mSelf = self; + } + public Result GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut + { + mSelf.Dispose(); + } + } + + public void Dispose() mut + { + mIterator.Dispose(); + } + } + + public static WhereEnumerable + Where(this TCollection items, TPredicate predicate) + where TCollection : concrete, IEnumerable + where TPredicate : delegate bool(TSource) + { + return .(items.GetEnumerator(), predicate); + } + + public static WhereEnumerable + Where(this TEnum items, TPredicate predicate) + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + { + return .(items, predicate); + } + + struct TakeEnumerable : Iterator, IEnumerable + where TEnum : concrete, IEnumerator + { + int mCount; + + public this(TEnum enumerator, int count) : base(enumerator) + { + mCount = count; + } + + public Result GetNext() mut + { + while (mCount-- > 0 && mEnum.GetNext() case .Ok(let val)) + return val; + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator + { + SelfOuter mEnum; + public this(SelfOuter enumerator) + { + mEnum = enumerator; + } + public Result GetNext() mut => mEnum.GetNext(); + } + } + + public static TakeEnumerable + Take(this TCollection items, int count) + where TCollection : concrete, IEnumerable + { + return .(items.GetEnumerator(), count); + } + + public static TakeEnumerable + Take(this TEnum items, int count) + where TEnum : concrete, IEnumerator + { + return .(items, count); + } + + struct TakeWhileEnumerable : Iterator, IEnumerable + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + { + TPredicate mPredicate; + + public this(TEnum enumerator, TPredicate predicate) : base(enumerator) + { + mPredicate = predicate; + } + + Result GetNext() mut + { + if (mEnum.GetNext() case .Ok(let val)) + if (mPredicate(val)) + return .Ok(val); + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator + { + SelfOuter mEnum; + public this(SelfOuter enumerator) + { + mEnum = enumerator; + } + public Result GetNext() mut => mEnum.GetNext(); + } + } + + public static TakeWhileEnumerable + TakeWhile(this TCollection items, TPredicate predicate) + where TCollection : concrete, IEnumerable + where TPredicate : delegate bool(TSource) + { + return .(items.GetEnumerator(), predicate); + } + + public static TakeWhileEnumerable + TakeWhile(this TEnum items, TPredicate predicate) + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + { + return .(items, predicate); + } + + struct SkipEnumerable : Iterator, IEnumerable + where TEnum : concrete, IEnumerator + { + int mCount; + + public this(TEnum enumerator, int count) : base(enumerator) + { + mCount = count; + } + + public Result GetNext() mut + { + while (mCount-- > 0 && mEnum.GetNext() case .Ok(?)) { } + + while (mEnum.GetNext() case .Ok(let val)) + return val; + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator + { + SelfOuter mEnum; + public this(SelfOuter enumerator) + { + mEnum = enumerator; + } + public Result GetNext() mut => mEnum.GetNext(); + } + } + + public static SkipEnumerable + Skip(this TCollection items, int count) + where TCollection : concrete, IEnumerable + { + return .(items.GetEnumerator(), count); + } + + public static SkipEnumerable + Skip(this TEnum items, int count) + where TEnum : concrete, IEnumerator + { + return .(items, count); + } + + struct SkipWhileEnumerable : Iterator, IEnumerable + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + { + TPredicate mPredicate; + int mState = 0; + + public this(TEnum enumerator, TPredicate predicate) : base(enumerator) + { + mPredicate = predicate; + } + + public Result GetNext() mut + { + switch (mState) { + case 0: + while (mEnum.GetNext() case .Ok(let val)) + { + if (!mPredicate(val)) + { + mState = 1; + return .Ok(val); + } + } + case 1: + return mEnum.GetNext(); + } + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator + { + SelfOuter mEnum; + public this(SelfOuter enumerator) + { + mEnum = enumerator; + } + public Result GetNext() mut => mEnum.GetNext(); + } + } + + public static SkipWhileEnumerable + SkipWhile(this TCollection items, TPredicate predicate) + where TCollection : concrete, IEnumerable + where TPredicate : delegate bool(TSource) + { + return .(items.GetEnumerator(), predicate); + } + + public static SkipWhileEnumerable + SkipWhile(this TEnum items, TPredicate predicate) + where TEnum : concrete, IEnumerator + where TPredicate : delegate bool(TSource) + { + return .(items, predicate); + } + + struct DefaultIfEmptyEnumerable : Iterator, IEnumerable + where TEnum : concrete, IEnumerator + { + TSource mDefaultValue; + int mState = 0; + + public this(TEnum enumerator, TSource defaultValue) : base(enumerator) + { + mDefaultValue = defaultValue; + } + + public Result GetNext() mut + { + switch (mState) { + case 0: + if (mEnum.GetNext() case .Ok(let val)) + { + mState = 1; + return .Ok(val); + } + + mState = 2; + return .Ok(mDefaultValue); + case 1: + return mEnum.GetNext(); + } + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator + { + SelfOuter mEnum; + public this(SelfOuter enumerator) + { + mEnum = enumerator; + } + public Result GetNext() mut => mEnum.GetNext(); + } + } + + public static DefaultIfEmptyEnumerable + DefaultIfEmpty(this TCollection items) + where TCollection : concrete, IEnumerable + { + return .(items.GetEnumerator(), default); + } + + public static DefaultIfEmptyEnumerable + DefaultIfEmpty(this TEnum items) + where TEnum : concrete, IEnumerator + { + return .(items, default); + } + + public static DefaultIfEmptyEnumerable + DefaultIfEmpty(this TCollection items, TSource defaultValue = default) + where TCollection : concrete, IEnumerable + { + return .(items.GetEnumerator(), defaultValue); + } + + public static DefaultIfEmptyEnumerable + DefaultIfEmpty(this TEnum items, TSource defaultValue = default) + where TEnum : concrete, IEnumerator + { + return .(items, defaultValue); + } + + struct DistinctEnumerable : IEnumerable, IDisposable + where TEnum : concrete, IEnumerator + where TSource : IHashable + { + HashSet mDistinctValues; + HashSet.Enumerator mEnum; + Iterator mIterator; + int mState = 0; + + public this(TEnum enumerator) + { + mIterator = .(enumerator); + mDistinctValues = new .(); + mEnum = default; + } + + public Result GetNext() mut + { + switch (mState) { + case 0: + var enumerator = mIterator.mEnum; + while (enumerator.GetNext() case .Ok(let val)) + mDistinctValues.Add(val); + + mIterator.Dispose(); + mIterator = default; + mEnum = mDistinctValues.GetEnumerator(); + mState = 1; + fallthrough; + case 1: + return mEnum.GetNext(); + } + + return .Err; + } + + public void Dispose() mut + { + mEnum.Dispose(); + DeleteAndNullify!(mDistinctValues); + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mEnum; + public this(SelfOuter enumerator) + { + mEnum = enumerator; + } + public Result GetNext() mut => mEnum.GetNext(); + + public void Dispose() mut => mEnum.Dispose(); + } + } + + public static DistinctEnumerable + Distinct(this TCollection items) + where TCollection : concrete, IEnumerable + where TSource : IHashable + { + return .(items.GetEnumerator()); + } + + public static DistinctEnumerable + Distinct(this TEnum items) + where TEnum : concrete, IEnumerator + where TSource : IHashable + { + return .(items); + } + + struct ReverseEnumerable : IEnumerable, IDisposable + where TEnum : concrete, IEnumerator + { + List mCopyValues; + List.Enumerator mEnum; + Iterator mIterator; + int mIndex = -1; + + public this(TEnum enumerator) + { + mIterator = .(enumerator); + mCopyValues = new .(); + mEnum = default; + } + + public Result GetNext() mut + { + switch (mIndex) { + case -1: + var enumerator = mIterator.mEnum; + while (enumerator.GetNext() case .Ok(let val)) + mCopyValues.Add(val); + + mIterator.Dispose(); + mIterator = default; + mEnum = mCopyValues.GetEnumerator(); + mIndex = mCopyValues.Count; + fallthrough; + default: + if (--mIndex >= 0) + return .Ok(mCopyValues[mIndex]); + + return .Err; + } + } + + public void Dispose() mut + { + mEnum.Dispose(); + DeleteAndNullify!(mCopyValues); + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mSelf; + public this(SelfOuter self) + { + mSelf = self; + } + public Result GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut + { + mSelf.Dispose(); + } + } + } + + public static ReverseEnumerable + Reverse(this TCollection items) + where TCollection : concrete, IEnumerable + { + return .(items.GetEnumerator()); + } + + struct MapEnumerable : Iterator, IEnumerator, IEnumerable + where bool : operator TSource < TSource + where TSource : operator TSource - TSource + where TResult : operator TResult + TResult + where TResult : operator TResult - TResult + where float : operator float / TSource + where float : operator TSource * float + where float : operator float / TResult + where TResult : operator explicit float + where TEnum : concrete, IEnumerator + { + int mState = 0; + float mScale = 0f, mMapScale; + TSource mMin = default; + TResult mMapMin; + + public this(TEnum enumerator, TResult mapMin, TResult mapMax) : base(enumerator) + { + mMapMin = mapMin; + mMapScale = 1f / (mapMax - mapMin); + } + + public Result GetNext() mut + { + switch (mState) { + case 0: + var copyEnum = mEnum; + switch (copyEnum.GetNext()) { + case .Ok(let first): + var min = first; + var max = first; + + while (copyEnum.GetNext() case .Ok(let next)) + { + if (next < min) min = next; + if (max < next) max = next; + } + + mMin = min; + mScale = 1f / (max - min); + if (mScale == default) + { + mState = 2; + return .Ok(default); + } + + mState = 1; + case .Err: return .Err; + } + fallthrough; + case 1: + if (mEnum.GetNext() case .Ok(let val)) + return (TResult)(((val - mMin) * mScale) / mMapScale) + mMapMin; + case 2: + if (mEnum.GetNext() case .Ok(let val)) + return .Ok(default); + } + + return .Err; + } + + /*typealias SelfOuter = MapEnumerable; + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator: IEnumerator + { + SelfOuter mEnum; + public this(SelfOuter enumerator) + { + mEnum = enumerator; + } + public Result GetNext() mut => mEnum.GetNext(); + }*/ + + public Self GetEnumerator() => this; + } + + public static MapEnumerable + Map(this TCollection items, TResult min, TResult max) + where TCollection : concrete, IEnumerable + where bool : operator TSource < TSource + where TSource : operator TSource - TSource + where TResult : operator TResult + TResult + where TResult : operator TResult - TResult + where float : operator float / TSource + where float : operator TSource * float + where float : operator float / TResult + where TResult : operator explicit float + { + return .(items.GetEnumerator(), min, max); + } + + public static MapEnumerable + Map(this TEnum items, TResult min, TResult max) + where TEnum : concrete, IEnumerator + where bool : operator TSource < TSource + where TSource : operator TSource - TSource + where TResult : operator TResult + TResult + where TResult : operator TResult - TResult + where float : operator float / TSource + where float : operator TSource * float + where float : operator float / TResult + where TResult : operator explicit float + { + return .(items, min, max); + } +#endregion + +#region ToXYZ methods + public static void ToDictionary(this TCollection items, TKeyDlg keyDlg, TValueDlg valueDlg, Dictionary output) + where TCollection : concrete, IEnumerable + where TKey : IHashable + where TKeyDlg : delegate TKey(TSource) + where TValueDlg : delegate TValue(TSource) + { + for (var it in items) + output.Add(keyDlg(it), valueDlg(it)); + } + + public static void ToDictionary(this TEnum items, TKeyDlg keyDlg, TValueDlg valueDlg, Dictionary output) + where TEnum : concrete, IEnumerator + where TKey : IHashable + where TKeyDlg : delegate TKey(TSource) + where TValueDlg : delegate TValue(TSource) + { + for (var it in items) + output.Add(keyDlg(it), valueDlg(it)); + } + + public static void ToDictionary(this TCollection items, TKeyDlg keyDlg, Dictionary output) + where TCollection : concrete, IEnumerable + where TKey : IHashable + where TKeyDlg : delegate TKey(TSource) + { + for (var it in items) + output.Add(keyDlg(it), it); + } + + public static void ToDictionary(this TEnum items, TKeyDlg keyDlg, Dictionary output) + where TEnum : concrete, IEnumerator + where TKey : IHashable + where TKeyDlg : delegate TKey(TSource) + { + for (var it in items) + output.Add(keyDlg(it), it); + } + + public static void ToHashSet(this TCollection items, HashSet output) + where TCollection : concrete, IEnumerable + where TSource : IHashable + { + for (var it in items) + output.Add(it); + } + + public static void ToHashSet(this TEnum items, HashSet output) + where TEnum : concrete, IEnumerator + where TSource : IHashable + { + for (var it in items) + output.Add(it); + } + + public static void ToList(this TCollection items, List output) + where TCollection : concrete, IEnumerable + { + for (var it in items) + output.Add(it); + } + + public static void ToList(this TEnum items, List output) + where TEnum : concrete, IEnumerator + { + for (var it in items) + output.Add(it); + } +#endregion + +#region Aggregates + public static TSource + Aggregate(this TCollection items, TAccumulate accumulate) + where TCollection : concrete, IEnumerable + where TAccumulate : delegate TSource(TSource, TSource) + { + if (InternalAggregate(items.GetEnumerator(), accumulate, let result)) + return result; + + Runtime.FatalError("No elements in the sequence."); + } + + public static TSource + Aggregate(this TEnum items, TAccumulate accumulate) + where TEnum : concrete, IEnumerator + where TAccumulate : delegate TSource(TSource, TSource) + { + if (InternalAggregate(items, accumulate, let result)) + return result; + + Runtime.FatalError("No elements in the sequence."); + } + + public static TAccumulate + Aggregate(this TCollection items, TAccumulate seed, TAccDlg accumulate) + where TCollection : concrete, IEnumerable + where TAccDlg : delegate TAccumulate(TAccumulate, TSource) + { + if (InternalAggregate(items.GetEnumerator(), seed, accumulate, let result)) + return result; + + return seed; + } + + public static TAccumulate + Aggregate(this TEnum items, TAccumulate seed, TAccDlg accumulate) + where TEnum : concrete, IEnumerator + where TAccDlg : delegate TAccumulate(TAccumulate, TSource) + { + if (InternalAggregate(items, seed, accumulate, let result)) + return result; + + return seed; + } + + public static TResult + Aggregate(this TCollection items, TAccDlg accumulate, TResDlg resultSelector) + where TCollection : concrete, IEnumerable + where TAccDlg : delegate TSource(TSource, TSource) + where TResDlg : delegate TResult(TSource) + { + if (InternalAggregate(items.GetEnumerator(), accumulate, let result)) + return resultSelector(result); + + return resultSelector(default); + } + + public static TResult + Aggregate(this TEnum items, TAccDlg accumulate, TResDlg resultSelector) + where TEnum : concrete, IEnumerator + where TAccDlg : delegate TSource(TSource, TSource) + where TResDlg : delegate TResult(TSource) + { + if (InternalAggregate(items, accumulate, let result)) + return resultSelector(result); + + return resultSelector(default); + } + + public static TResult + Aggregate(this TCollection items, TAccumulate seed, TAccDlg accumulate, TResDlg resultSelector) + where TCollection : concrete, IEnumerable + where TAccDlg : delegate TAccumulate(TAccumulate, TSource) + where TResDlg : delegate TResult(TAccumulate) + { + if (InternalAggregate(items.GetEnumerator(), seed, accumulate, let result)) + return resultSelector(result); + + return resultSelector(seed); + } + + public static TResult + Aggregate(this TEnum items, TAccumulate seed, TAccDlg accumulate, TResDlg resultSelector) + where TEnum : concrete, IEnumerator + where TAccDlg : delegate TAccumulate(TAccumulate, TSource) + where TResDlg : delegate TResult(TAccumulate) + { + if (InternalAggregate(items, seed, accumulate, let result)) + return resultSelector(result); + + return resultSelector(seed); + } + + static bool InternalAggregate(TEnum items, TAccDlg accumulate, out TSource aggregate) + where TEnum : concrete, IEnumerator + where TAccDlg : delegate TSource(TSource, TSource) + { + aggregate = default; + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + + //I guess we need at least 2 elements to do an accumulation without a seed? + if (!(enumerator.GetNext() case .Ok(out aggregate))) + return false; + + while (enumerator.GetNext() case .Ok(let val)) + aggregate = accumulate(aggregate, val); + } + return true; + } + + + static bool InternalAggregate(TEnum items, TAccumulate seed, TAccDlg func, out TAccumulate result) + where TEnum : concrete, IEnumerator + where TAccDlg : delegate TAccumulate(TAccumulate, TSource) + { + TAccumulate sum = seed; + var accumulated = false; + using (var iterator = Iterator.Wrap(items)) + { + var enumerator = iterator.mEnum; + + if (enumerator.GetNext() case .Ok(let val)) + { + sum = func(sum, val); + accumulated = true; + } + + if (accumulated) + while (enumerator.GetNext() case .Ok(let val)) + sum = func(sum, val); + } + + result = sum; + return accumulated; + } + #endregion + +#region GroupBy + + struct DynamicArray : IDisposable + { + TValue[] mPtr = default; + Span mSpan = default; + int mLength = 0; + int mSize = 4; + int mIndex = 0; + + public int Length => mLength; + + public this() + { + this.mPtr = new TValue[mSize]; + this.mLength = 0; + } + + public ref TValue this[int index] => ref mPtr[index]; + + public void Dispose() mut + { + DeleteAndNullify!(mPtr); + mPtr = null; + } + public void Add(TValue value) mut + { + if (mLength + 1 > mSize) + { + var newSize = mSize * 3 / 2; + var dst = new TValue[newSize]; + Array.Copy(mPtr, dst, mLength); + Swap!(mPtr, dst); + delete dst; + mSize = newSize; + } + mPtr[mLength++] = value; + } + + public Span.Enumerator GetEnumerator() mut + { + mSpan = .(mPtr, 0, mLength); + return mSpan.GetEnumerator(); + } + + public static implicit operator Span(Self it) => .(it.mPtr, 0, it.mLength); + } + + public extension DynamicArray : IDisposable + where TValue : IDisposable + { + public void Dispose() mut + { + for (var it in mPtr) + it.Dispose(); + + base.Dispose(); + } + } + + public struct Grouping : IEnumerable, IDisposable, IResettable + { + List mValues; + int mIndex = 0; + public readonly TKey Key; + + public this(TKey key) + { + Key = key; + mValues = new .(); + } + + public void Reset() mut + { + mIndex = 0; + } + + public List.Enumerator GetEnumerator() + { + return mValues.GetEnumerator(); + } + + public void Add(TValue value) mut + { + mValues.Add(value); + } + + public void Dispose() mut + { + DeleteAndNullify!(mValues); + } + } + + public class GroupByResult : IEnumerable> + where bool : operator TKey == TKey//where TKey : IHashable + { + DynamicArray> mResults = .() ~ mResults.Dispose(); + + public int Count => mResults.Length; + + public void Add(Grouping group) + { + mResults.Add(group); + } + + public ref Grouping this[int index] => ref mResults[index]; + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IRefEnumerator*>, IEnumerator>, IResettable + { + SelfOuter mSelf; + Span> mSpan; + int mIndex = 0; + + public this(SelfOuter self) + { + mSelf = self; + mSpan = self.mResults; + } + + public Result> GetNext() mut + { + if (mIndex < mSpan.Length) + return .Ok(mSpan[mIndex++]); + + return .Err; + } + + public Result*> GetNextRef() mut + { + if (mIndex < mSpan.Length) + return .Ok(&mSpan[mIndex++]); + + return .Err; + } + + public void Reset() mut + { + mIndex = 0; + } + } + } + + public struct GroupByEnumerable : IEnumerable>, IDisposable + where TEnum : concrete, IEnumerator + where bool : operator TKey == TKey//where TKey : IHashable + where TKeyDlg : delegate TKey(TSource) + where TValueDlg : delegate TValue(TSource) + { + GroupByResult mResults; + TKeyDlg mKeyDlg; + TValueDlg mValueDlg; + Iterator mIterator; + int mIndex = -1; + bool mDeleteResult; + + public this(GroupByResult results, TEnum enumerator, TKeyDlg keyDlg, TValueDlg valueDlg, bool deleteResult) + { + mResults = results; + mIterator = .(enumerator); + mKeyDlg = keyDlg; + mValueDlg = valueDlg; + mDeleteResult = deleteResult; + } + + Result> GetNext() mut + { + if (mIndex == -1) + { + while (mIterator.mEnum.GetNext() case .Ok(let val)) + { + let k = mKeyDlg(val); + let v = mValueDlg(val); + var added = false; + for (var it in ref mResults) + { + if (it.Key == k) + { + it.Add(v); + added = true; + } + } + + if (!added) + { + var group = mResults.Add(.. .(k)); + group.Add(v); + } + } + mIndex = 0; + } + + if (mIndex < mResults.Count) + return mResults[mIndex++]; + + return .Err; + } + + public void Dispose() mut + { + mIterator.Dispose(); + if (mDeleteResult) + DeleteAndNullify!(mResults); + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator>, IDisposable + { + SelfOuter mSelf; + + public this(SelfOuter self) + { + mSelf = self; + } + + public Result> GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut => mSelf.Dispose(); + } + } + + extension GroupByEnumerable + where TValueDlg : Object + { + public void Dispose() mut + { + base.Dispose(); + DeleteAndNullify!(mValueDlg); + } + } + + public static GroupByEnumerable + GroupBy(this TCollection items, TKeyDlg key) + where TCollection : concrete, IEnumerable + where TKeyDlg : delegate TKey(TSource) + where TKey : IHashable + { + //guess we could optimize out this scope with some code duplication + return .(new .(), items.GetEnumerator(), key, new (val) => val, true); + } + + public static GroupByEnumerable + GroupBy(this TEnum items, TKeyDlg key) + where TEnum : concrete, IEnumerator + where TKeyDlg : delegate TKey(TSource) + where TKey : IHashable + { + //guess we could optimize out this scope with some code duplication + return .(new .(), items, key, new (val) => val, true); + } + + public static GroupByEnumerable + GroupBy(this TCollection items, TKeyDlg key, GroupByResult results) + where TCollection : concrete, IEnumerable + where TKeyDlg : delegate TKey(TSource) + where TKey : IHashable + { + //guess we could optimize out this scope with some code duplication + return .(results, items.GetEnumerator(), key, new (val) => val, false); + } + + public static GroupByEnumerable + GroupBy(this TEnum items, TKeyDlg key, GroupByResult results) + where TEnum : concrete, IEnumerator + where TKeyDlg : delegate TKey(TSource) + where TKey : IHashable + { + //guess we could optimize out this scope with some code duplication + return .(results, items, key, new (val) => val, false); + } + + /*public static GroupByEnumerable GroupBy(this TCollection items, TKeyDlg + key, GroupByResult results) where TCollection : concrete, IEnumerable where TKeyDlg : + delegate TKey(TSource) where TKey : IHashable + { + //guess we could optimize out this scope with some code duplication + return .(results, items.GetEnumerator(), key, scope (val) => val); + }*/ +#endregion + struct UnionEnumerable : IEnumerable, IDisposable + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + where TSource : IHashable + { + HashSet mDistinctValues; + Iterator mSource; + Iterator mOther; + int mState = 0; + + public this(TEnum sourceEnumerator, TEnum2 otherEnumerator) + { + mSource = sourceEnumerator; + mOther = otherEnumerator; + + mDistinctValues = new .(); + } + + Result GetNext() mut + { + switch (mState) { + case 0: + var e = mSource.mEnum; + while (e.GetNext() case .Ok(let val)) + if (mDistinctValues.Add(val)) + return .Ok(val); + + mState++; + fallthrough; + case 1: + var e = mOther.mEnum; + while (e.GetNext() case .Ok(let val)) + if (mDistinctValues.Add(val)) + return .Ok(val); + + mState++; + } + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mSelf; + + public this(SelfOuter self) + { + mSelf = self; + } + + public Result GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut => mSelf.Dispose(); + } + + public void Dispose() mut + { + mSource.Dispose(); + mOther.Dispose(); + DeleteAndNullify!(mDistinctValues); + } + } + + public static UnionEnumerable + Union(this TCollection items, TCollection2 other) + where TCollection : concrete, IEnumerable + where TCollection2 : concrete, IEnumerable + where TSource : IHashable + { + return .(items.GetEnumerator(), other.GetEnumerator()); + } + + public static UnionEnumerable + Union(this TEnum items, TCollection2 other) + where TEnum : concrete, IEnumerator + where TCollection2 : concrete, IEnumerable + where TSource : IHashable + { + return .(items, other.GetEnumerator()); + } + + public static UnionEnumerable + Union(this TCollection items, TEnum other) + where TCollection : concrete, IEnumerable + where TEnum : concrete, IEnumerator + where TSource : IHashable + { + return .(items.GetEnumerator(), other); + } + + public static UnionEnumerable + Union(this TEnum items, TEnum2 other) + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + where TSource : IHashable + { + return .(items, other); + } + + struct ExceptEnumerable : IEnumerable, IDisposable + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + where TSource : IHashable + { + HashSet mDistinctValues; + Iterator mSource; + Iterator mOther; + int mState = 0; + + public this(TEnum sourceEnumerator, TEnum2 otherEnumerator) + { + mSource = sourceEnumerator; + mOther = otherEnumerator; + + mDistinctValues = new .(); + } + + Result GetNext() mut + { + switch (mState) { + case 0: + var e = mOther.mEnum; + while (e.GetNext() case .Ok(let val)) + mDistinctValues.Add(val); + + mState++; + fallthrough; + case 1: + var e = mSource.mEnum; + while (e.GetNext() case .Ok(let val)) + if (mDistinctValues.Add(val)) + return .Ok(val); + + mState++; + } + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mSelf; + + public this(SelfOuter self) + { + mSelf = self; + } + + public Result GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut => mSelf.Dispose(); + } + + public void Dispose() mut + { + mSource.Dispose(); + mOther.Dispose(); + DeleteAndNullify!(mDistinctValues); + } + } + + public static ExceptEnumerable + Except(this TCollection items, TCollection2 other) + where TCollection : concrete, IEnumerable + where TCollection2 : concrete, IEnumerable + where TSource : IHashable + { + return .(items.GetEnumerator(), other.GetEnumerator()); + } + + public static ExceptEnumerable + Except(this TEnum items, TCollection2 other) + where TEnum : concrete, IEnumerator + where TCollection2 : concrete, IEnumerable + where TSource : IHashable + { + return .(items, other.GetEnumerator()); + } + + public static ExceptEnumerable + Except(this TCollection items, TEnum other) + where TCollection : concrete, IEnumerable + where TEnum : concrete, IEnumerator + where TSource : IHashable + { + return .(items.GetEnumerator(), other); + } + + public static ExceptEnumerable + Except(this TEnum items, TEnum2 other) + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + where TSource : IHashable + { + return .(items, other); + } + + struct IntersectEnumerable : IEnumerable, IDisposable + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + where TSource : IHashable + { + HashSet mDistinctValues; + Iterator mSource; + Iterator mIntersect; + int mState = 0; + + public this(TEnum sourceEnumerator, TEnum2 intersectEnumerator) + { + mSource = .(sourceEnumerator); + mIntersect = .(intersectEnumerator); + mDistinctValues = new .(); + } + + Result GetNext() mut + { + switch (mState) + { + case 0: + var e = mSource.mEnum; + while (e.GetNext() case .Ok(let val)) + mDistinctValues.Add(val); + + mState++; + fallthrough; + case 1: + var e = mIntersect.mEnum; + while (e.GetNext() case .Ok(let val)) + if (mDistinctValues.Remove(val)) + return .Ok(val); + + mState++; + } + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mSelf; + + public this(SelfOuter self) + { + mSelf = self; + } + + public Result GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut => mSelf.Dispose(); + } + + public void Dispose() mut + { + mSource.Dispose(); + mIntersect.Dispose(); + DeleteAndNullify!(mDistinctValues); + } + } + + public static IntersectEnumerable + Intersect(this TCollection items, TCollection2 other) + where TCollection : concrete, IEnumerable + where TCollection2 : concrete, IEnumerable + where TSource : IHashable + { + return .(items.GetEnumerator(), other.GetEnumerator()); + } + + public static IntersectEnumerable + Intersect(this TEnum items, TCollection2 other) + where TEnum : concrete, IEnumerator + where TCollection2 : concrete, IEnumerable + where TSource : IHashable + { + return .(items, other.GetEnumerator()); + } + + public static IntersectEnumerable + Intersect(this TCollection items, TEnum other) + where TCollection : concrete, IEnumerable + where TEnum : concrete, IEnumerator + where TSource : IHashable + { + return .(items.GetEnumerator(), other); + } + + public static IntersectEnumerable + Intersect(this TEnum items, TEnum2 other) + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + where TSource : IHashable + { + return .(items, other); + } + + struct ZipEnumerable : IEnumerable, IDisposable + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + where TSelect : delegate TResult(TSource first, TSource second) + { + Iterator mSource; + Iterator mOther; + TSelect mSelect; + + public this(TEnum sourceEnumerator, TEnum2 otherEnumerator, TSelect select) + { + mSource = sourceEnumerator; + mOther = otherEnumerator; + mSelect = select; + } + + Result GetNext() mut + { + if (mSource.mEnum.GetNext() case .Ok(let first)) + if (mOther.mEnum.GetNext() case .Ok(let second)) + return mSelect(first, second); + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mSelf; + + public this(SelfOuter self) + { + mSelf = self; + } + + public Result GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut => mSelf.Dispose(); + } + + + public void Dispose() mut + { + mSource.Dispose(); + mOther.Dispose(); + } + } + + public static ZipEnumerable + Zip(this TCollection items, TCollection2 other, TSelect select) + where TCollection : concrete, IEnumerable + where TCollection2 : concrete, IEnumerable + where TSelect : delegate TResult(TSource first, TSource second) + { + return .(items.GetEnumerator(), other.GetEnumerator(), select); + } + + public static ZipEnumerable + Zip(this TEnum items, TCollection2 other, TSelect select) + where TEnum : concrete, IEnumerator + where TCollection2 : concrete, IEnumerable + where TSelect : delegate TResult(TSource first, TSource second) + { + return .(items, other.GetEnumerator(), select); + } + + public static ZipEnumerable + Zip(this TCollection items, TEnum other, TSelect select) + where TCollection : concrete, IEnumerable + where TEnum : concrete, IEnumerator + where TSelect : delegate TResult(TSource first, TSource second) + { + return .(items.GetEnumerator(), other, select); + } + + public static ZipEnumerable + Zip(this TEnum items, TEnum2 other, TSelect select) + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + where TSelect : delegate TResult(TSource first, TSource second) + { + return .(items, other, select); + } + + struct ConcatEnumerable : IEnumerable, IDisposable + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + { + Iterator mFirst; + Iterator mSecond; + int mState = 0; + + public this(TEnum firstEnumerator, TEnum2 secondEnumerator) + { + mFirst = firstEnumerator; + mSecond = secondEnumerator; + } + + Result GetNext() mut + { + switch (mState) { + case 0: + if (mFirst.mEnum.GetNext() case .Ok(let val)) + return .Ok(val); + + mState++; + fallthrough; + case 1: + if (mSecond.mEnum.GetNext() case .Ok(let val)) + return .Ok(val); + + mState++; + } + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mSelf; + + public this(SelfOuter self) + { + mSelf = self; + } + + public Result GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut => mSelf.Dispose(); + } + + public void Dispose() mut + { + mFirst.Dispose(); + mSecond.Dispose(); + } + } + + + + public static ConcatEnumerable + Concat(this TCollection items, TCollection2 other) + where TCollection : concrete, IEnumerable + where TCollection2 : concrete, IEnumerable + { + return .(items.GetEnumerator(), other.GetEnumerator()); + } + + public static ConcatEnumerable + Concat(this TEnum items, TCollection2 other) + where TEnum : concrete, IEnumerator + where TCollection2 : concrete, IEnumerable + { + return .(items, other.GetEnumerator()); + } + + public static ConcatEnumerable + Concat(this TCollection items, TEnum other) + where TCollection : concrete, IEnumerable + where TEnum : concrete, IEnumerator + { + return .(items.GetEnumerator(), other); + } + + public static ConcatEnumerable + Concat(this TEnum items, TEnum2 other) + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + { + return .(items, other); + } + + public static ConcatEnumerable + Append(this TCollection items, TCollection2 other) + where TCollection : concrete, IEnumerable + where TCollection2 : concrete, IEnumerable + { + return .(items.GetEnumerator(), other.GetEnumerator()); + } + + public static ConcatEnumerable + Append(this TEnum items, TCollection2 other) + where TEnum : concrete, IEnumerator + where TCollection2 : concrete, IEnumerable + { + return .(items, other.GetEnumerator()); + } + + public static ConcatEnumerable + Append(this TCollection items, TEnum other) + where TCollection : concrete, IEnumerable + where TEnum : concrete, IEnumerator + { + return .(items.GetEnumerator(), other); + } + + public static ConcatEnumerable + Append(this TEnum items, TEnum2 other) + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + { + return .(items, other); + } + + public static ConcatEnumerable + Prepend(this TCollection items, TCollection2 other) + where TCollection : concrete, IEnumerable + where TCollection2 : concrete, IEnumerable + { + return .(other.GetEnumerator(), items.GetEnumerator()); + } + + public static ConcatEnumerable + Prepend(this TEnum items, TCollection2 other) + where TEnum : concrete, IEnumerator + where TCollection2 : concrete, IEnumerable + { + return .(other.GetEnumerator(), items); + } + + public static ConcatEnumerable + Prepend(this TCollection items, TEnum other) + where TCollection : concrete, IEnumerable + where TEnum : concrete, IEnumerator + { + return .(other, items.GetEnumerator()); + } + + public static ConcatEnumerable + Prepend(this TEnum items, TEnum2 other) + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + { + return .(other, items); + } + + + + static class OrderByComparison + where int : operator T <=> T + { + typealias TCompare = delegate int(T lhs, T rhs); + public readonly static TCompare Comparison = (new (lhs, rhs) => lhs <=> rhs) ~ delete _; + } + + struct SortedEnumerable : IEnumerator<(TKey key, TSource value)>, IDisposable + where TEnum : concrete, IEnumerator + where TKeyDlg : delegate TKey(TSource) + where TCompare : delegate int(TKey lhs, TKey rhs) + { + List<(TKey key, TSource value)> mOrderedList; + Iterator mIterator; + TKeyDlg mKey; + TCompare mCompare; + int mIndex; + int mCount = 0; + bool mDescending; + + public this(TEnum firstEnumerator, TKeyDlg key, TCompare compare, bool descending) + { + mOrderedList = new .(); + mKey = key; + mIterator = firstEnumerator; + mCompare = compare; + mIndex = -1; + mDescending = descending; + } + + public Result<(TKey key, TSource value)> GetNext() mut + { + if (mIndex == -1) + { + while (mIterator.mEnum.GetNext() case .Ok(let val)) + mOrderedList.Add((mKey(val), val)); + + mOrderedList.Sort(scope (l, r) => mCompare(l.key, r.key)); + mCount = mOrderedList.Count;//keeping vars local + mIndex = mDescending ? mCount : 0; + } + + if (mDescending) + { + if (mIndex > 0) + return .Ok(mOrderedList[--mIndex]); + } + else if (mIndex < mCount) + return .Ok(mOrderedList[mIndex++]); + + return .Err; + } + + + public Enumerator GetEnumerator() => .(this); + public struct Enumerator : IEnumerator<(TKey key, TSource value)>, IDisposable + { + SelfOuter mSelf; + + public this(SelfOuter self) + { + mSelf = self; + } + + public Result<(TKey key, TSource value)> GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut => mSelf.Dispose(); + } + + + public void Dispose() mut + { + mIterator.Dispose(); + DeleteAndNullify!(mOrderedList); + } + } + + //if the data is a large struct, this could get pretty slow with all the copies + //we may need to investigate the ability to use ptrs instead if the type is a struct? + struct OrderByEnumerable : IEnumerable, IDisposable + where TEnum : concrete, IEnumerator + { + typealias TCompare = delegate int(TSource lhs, TSource rhs); + + struct Comparer + { + public TCompare comparer; + public bool descending; + } + + DynamicArray mValues; + int[] mMap; + //List mOrderedList; + List mCompares = default; + Iterator mIterator; + int mIndex; + + public this(TEnum enumerator, TCompare compare, bool descending) + { + mCompares = new .(); + var add = mCompares.GrowUnitialized(1); + add.comparer = compare; + add.descending = descending; + + mValues = ?; + mMap = null; + mIterator = enumerator; + mIndex = -1; + } + + void QuickSort(int left, int right) { + var left; + var right; +#unwarn + repeat { + int i = left; + int j = right; + int x = mMap[i + ((j - i) >> 1)]; + repeat { + while (i < mMap.Count && CompareKeys(x, mMap[i]) > 0) i++; + while (j >= 0 && CompareKeys(x, mMap[j]) < 0) j--; + if (i > j) break; + if (i < j) { + int temp = mMap[i]; + mMap[i] = mMap[j]; + mMap[j] = temp; + } + i++; + j--; + } while (i <= j); + if (j - left <= right - i) { + if (left < j) QuickSort(left, j); + left = i; + } + else { + if (i < right) QuickSort(i, right); + right = j; + } + } while (left < right); + } + + [Inline] + int CompareKeys(int index1, int index2) + { + for(var cmp in ref mCompares) + { + int c = cmp.comparer(mValues[index1], mValues[index2]); + if (c != 0) + return cmp.descending ? -c : c; + } + + return index1 - index2; + } + + Result GetNext() mut + { + if (mIndex == -1) + { + mIndex = 0; + mValues = .(); + while (mIterator.mEnum.GetNext() case .Ok(let val)) + mValues.Add(val); + + let count = mValues.Length; + mMap = new int[count]; + for (int i = 0; i < count; i++) mMap[i] = i; + QuickSort( 0, count - 1); + } + + if (mIndex < mValues.Length) + return .Ok(mValues[mMap[mIndex++]]); + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mSelf; + + public this(SelfOuter self) + { + mSelf = self; + } + + public Result GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut => mSelf.Dispose(); + } + + public void Dispose() mut + { + mIterator.Dispose(); + + for(var it in mCompares) + delete it.comparer; + DeleteAndNullify!(mCompares); + DeleteAndNullify!(mMap); + mValues.Dispose(); + } + + public Self ThenBy(TKeyDlg2 keySelect, TCompare2 comparison) mut + where TKeyDlg2 : delegate TKey2(TSource) + where TCompare2 : delegate int(TKey2 lhs, TKey2 rhs) + { + var add = mCompares.GrowUnitialized(1); + add.comparer = new (l, r) => comparison(keySelect(l), keySelect(r)); + add.descending = false; + return this; + } + + public Self ThenBy(TKeyDlg2 keySelect) + where TKeyDlg2 : delegate TKey2(TSource) + where int : operator TKey2 <=> TKey2 + { + let comparison = OrderByComparison.Comparison; + var add = mCompares.GrowUnitialized(1); + add.comparer = new (l, r) => comparison(keySelect(l), keySelect(r)); + add.descending = false; + return this; + } + + + public Self ThenByDescending(TKeyDlg2 keySelect, TCompare2 comparison) mut + where TKeyDlg2 : delegate TKey2(TSource) + where TCompare2 : delegate int(TKey2 lhs, TKey2 rhs) + { + var add = mCompares.GrowUnitialized(1); + add.comparer = new (l, r) => comparison(keySelect(l), keySelect(r)); + add.descending = true; + return this; + } + + public Self ThenByDescending(TKeyDlg2 keySelect) + where TKeyDlg2 : delegate TKey2(TSource) + where int : operator TKey2 <=> TKey2 + { + let comparison = OrderByComparison.Comparison; + var add = mCompares.GrowUnitialized(1); + add.comparer = new (l, r) => comparison(keySelect(l), keySelect(r)); + add.descending = true; + return this; + } + } + + public static OrderByEnumerable + OrderBy(this TCollection items, TKeyDlg keySelect) + where TCollection : concrete, IEnumerable + where TKeyDlg : delegate TKey(TSource) + where int : operator TKey <=> TKey + { + let comparison = OrderByComparison.Comparison; + return .(items.GetEnumerator(), new (l, r) => comparison(keySelect(l), keySelect(r)), false); + } + + public static OrderByEnumerable + OrderBy(this TEnum items, TKeyDlg keySelect) + where TEnum : concrete, IEnumerator + where TKeyDlg : delegate TKey(TSource) + where int : operator TKey <=> TKey + { + let comparison = OrderByComparison.Comparison; + return .(items, new (l, r) => comparison(keySelect(l), keySelect(r)), false); + } + + public static OrderByEnumerable + OrderBy(this TCollection items, TKeyDlg keySelect, TCompare comparison) + where TCollection : concrete, IEnumerable + where TKeyDlg : delegate TKey(TSource) + where TCompare : delegate int(TKey lhs, TKey rhs) + { + return .(items.GetEnumerator(), new (l, r) => comparison(keySelect(l), keySelect(r)), false); + } + + public static OrderByEnumerable + OrderBy(this TEnum items, TKeyDlg keySelect, TCompare comparison) + where TEnum : concrete, IEnumerator + where TKeyDlg : delegate TKey(TSource) + where TCompare : delegate int(TKey lhs, TKey rhs) + { + return .(items, new (l, r) => comparison(keySelect(l), keySelect(r)), false); + } + + public static OrderByEnumerable + OrderByDescending(this TCollection items, TKeyDlg keySelect) + where TCollection : concrete, IEnumerable + where TKeyDlg : delegate TKey(TSource) + where int : operator TKey <=> TKey + { + let comparison = OrderByComparison.Comparison; + return .(items.GetEnumerator(), new (l, r) => comparison(keySelect(l), keySelect(r)), true); + } + + public static OrderByEnumerable + OrderByDescending(this TEnum items, TKeyDlg keySelect) + where TEnum : concrete, IEnumerator + where TKeyDlg : delegate TKey(TSource) + where int : operator TKey <=> TKey + { + let comparison = OrderByComparison.Comparison; + return .(items, new (l, r) => comparison(keySelect(l), keySelect(r)), true); + } + + public static OrderByEnumerable + OrderByDescending(this TCollection items, TKeyDlg keySelect, TCompare comparison) + where TCollection : concrete, IEnumerable + where TKeyDlg : delegate TKey(TSource) + where TCompare : delegate int(TKey lhs, TKey rhs) + { + return .(items.GetEnumerator(),new (l, r) => comparison(keySelect(l), keySelect(r)), true); + } + + public static OrderByEnumerable + OrderByDescending(this TEnum items, TKeyDlg keySelect, TCompare comparison) + where TEnum : concrete, IEnumerator + where TKeyDlg : delegate TKey(TSource) + where TCompare : delegate int(TKey lhs, TKey rhs) + { + return .(items, new (l, r) => comparison(keySelect(l), keySelect(r)), true); + } + + struct SelectManyEnumerable : IEnumerable, IDisposable + where TEnum : concrete, IEnumerator + where TEnum2 : concrete, IEnumerator + where TSelect : delegate TEnum2(TSource) + { + public readonly static String SelectManyEnum = new String() ~ delete _; + + Iterator mItems; + Iterator mCurrent = default; + TSelect mSelect; + int mState = -1; + + public this(TEnum firstEnumerator, TSelect select) + { + mItems = firstEnumerator; + mSelect = select; + } + + Result GetNext(out bool moveNext) mut + { + if (mState < 1) + { + if (mState == 0) + mCurrent.Dispose(); + + if (mItems.mEnum.GetNext() case .Ok(var val)) + { + mCurrent = mSelect(val); + mState = 1; + } + else + { + //no more elements + moveNext = false; + return .Err; + } + } + + if (mCurrent.mEnum.GetNext() case .Ok(let val)) + { + moveNext = false; + return .Ok(val); + } + + //done with current enumerator + mState = 0; + moveNext = true; + return .Err; + } + + Result GetNext() mut + { + var moveNext = true; + while (moveNext) + { + let result = GetNext(out moveNext); + if (!moveNext) + return result; + } + + return .Err; + } + + public Enumerator GetEnumerator() => .(this); + + public struct Enumerator : IEnumerator, IDisposable + { + SelfOuter mSelf; + + public this(SelfOuter self) + { + mSelf = self; + } + + public Result GetNext() mut => mSelf.GetNext(); + + public void Dispose() mut => mSelf.Dispose(); + } + + public void Dispose() mut + { + mItems.Dispose(); + } + } + extension SelectManyEnumerable + where TSelect : Object + { + public void Dispose() mut + { + base.Dispose(); + DeleteAndNullify!(mSelect); + } + } + + public static SelectManyEnumerable + SelectMany(this TCollection items, TSelect select) + where TCollection : concrete, IEnumerable + where TCollection2 : concrete, IEnumerable + where TSelect : delegate TCollection2(TSource) + { + return .(items.GetEnumerator(), new (x) => select(x).GetEnumerator()); + } + + public static SelectManyEnumerable + SelectMany(this TCollection items, TSelect select) + where TCollection : concrete, IEnumerable + where TEnum2 : concrete, IEnumerator + where TSelect : delegate TEnum2(TSource) + { + return .(items.GetEnumerator(), select); + } + + /*struct OfTypeEnumerable : Iterator, IEnumerator, IEnumerable + where TEnum : concrete, IEnumerator + where TSource : class + { + public this(TEnum enumerator) : base(enumerator) + { + } + + public Result GetNext() mut + { + while (mEnum.GetNext() case .Ok(let val)) + { + if (val is TOf) + return .Ok(*(TOf*)Internal.UnsafeCastToPtr(val)); + } + return .Err; + } + + public Self GetEnumerator() + { + return this; + } + } + + public static OfTypeEnumerable + OfType(this TCollection items) + where TCollection : concrete, IEnumerable + where TSource : class + { + return .(items.GetEnumerator()); + }*/ + + + } +} diff --git a/IDEHelper/Tests/BeefLinq/src/test/BeefProj.toml b/IDEHelper/Tests/BeefLinq/src/test/BeefProj.toml new file mode 100644 index 00000000..70d49547 --- /dev/null +++ b/IDEHelper/Tests/BeefLinq/src/test/BeefProj.toml @@ -0,0 +1,8 @@ +FileVersion = 1 +Dependencies = {corlib = "*", "Beef.Linq" = "*"} + +[Project] +Name = "Beef.Linq.Tests" +TargetType = "BeefTest" +StartupObject = "Befe.Linq.Tests.Program" +DefaultNamespace = "System.Linq" diff --git a/IDEHelper/Tests/BeefLinq/src/test/src/EnumerableTests.bf b/IDEHelper/Tests/BeefLinq/src/test/src/EnumerableTests.bf new file mode 100644 index 00000000..1407b52d --- /dev/null +++ b/IDEHelper/Tests/BeefLinq/src/test/src/EnumerableTests.bf @@ -0,0 +1,777 @@ +//#define INCLUDE_FAILURES + +using System.Collections; +using System; +namespace System.Linq +{ + public class EnumerableTests + { + [Test] + public static void Any() + { + let data = scope List(); + + var actual = data.Any(); + Test.Assert(!actual); + + actual = data.Any((it) => it == 2); + Test.Assert(!actual); + + data.Add(1); + data.Add(2); + data.Add(3); + data.Add(4); + + actual = data.Any((it) => it == 2); + Test.Assert(actual); + + actual = data.Any(); + Test.Assert(actual); + + data.RemoveAt(1); + actual = data.Any((it) => it == 2); + Test.Assert(!actual); + } + + [Test] + public static void All() + { + let data = scope List(); + + var actual = data.All((it) => it == 2); + Test.Assert(!actual); + + data.Add(2); + data.Add(2); + data.Add(2); + data.Add(2); + + actual = data.All((it) => it == 2); + Test.Assert(actual); + + data.Add(3); + actual = data.All((it) => it == 2); + Test.Assert(!actual); + } + + + struct ContainsTest : IEnumerable, IEnumerator + { + int mState = 0; + public Self GetEnumerator() + { + return this; + } + + public Result GetNext() mut + { + if (mState > 3) + return .Err; + + return .Ok(mState++); + } + } + + [Test] + public static void Contains() + { + let data = ContainsTest(); + + var actual = data.Contains(2); + Test.Assert(actual); + + actual = data.Contains(4); + Test.Assert(!actual); + } + + + [Test] + public static void Average() + { + let data = scope List() { 1, 1, 2, 2, 4 }; + { + let actual = data.Average(); + Test.Assert(actual == 2); + } + { + let actual = data.GetEnumerator().Average(); + Test.Assert(actual == 2); + } + } + + + [Test] + public static void Max() + { + { + let data = scope List(); + + var actual = data.Max(); + Test.Assert(actual == default); + + data.Add(3); + actual = data.Max(); + Test.Assert(actual == 3); + + data.Add(1); + actual = data.Max(); + Test.Assert(actual == 3); + } + { + let data = scope List(); + + var actual = data.GetEnumerator().Max(); + Test.Assert(actual == default); + + data.Add(3); + actual = data.GetEnumerator().Max(); + Test.Assert(actual == 3); + + data.Add(1); + actual = data.GetEnumerator().Max(); + Test.Assert(actual == 3); + } + } + + [Test] + public static void Min() + { + let data = scope List(); + + var actual = data.Min(); + Test.Assert(actual == default); + + data.Add(3); + actual = data.Min(); + Test.Assert(actual == 3); + + data.Add(1); + actual = data.Min(); + Test.Assert(actual == 1); + } + + [Test] + public static void Sum() + { + { + let data = scope List() { 1, 2, 3, 4 }; + let actual = data.Sum(); + Test.Assert(actual == 10); + } + { + let data = scope List() { 1, 2, 3, 4 }; + let actual = data.Sum(); + Test.Assert(actual == 10); + } + { + let data = scope List() { 1, null, 3, 4 }; + let actual = data.Sum(); + Test.Assert(actual == null); + } + { + let data = scope List(); + let actual = data.Sum(); + Test.Assert(actual == null); + } + } + + [Test] + public static void ElementAt() + { + let data = scope List() { 1, 2, 3 }; + let actual = data.ElementAt(1); + Test.Assert(actual == 1); + } + + [Test] + public static void First() + { + let data = scope List() { 1, 2, 3 }; + let actual = data.First(); + Test.Assert(actual == 1); + } + + [Test] + public static void FirstOrDefault() + { + let data = scope List(); + let actual = data.FirstOrDefault(); + Test.Assert(actual == default); + } + + [Test] + public static void Last() + { + let data = scope List() { 1, 2, 3 }; + let actual = data.Last(); + Test.Assert(actual == 3); + } + + [Test] + public static void LastOrDefault() + { + let data = scope List(); + let actual = data.LastOrDefault(); + Test.Assert(actual == default); + } + + [Test] + public static void Take() + { + let data = scope List(); + for (var i < 20) data.Add(i); + + let actual = data.Take(10).ToList(.. scope .()); + + let expected = scope List(); + for (var i < 10) expected.Add(i); + + Test.Assert(actual.Count == 10); + Test.Assert(actual.SequenceEquals(expected) == true); + } + + [Test] + public static void Skip() + { + let data = scope List(); + for (var i < 20) data.Add(i); + + let actual = data.Skip(10).ToList(.. scope .()); + + let expected = scope List(); + for (var i < 10) expected.Add(i + 10); + + Test.Assert(actual.Count == 10); + Test.Assert(actual.SequenceEquals(expected) == true); + } + + [Test] + public static void Empty() + { + let actual = Enumerable.Empty().ToList(.. scope .()); + Test.Assert(actual.Count == 0); + } + + [Test] + public static void Range() + { + { + let actual = Enumerable.Range(10).ToList(.. scope .()); + let expected = scope List(); + for (var i < 10) expected.Add(i); + Test.Assert(actual.SequenceEquals(expected) == true); + } + { + let actual = Enumerable.Range(10, 20).ToList(.. scope .()); + let expected = scope List(); + for (var i < 10) expected.Add(i + 10); + Test.Assert(actual.SequenceEquals(expected) == true); + } + } + + [Test] + public static void Map() + { + { + let data = scope List() { 0, 5, 10 }; + let actual = data.Map(0f, 1f).ToList(.. scope .()); + let expected = scope List() { 0f, 0.5f, 1f }; + + Test.Assert(actual.SequenceEquals(expected)); + } + { + let data = scope List() { 0, 5, 10 }; + let actual = data.Map(0, 100).ToList(.. scope .()); + let expected = scope List(); + expected.Add(0); + expected.Add(50); + expected.Add(100); + + Test.Assert(actual.SequenceEquals(expected)); + } + } + + + [Test] + public static void Select() + { + let data = scope List<(int x, int y, float z, float w)>() { (1, 2, 3, 4), (4, 3, 2, 1) }; + let actual = data.Select((it) => (x: it.x, y: it.y)).ToList(.. scope .()); + let expected = scope List<(int x, int y)>(); + expected.Add((1, 2)); + expected.Add((4, 3)); + + Test.Assert(actual.Count == 2); + Test.Assert(actual.SequenceEquals(expected)); + } + + [Test] + public static void Where() + { + let data = scope List<(int x, int y, float z, float w)>() { (1, 2, 3, 4), (4, 3, 2, 1) }; + let actual = data.Where((it) => it.x == 1).ToList(.. scope .()); + Test.Assert(actual.Count == 1); + Test.Assert(actual[0] == (1, 2, 3, 4)); + } + + [Test] + public static void TakeWhile() + { + let data = scope List() { 1, 1, 2, 4 }; + let actual = data.TakeWhile((it) => it == 1).ToList(.. scope .()); + let expected = scope List(); + expected.Add(1); + expected.Add(1); + Test.Assert(actual.Count == 2); + } + + + [Test] + public static void SkipWhile() + { + let data = scope List() { 1, 1, 2, 3 }; + let actual = data.SkipWhile((it) => it == 1).ToList(.. scope .()); + let expected = scope List(); + expected.Add(2); + expected.Add(3); + Test.Assert(actual.Count == expected.Count); + } + + [Test] + public static void Repeat() + { + let actual = Enumerable.Repeat(10, 10).ToList(.. scope .()); + let expected = scope List(); + for (var i < 10) + expected.Add(10); + + Test.Assert(actual.SequenceEquals(expected)); + } + + [Test] + public static void Distinct() + { + let data = scope List() { 1, 1, 2, 3 }; + let actual = data.Distinct().ToList(.. scope .()); + let expected = scope List(); + expected.Add(1); + expected.Add(2); + expected.Add(3); + + Test.Assert(actual.Count == expected.Count); + Test.Assert(actual.SequenceEquals(expected)); + } + + [Test] + public static void Reverse() + { + let data = scope List() { 1, 1, 2, 3 }; + let actual = data.Reverse().ToList(.. scope .()); + let expected = scope List(); + expected.Add(3); + expected.Add(2); + expected.Add(1); + expected.Add(1); + + Test.Assert(actual.Count == expected.Count); + Test.Assert(actual.SequenceEquals(expected)); + } + + [Test] + public static void DefaultIfEmpty() + { + { + let data = scope List(); + let actual = data.DefaultIfEmpty().ToList(.. scope .()); + let expected = scope List(); + expected.Add(null); + Test.Assert(actual.Count == 1); + Test.Assert(actual[0] == null); + } + { + let data = scope List(); + let actual = data.DefaultIfEmpty().ToList(.. scope .()); + let expected = scope List(); + expected.Add(10); + Test.Assert(actual.Count == 1); + Test.Assert(actual[0] == 0); + } + { + let data = scope List(); + let actual = data.DefaultIfEmpty(10).ToList(.. scope .()); + let expected = scope List(); + expected.Add(10); + Test.Assert(actual.Count == 1); + Test.Assert(actual[0] == 10); + } + } + + + [Test] + public static void Aggregate() + { + { + let data = scope List() { 1, 2, 3, 4, 5 }; + let actual = data.Aggregate((sum, next) => sum + next); + Test.Assert(actual == 15); + } + { + let data = scope List() { 1, 2, 3, 4, 5 }; + let actual = data.Aggregate(5, (sum, next) => sum + next); + Test.Assert(actual == 20); + } + + /*{ + let data = scope List() { 1, 2, 3, 4, 5 }; + let actual = data.Aggregate( (sum, next) => sum + next, (result) => result * 1000f); + Test.Assert(actual == 15000f); + }*/ + { + let data = scope List() { 1, 2, 3, 4, 5 }; + let actual = data.Aggregate(5, (sum, next) => sum + next, (result) => result * 1000f); + Test.Assert(actual == 20000f); + } + } + +#region ToXYZ methods + [Test] + public static void ToDictionary() + { + { + let data = scope List<(int x, float y)>() { (1, 2f), (4, 3f) }; + let actual = data.ToDictionary((it) => it.x, .. scope .()); + + Test.Assert(actual.Count == 2); + Test.Assert(actual.Contains((1, (1, 2f)))); + Test.Assert(actual.Contains((4, (4, 3f)))); + } + { + let data = scope List<(int x, float y)>() { (1, 2f), (4, 3f) }; + let actual = data.ToDictionary((it) => it.x, (it) => it.y, .. scope .()); + + Test.Assert(actual.Count == 2); + Test.Assert(actual.Contains((1, 2f))); + Test.Assert(actual.Contains((4, 3f))); + } + } + + [Test] + public static void ToHashSet() + { + let data = scope List() { 1, 2, 2 }; + let actual = data.ToHashSet(.. scope .()); + + Test.Assert(actual.Count == 2); + Test.Assert(actual.Contains(1)); + Test.Assert(actual.Contains(2)); + } +#endregion + +#region GroupBy + + /*public static mixin GroupBy(this TCollection items, TKey key) + where TCollection : concrete, IEnumerable + where TKeyDlg : delegate TKey(TSource) + { + let groupByMemory = scope:mixin GroupByMemory(); + return InternalGroupBy(groupByMemory, items, key); + }*/ + + [Test] + public static void GroupBy() + { + { + let data = scope List<(int x, int y, int z)>() { (0, 1, 9), (0, 2, 8), (2, 4, 5), (1, 1, 1), (2, 2, 2) }; + let actual = data.GroupBy((key) => key.x, scope .()).ToList(.. scope .()); + + Test.Assert(actual.Count == 3); + + var i = 0; + for (var it in actual) + { + switch (it.Key) + { + case 0: Test.Assert(it.SequenceEquals(scope List<(int x, int y, int z)>() { (0, 1, 9), (0, 2, 8) })); i |= 1; + case 1: Test.Assert(it.SequenceEquals(scope List<(int x, int y, int z)>() { (1, 1, 1) })); i |= 2; + case 2: Test.Assert(it.SequenceEquals(scope List<(int x, int y, int z)>() { (2, 4, 5), (2, 2, 2) })); i |= 4; + } + } + + Test.Assert(i == 7); + } + + { + let data = scope List<(int x, int y, int z)>() { (0, 1, 9), (0, 2, 8), (2, 4, 5), (1, 1, 1), (2, 2, 2) }; + + var i = 0; + for (var it in data.GroupBy((key) => key.x)) + { + switch (it.Key) + { + case 0: Test.Assert(it.SequenceEquals(scope List<(int x, int y, int z)>() { (0, 1, 9), (0, 2, 8) })); i |= 1; + case 1: Test.Assert(it.SequenceEquals(scope List<(int x, int y, int z)>() { (1, 1, 1) })); i |= 2; + case 2: Test.Assert(it.SequenceEquals(scope List<(int x, int y, int z)>() { (2, 4, 5), (2, 2, 2) })); i |= 4; + } + } + + Test.Assert(i == 7); + } + } + +#endregion + + [Test] + public static void Union() + { + let data = scope List() { 0, 1, 2 }; + let other = scope List() { 3, 4, 5 }; + let actual = data.Union(other).ToList(.. scope .()); + + Test.Assert(actual.SequenceEquals(int[6](0, 1, 2, 3, 4, 5))); + } + + [Test] + public static void Intersect() + { + let data = scope List() { 0, 1, 2 }; + let other = scope List() { 1, 5, 2 }; + let actual = data.Intersect(other).ToList(.. scope .()); + + Test.Assert(actual.SequenceEquals(int[2](1, 2))); + } + + [Test] + public static void Except() + { + let data = scope List() { 0, 1, 2 }; + let other = scope List() { 0, 5, 2 }; + let actual = data.Except(other).ToList(.. scope .()); + + Test.Assert(actual.SequenceEquals(int[1](1))); + } + + [Test] + public static void Zip() + { + let data = scope List() { 0, 1, 2, 3 }; + let other = scope List() { 1, 2, 3 }; + let actual = data.Zip(other, (first, second) => first + second).ToList(.. scope .()); + + Test.Assert(actual.SequenceEquals(int[3](1, 3, 5))); + } + + [Test] + public static void Concat() + { + let data = scope List() { 0, 1, 2, 3 }; + let other = scope List() { 1, 2, 3 }; + let actual = data.Concat(other).ToList(.. scope .()); + + Test.Assert(actual.SequenceEquals(int[?](0, 1, 2, 3, 1, 2, 3))); + } + + [Test] + public static void Append() + { + let data = scope List() { 0, 1, 2 }; + let other = scope List() { 1, 2, 3, 3 }; + let actual = data.Append(other).ToList(.. scope .()); + + Test.Assert(actual.SequenceEquals(int[?](0, 1, 2, 1, 2, 3, 3))); + } + + [Test] + public static void Prepend() + { + let data = scope List() { 0, 1, 2 }; + let other = scope List() { 1, 2, 3, 3 }; + let actual = data.Prepend(other).ToList(.. scope .()); + + Test.Assert(actual.SequenceEquals(int[?](1, 2, 3, 3, 0, 1, 2))); + } + + [Test] + public static void OrderBy() + { + { + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderBy((it) => it.x).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (0, 4), (1, 2), (1, 3), (2, 0), (3, 2) }; + Test.Assert(actual.SequenceEquals(expected)); + } + { + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderBy((it) => it.x, (l, r) => l - r).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (0, 4), (1, 2), (1, 3), (2, 0), (3, 2) }; + Test.Assert(actual.SequenceEquals(expected)); + } + { + //orderby has some temp allocations, this test is just to make sure those temp allocations don't fail + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderBy((it) => it.x, (l, r) => l - r).OrderBy((it) => it.x, (l, r) => r - l).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (3, 2), (2, 0), (1, 2), (1, 3), (0, 4) }; + Test.Assert(actual.SequenceEquals(expected)); + } + } + + [Test] + public static void OrderByDescending() + { + //this method shouldn't be using reverse, but I'm being lazy + { + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderByDescending((it) => it.x).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (0, 4), (1, 3), (1, 2), (2, 0), (3, 2) }; + Test.Assert(actual.SequenceEquals(expected.Reverse())); + } + { + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderByDescending((it) => it.x, (l, r) => l - r).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (0, 4), (1, 3), (1, 2), (2, 0), (3, 2) }; + Test.Assert(actual.SequenceEquals(expected.Reverse())); + } + { + //orderby has some temp allocations, this test is just to make sure those temp allocations don't fail + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderByDescending((it) => it.x, (l, r) => l - r).OrderBy((it) => it.x, (l, r) => r - l).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (3, 2), (2, 0), (1, 2), (1, 3), (0, 4) }; + Test.Assert(actual.SequenceEquals(expected)); + } + } + + [Test] + public static void ThenBy() + { + { + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderBy((it) => it.x).ThenBy((it) => it.y).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (0, 4), (1, 2), (1, 3), (2, 0), (3, 2) }; + Test.Assert(actual.SequenceEquals(expected)); + } + { + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderBy((it) => it.x, (l, r) => l - r).ThenBy((it) => it.y).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (0, 4), (1, 2), (1, 3), (2, 0), (3, 2) }; + Test.Assert(actual.SequenceEquals(expected)); + } + { + //orderby has some temp allocations, this test is just to make sure those temp allocations don't fail + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderBy((it) => it.x, (l, r) => l - r).OrderBy((it) => it.x, (l, r) => r - l).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (3, 2), (2, 0), (1, 2), (1, 3), (0, 4) }; + Test.Assert(actual.SequenceEquals(expected)); + } + } + + + [Test] + public static void ThenByDescending() + { + { + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderBy((it) => it.x).ThenByDescending((it) => it.y).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (0, 4), (1, 3), (1, 2), (2, 0), (3, 2) }; + Test.Assert(actual.SequenceEquals(expected)); + } + { + let data = scope List<(int x, int y)>() { (1, 2), (1, 3), (3, 2), (0, 4), (2, 0) }; + let actual = data.OrderBy((it) => it.x, (l, r) => l - r).ThenByDescending((it) => it.y).ToList(.. scope .()); + + let expected = scope List<(int x, int y)>() { (0, 4), (1, 3), (1, 2), (2, 0), (3, 2) }; + Test.Assert(actual.SequenceEquals(expected)); + } + } + + [Test] + public static void SelectMany() + { + { + let data = scope List>() + { + scope List(), + scope List(), + scope List(), + scope List(), + scope List() + }; + + for (var i < 5) + for (var k < 2) + data[i].Add(i * 2 + k); + + let actual = data.SelectMany((x) => x).ToList(.. scope .()); + let actual2 = data.SelectMany((x) => x.GetEnumerator()).ToList(.. scope .()); + + Test.Assert(actual.SequenceEquals(scope int[](0, 1, 2, 3, 4, 5, 6, 7, 8, 9))); + Test.Assert(actual2.SequenceEquals(scope int[](0, 1, 2, 3, 4, 5, 6, 7, 8, 9))); + } + } + +#region Failures +#if INCLUDE_FAILURES + [Test(ShouldFail = true)] + public static void ElementAtSequenceError() + { + let data = scope List(){ 1, 2, 3}; + data.ElementAt(4); + } + + [Test(ShouldFail = true)] + public static void FirstFatalOnEmpty() + { + let data = scope List(); + data.First(); + } + + [Test(ShouldFail = true)] + public static void LastFatalOnEmpty() + { + let data = scope List(); + + data.Last(); + } +#endif +#endregion + + [AttributeUsage(.Method)] + public struct MyTestAttribute : Attribute + { + } + + [Reflect(.Methods), AlwaysInclude(IncludeAllMethods = true)] + public struct ReflectionTest + { + [MyTest, AlwaysInclude, Reflect] + public int HelloWorld() { return 1; } + } + +#region Reported bugs + + /*[Test] + public static void HitGetMethodsReflectionIssue() + { + let actual = typeof(ReflectionTest).GetMethods().Where((m) => m.GetCustomAttribute() case .Ok).ToList(.. scope .()); + Test.Assert(actual.Count == 1); + }*/ + + [Test] + public static void HigCallingMutatingIssue() + { + int[] test1 = scope .(10, 11, 10, 12, 13, 14, -1); + let actual = test1.Reverse().Where((x) => x > 0 && x % 2 == 0).Sum(); + + Test.Assert(actual == 46); + } +#endregion + } +} diff --git a/IDEHelper/Tests/src/Generics.bf b/IDEHelper/Tests/src/Generics.bf index e49acc62..01d420b6 100644 --- a/IDEHelper/Tests/src/Generics.bf +++ b/IDEHelper/Tests/src/Generics.bf @@ -296,6 +296,18 @@ namespace Tests } + public static void TestGen(T val) + where T : IEnumerable + where TItem : var + { + } + + public static void TestPreGen() + { + List a = default; + TestGen(a); + } + [Test] public static void TestBasics() { diff --git a/bin/test_build.bat b/bin/test_build.bat index 921387e8..4e3b35f2 100644 --- a/bin/test_build.bat +++ b/bin/test_build.bat @@ -15,6 +15,8 @@ CALL bin/msbuild.bat IDEHelper\Tests\CLib\CLib.vcxproj /p:Configuration=Debug /p @IF %ERRORLEVEL% NEQ 0 GOTO HADERROR IDE\dist\BeefBuild_d -proddir=IDEHelper\Tests -test @IF %ERRORLEVEL% NEQ 0 GOTO HADERROR +IDE\dist\BeefBuild_d -proddir=IDEHelper\Tests\BeefLinq -test +@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR @ECHO Testing IDEHelper\Tests (Win32) CALL bin/msbuild.bat IDEHelper\Tests\CLib\CLib.vcxproj /p:Configuration=Debug /p:Platform=x86