diff --git a/samples/Benchmarks/Benchmarks.csproj b/samples/Benchmarks/Benchmarks.csproj index 8a1ff02..c0a1081 100644 --- a/samples/Benchmarks/Benchmarks.csproj +++ b/samples/Benchmarks/Benchmarks.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net9.0 enable enable diff --git a/samples/SampleUsing/SampleUsing.csproj b/samples/SampleUsing/SampleUsing.csproj index 48346f5..8b63013 100644 --- a/samples/SampleUsing/SampleUsing.csproj +++ b/samples/SampleUsing/SampleUsing.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net9.0 enable enable diff --git a/samples/UsingWithFewSubranges/UsingWithFewSubranges.csproj b/samples/UsingWithFewSubranges/UsingWithFewSubranges.csproj index 48346f5..8b63013 100644 --- a/samples/UsingWithFewSubranges/UsingWithFewSubranges.csproj +++ b/samples/UsingWithFewSubranges/UsingWithFewSubranges.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net9.0 enable enable diff --git a/samples/UsingWithSubrange/UsingWithSubrange.csproj b/samples/UsingWithSubrange/UsingWithSubrange.csproj index 48346f5..8b63013 100644 --- a/samples/UsingWithSubrange/UsingWithSubrange.csproj +++ b/samples/UsingWithSubrange/UsingWithSubrange.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net9.0 enable enable diff --git a/src/DateRecurrenceR/Collections/UnionEnumerator.cs b/src/DateRecurrenceR/Collections/UnionEnumerator.cs new file mode 100644 index 0000000..c704c65 --- /dev/null +++ b/src/DateRecurrenceR/Collections/UnionEnumerator.cs @@ -0,0 +1,115 @@ +using System.Collections; + +namespace DateRecurrenceR.Collections; + +internal struct UnionEnumerator : IEnumerator +{ + private readonly EWrapper[] _enumerators; + private DateOnly? _current = null; + + public UnionEnumerator(IReadOnlyList> enumerators) + { + var hash = new HashSet(); + + for (var i = 0; i < enumerators.Count; i++) + { + if (enumerators[i] is UnionEnumerator ue) + { + for (var j = 0; j < ue._enumerators.Length; j++) + { + hash.Add(ue._enumerators[j]); + } + } + else + { + hash.Add(new EWrapper(enumerators[i])); + } + } + + _enumerators = hash.ToArray(); + + Current = default; + } + + public bool MoveNext() + { + var nextIndex = -1; + + for (var i = 0; i < _enumerators.Length; i++) + { + if (_current.HasValue) + { + while (_enumerators[i].CanMoveNext && _enumerators[i].Enum.Current <= _current.Value) + { + _enumerators[i].MoveNext(); + } + } + else + { + _enumerators[i].MoveNext(); + } + } + + for (var i = 0; i < _enumerators.Length; i++) + { + if (!_enumerators[i].CanMoveNext) continue; + + _current = _enumerators[i].Enum.Current; + nextIndex = i; + break; + } + + for (var i = 0; i < _enumerators.Length; i++) + { + if (!_enumerators[i].CanMoveNext || !(_current > _enumerators[i].Enum.Current)) continue; + + _current = _enumerators[i].Enum.Current; + nextIndex = i; + } + + if (nextIndex < 0) + { + return false; + } + + Current = _enumerators[nextIndex].Enum.Current; + + return true; + } + + public void Reset() + { + throw new NotSupportedException(); + } + + public DateOnly Current { get; private set; } + + object IEnumerator.Current => Current; + + public void Dispose() + { + } + + private struct EWrapper + { + public EWrapper(IEnumerator @enum) + { + Enum = @enum; + CanMoveNext = true; + } + + public IEnumerator Enum { get; } + public bool CanMoveNext { get; private set; } + + public bool MoveNext() + { + CanMoveNext = Enum.MoveNext(); + return CanMoveNext; + } + + public override int GetHashCode() + { + return Enum.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/src/DateRecurrenceR/Core/IInt32Based.cs b/src/DateRecurrenceR/Core/IInt32Based.cs index 48f949e..7c5eada 100644 --- a/src/DateRecurrenceR/Core/IInt32Based.cs +++ b/src/DateRecurrenceR/Core/IInt32Based.cs @@ -6,7 +6,7 @@ namespace DateRecurrenceR.Core; /// /// /// -#if NET8_0 +#if NET8_0_OR_GREATER public interface IInt32Based : IEquatable, IEqualityOperators, IMinMaxValue where TSelf : IInt32Based; #endif \ No newline at end of file diff --git a/src/DateRecurrenceR/DateRecurrenceR.csproj b/src/DateRecurrenceR/DateRecurrenceR.csproj index 7834c61..855f70b 100644 --- a/src/DateRecurrenceR/DateRecurrenceR.csproj +++ b/src/DateRecurrenceR/DateRecurrenceR.csproj @@ -9,7 +9,7 @@ 0.5.3-beta.2 true enable - net6.0;net8.0 + net6.0;net8.0;net9.0 enable Nullable true diff --git a/src/DateRecurrenceR/Recurrence.cs b/src/DateRecurrenceR/Recurrence.cs index 7c91a40..63e1278 100644 --- a/src/DateRecurrenceR/Recurrence.cs +++ b/src/DateRecurrenceR/Recurrence.cs @@ -6,6 +6,16 @@ public readonly partial struct Recurrence { private static readonly EmptyEnumerator EmptyEnumerator = new(); + public static IEnumerator Union(IEnumerator d1, IEnumerator d2) + { + return new UnionEnumerator(new[] {d1, d2}); + } + + public static IEnumerator Union(params IEnumerator[] enumerators) + { + return new UnionEnumerator(enumerators); + } + private static DateOnly DateOnlyMin(DateOnly val1, DateOnly val2) { return val1 <= val2 ? val1 : val2; diff --git a/src/DateRecurrenceR/WeekDays.cs b/src/DateRecurrenceR/WeekDays.cs index db932b1..f5f477c 100644 --- a/src/DateRecurrenceR/WeekDays.cs +++ b/src/DateRecurrenceR/WeekDays.cs @@ -2,7 +2,7 @@ namespace DateRecurrenceR; public sealed class WeekDays { -#if NET8_0 +#if NET8_0_OR_GREATER private readonly WeekDaysArray _ds = new WeekDaysArray(); #else private readonly bool[] _ds = new bool[DaysInWeek]; @@ -38,7 +38,7 @@ public WeekDays(DayOfWeek day1, DayOfWeek day2, DayOfWeek day3, DayOfWeek day4, { } -#if NET8_0 +#if NET8_0_OR_GREATER public WeekDays(WeekDaysArray daysArray) { MinDay = (DayOfWeek) DaysInWeek; diff --git a/src/DateRecurrenceR/WeekDaysArray.cs b/src/DateRecurrenceR/WeekDaysArray.cs index e0df7ff..0eff699 100644 --- a/src/DateRecurrenceR/WeekDaysArray.cs +++ b/src/DateRecurrenceR/WeekDaysArray.cs @@ -1,4 +1,4 @@ -#if NET8_0 +#if NET8_0_OR_GREATER namespace DateRecurrenceR; [System.Runtime.CompilerServices.InlineArray(7)] diff --git a/test/DateRecurrenceR.Tests.Unit/Collections/UnionEnumeratorTest.cs b/test/DateRecurrenceR.Tests.Unit/Collections/UnionEnumeratorTest.cs new file mode 100644 index 0000000..0c985eb --- /dev/null +++ b/test/DateRecurrenceR.Tests.Unit/Collections/UnionEnumeratorTest.cs @@ -0,0 +1,101 @@ +using DateRecurrenceR.Collections; +using DateRecurrenceR.Core; +using FluentAssertions; +using JetBrains.Annotations; + +namespace DateRecurrenceR.Tests.Unit.Collections; + +[TestSubject(typeof(UnionEnumerator))] +public class UnionEnumeratorTest +{ + [Fact] + public void Union_two_recurrences() + { + // Arrange + const int takeCount = 5; + var interval = new Interval(1); + var beginDate = DateOnly.MinValue; + var fromDate = beginDate; + + var enumerator1 = Recurrence.Yearly(beginDate, fromDate, takeCount, new DayOfYear(1), interval); + var enumerator2 = Recurrence.Yearly(beginDate, fromDate, takeCount, new DayOfYear(2), interval); + + var res = Recurrence.Union(enumerator1, enumerator2); + + // Act + var list = new List(); + + while (res.MoveNext()) + { + list.Add(res.Current); + } + + //Assert + list.Count.Should().Be(takeCount * 2); + } + + [Fact] + public void Union_one_recurrence_multiple_times() + { + // Arrange + const int takeCount = 5; + var dayOfYear = new DayOfYear(256); + var interval = new Interval(1); + var beginDate = DateOnly.MinValue; + var fromDate = beginDate; + + var equivalentEnumerator = Recurrence.Yearly(beginDate, fromDate, takeCount, dayOfYear, interval); + var enumerator = Recurrence.Yearly(beginDate, fromDate, takeCount, dayOfYear, interval); + + var res = Recurrence.Union(enumerator, enumerator, enumerator, enumerator); + res = Recurrence.Union(res, enumerator, enumerator, enumerator); + res = Recurrence.Union(res, enumerator, enumerator); + res = Recurrence.Union(res, enumerator); + + // Act + var equivalentList = new List(); + while (equivalentEnumerator.MoveNext()) + { + equivalentList.Add(equivalentEnumerator.Current); + } + + var list = new List(); + while (res.MoveNext()) + { + list.Add(res.Current); + } + + //Assert + list.Count.Should().Be(takeCount); + list.Should().BeEquivalentTo(equivalentList); + } + + [Fact] + public void Union_two_recurrence_multiple_times() + { + // Arrange + const int takeCount = 5; + var interval = new Interval(1); + var beginDate = DateOnly.MinValue; + var fromDate = beginDate; + + var enumerator1 = Recurrence.Yearly(beginDate, fromDate, takeCount, new DayOfYear(1), interval); + var enumerator2 = Recurrence.Yearly(beginDate, fromDate, takeCount, new DayOfYear(2), interval); + + var res1 = Recurrence.Union(enumerator1, enumerator1, enumerator1, enumerator1); + var res2 = Recurrence.Union(enumerator2, enumerator2, enumerator2, enumerator2); + var res = Recurrence.Union(res1, res2); + res = Recurrence.Union(res, res1, enumerator2, enumerator1); + res = Recurrence.Union(res, res2, enumerator1, enumerator2); + + // Act + var list = new List(); + while (res.MoveNext()) + { + list.Add(res.Current); + } + + //Assert + list.Count.Should().Be(takeCount * 2); + } +} \ No newline at end of file diff --git a/test/DateRecurrenceR.Tests.Unit/DateRecurrenceR.Tests.Unit.csproj b/test/DateRecurrenceR.Tests.Unit/DateRecurrenceR.Tests.Unit.csproj index 5f0bd56..05c412d 100644 --- a/test/DateRecurrenceR.Tests.Unit/DateRecurrenceR.Tests.Unit.csproj +++ b/test/DateRecurrenceR.Tests.Unit/DateRecurrenceR.Tests.Unit.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 enable enable Nullable