diff --git a/Redcraft.Utility/Source/Private/Testing/Algorithms.cpp b/Redcraft.Utility/Source/Private/Testing/Algorithms.cpp index 6e60565..9d5b2cf 100644 --- a/Redcraft.Utility/Source/Private/Testing/Algorithms.cpp +++ b/Redcraft.Utility/Source/Private/Testing/Algorithms.cpp @@ -3,6 +3,7 @@ #include "Algorithms/Algorithms.h" #include "Containers/Array.h" #include "Containers/List.h" +#include "Ranges/Factory.h" #include "Miscellaneous/AssertionMacros.h" NAMESPACE_REDCRAFT_BEGIN @@ -15,20 +16,109 @@ NAMESPACE_PRIVATE_BEGIN void TestBasic() { - TArray Arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + { + TArray Arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - auto Iter = Arr.Begin(); + auto Iter = Arr.Begin(); - Algorithms::Advance(Iter, 5); + Algorithms::Advance(Iter, 5); - always_check(*Iter == 5); + always_check(*Iter == 5); - always_check(Algorithms::Distance(Arr.Begin(), Iter) == 5); + always_check(Algorithms::Distance(Arr.Begin(), Iter) == 5); - always_check(Algorithms::Distance(Arr) == 10); + always_check(Algorithms::Distance(Arr) == 10); - always_check(*Algorithms::Next(Iter, 2) == 7); - always_check(*Algorithms::Prev(Iter, 2) == 3); + always_check(*Algorithms::Next(Iter) == 6); + always_check(*Algorithms::Next(Iter, 2) == 7); + always_check(*Algorithms::Prev(Iter) == 4); + always_check(*Algorithms::Prev(Iter, 2) == 3); + + always_check(Algorithms::Next(Iter, Arr.End()) == Arr.End()); + always_check(Algorithms::Next(Iter, 16, Arr.End()) == Arr.End()); + + always_check(Algorithms::Prev(Iter, 16, Arr.Begin()) == Arr.Begin()); + + Iter = Arr.Begin(); + + Algorithms::Advance(Iter, Arr.End()); + + always_check(Iter == Arr.End()); + + Iter = Arr.Begin(); + + always_check(Algorithms::Advance(Iter, 16, Arr.End()) == 6); + + always_check(Iter == Arr.End()); + } + + { + TList Arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + auto Iter = Arr.Begin(); + + Algorithms::Advance(Iter, 5); + + always_check(*Iter == 5); + + always_check(Algorithms::Distance(Arr.Begin(), Iter) == 5); + + always_check(Algorithms::Distance(Arr) == 10); + + always_check(*Algorithms::Next(Iter) == 6); + always_check(*Algorithms::Next(Iter, 2) == 7); + always_check(*Algorithms::Prev(Iter) == 4); + always_check(*Algorithms::Prev(Iter, 2) == 3); + + always_check(Algorithms::Next(Iter, Arr.End()) == Arr.End()); + always_check(Algorithms::Next(Iter, 16, Arr.End()) == Arr.End()); + + always_check(Algorithms::Prev(Iter, 16, Arr.Begin()) == Arr.Begin()); + + Iter = Arr.Begin(); + + Algorithms::Advance(Iter, Arr.End()); + + always_check(Iter == Arr.End()); + + Iter = Arr.Begin(); + + always_check(Algorithms::Advance(Iter, 16, Arr.End()) == 6); + + always_check(Iter == Arr.End()); + } + + { + auto Arr = Ranges::Iota(0, 10); + + auto Iter = Arr.Begin(); + + Algorithms::Advance(Iter, 5); + + always_check(*Iter == 5); + + always_check(Algorithms::Distance(Arr.Begin(), Iter) == 5); + + always_check(Algorithms::Distance(Arr) == 10); + + always_check(*Algorithms::Next(Iter) == 6); + always_check(*Algorithms::Next(Iter, 2) == 7); + + always_check(Algorithms::Next(Iter, Arr.End()) == Arr.End()); + always_check(Algorithms::Next(Iter, 16, Arr.End()) == Arr.End()); + + Iter = Arr.Begin(); + + Algorithms::Advance(Iter, Arr.End()); + + always_check(Iter == Arr.End()); + + Iter = Arr.Begin(); + + always_check(Algorithms::Advance(Iter, 16, Arr.End()) == 6); + + always_check(Iter == Arr.End()); + } } NAMESPACE_PRIVATE_END diff --git a/Redcraft.Utility/Source/Public/Algorithms/Basic.h b/Redcraft.Utility/Source/Public/Algorithms/Basic.h index 59376d0..7ea63e1 100644 --- a/Redcraft.Utility/Source/Public/Algorithms/Basic.h +++ b/Redcraft.Utility/Source/Public/Algorithms/Basic.h @@ -6,6 +6,7 @@ #include "Iterators/Sentinel.h" #include "Iterators/BasicIterator.h" #include "Ranges/Utility.h" +#include "Numerics/Math.h" #include "Miscellaneous/AssertionMacros.h" NAMESPACE_REDCRAFT_BEGIN @@ -15,7 +16,7 @@ NAMESPACE_MODULE_BEGIN(Utility) NAMESPACE_BEGIN(Algorithms) /** Increments given iterator 'Iter' by 'N' elements. */ -template +template FORCEINLINE constexpr void Advance(I& Iter, ptrdiff N) { if constexpr (CRandomAccessIterator) @@ -32,22 +33,84 @@ FORCEINLINE constexpr void Advance(I& Iter, ptrdiff N) else { checkf(N >= 0, TEXT("The iterator must satisfy the CBidirectionalIterator in order to be decremented.")); + for (; N > 0; --N) ++Iter; } } +/** Increments given iterator 'Iter' to the 'Sent' position. */ +template S> +FORCEINLINE constexpr void Advance(I& Iter, S Sent) +{ + if constexpr (CAssignableFrom) + { + Iter = Sent; + } + + else if constexpr (CSizedSentinelFor) + { + Algorithms::Advance(Iter, Sent - Iter); + } + + else + { + for (; Iter != Sent; ++Iter); + } +} + +/** Increments given iterator 'Iter' by 'N' elements, up to the 'Sent' position. */ +template S> +FORCEINLINE constexpr ptrdiff Advance(I& Iter, ptrdiff N, S Sent) +{ + if constexpr (CSizedSentinelFor) + { + const ptrdiff Distance = Sent - Iter; + + if (Math::Abs(N) > Math::Abs(Distance)) + { + Algorithms::Advance(Iter, Sent); + + return N - Distance; + } + + Algorithms::Advance(Iter, N); + + return 0; + } + + else if constexpr (CBidirectionalIterator) + { + for (; N > 0 && Iter != Sent; --N) ++Iter; + for (; N < 0 && Iter != Sent; ++N) --Iter; + + return N; + } + + else + { + checkf(N >= 0, TEXT("The iterator must satisfy the CBidirectionalIterator in order to be decremented.")); + + for (; N > 0 && Iter != Sent; --N) ++Iter; + + return N; + } +} + /** @return The number of hops from 'First' to 'Last'. */ -template S> +template S> NODISCARD FORCEINLINE constexpr ptrdiff Distance(I First, S Last) { if constexpr (CSizedSentinelFor) { return Last - First; } + else { ptrdiff Result = 0; + for (; First != Last; ++First) ++Result; + return Result; } } @@ -60,25 +123,64 @@ NODISCARD FORCEINLINE constexpr ptrdiff Distance(R&& Range) { return static_cast(Ranges::Num(Range)); } + else return Algorithms::Distance(Ranges::Begin(Range), Ranges::End(Range)); } +/** @return The 1-th successor of iterator 'Iter'. */ +template +NODISCARD FORCEINLINE constexpr I Next(I Iter) +{ + return ++Iter; +} + /** @return The 'N'-th successor of iterator 'Iter'. */ -template -NODISCARD FORCEINLINE constexpr I Next(I Iter, ptrdiff N = 1) +template +NODISCARD FORCEINLINE constexpr I Next(I Iter, ptrdiff N) { Algorithms::Advance(Iter, N); return Iter; } +/** @return The successor of iterator 'Iter' to the 'Sent' position. */ +template S> +NODISCARD FORCEINLINE constexpr I Next(I Iter, S Sent) +{ + Algorithms::Advance(Iter, Sent); + return Iter; +} + +/** @return The 'N'-th successor of iterator 'Iter', up to the 'Sent' position. */ +template S> +NODISCARD FORCEINLINE constexpr I Next(I Iter, ptrdiff N, S Sent) +{ + Algorithms::Advance(Iter, N, Sent); + return Iter; +} + +/** @return The 1-th predecessor of iterator 'Iter'. */ +template +NODISCARD FORCEINLINE constexpr I Prev(I Iter) +{ + return --Iter; +} + /** @return The 'N'-th predecessor of iterator 'Iter'. */ template -NODISCARD FORCEINLINE constexpr I Prev(I Iter, ptrdiff N = 1) +NODISCARD FORCEINLINE constexpr I Prev(I Iter, ptrdiff N) { Algorithms::Advance(Iter, -N); return Iter; } +/** @return The predecessor of iterator 'Iter', up to the 'First' position. */ +template +NODISCARD FORCEINLINE constexpr I Prev(I Iter, ptrdiff N, I First) +{ + Algorithms::Advance(Iter, -N, First); + return Iter; +} + NAMESPACE_END(Algorithms) NAMESPACE_MODULE_END(Utility)