diff --git a/Redcraft.Utility/Source/Private/Testing/Algorithms.cpp b/Redcraft.Utility/Source/Private/Testing/Algorithms.cpp index 9d5b2cf..ee5b2c0 100644 --- a/Redcraft.Utility/Source/Private/Testing/Algorithms.cpp +++ b/Redcraft.Utility/Source/Private/Testing/Algorithms.cpp @@ -4,6 +4,7 @@ #include "Containers/Array.h" #include "Containers/List.h" #include "Ranges/Factory.h" +#include "Numerics/Math.h" #include "Miscellaneous/AssertionMacros.h" NAMESPACE_REDCRAFT_BEGIN @@ -121,11 +122,243 @@ void TestBasic() } } +void TestSearch() +{ + { + TArray Arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + TList Brr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + auto Crr = Ranges::Iota(0, 10); + + always_check( Algorithms::AllOf(Arr, [](int A) { return A < 10; })); + always_check( Algorithms::AllOf(Brr, [](int A) { return A < 10; })); + always_check( Algorithms::AllOf(Crr, [](int A) { return A < 10; })); + always_check(!Algorithms::AllOf(Arr, [](int A) { return A > 5; })); + always_check(!Algorithms::AllOf(Brr, [](int A) { return A > 5; })); + always_check(!Algorithms::AllOf(Crr, [](int A) { return A > 5; })); + + always_check( Algorithms::AllOf(Arr.Begin(), Arr.End(), [](int A) { return A < 10; })); + always_check( Algorithms::AllOf(Brr.Begin(), Brr.End(), [](int A) { return A < 10; })); + always_check( Algorithms::AllOf(Crr.Begin(), Crr.End(), [](int A) { return A < 10; })); + always_check(!Algorithms::AllOf(Arr.Begin(), Arr.End(), [](int A) { return A > 5; })); + always_check(!Algorithms::AllOf(Brr.Begin(), Brr.End(), [](int A) { return A > 5; })); + always_check(!Algorithms::AllOf(Crr.Begin(), Crr.End(), [](int A) { return A > 5; })); + + always_check(Algorithms::AnyOf(Arr, [](int A) { return A < 10; })); + always_check(Algorithms::AnyOf(Brr, [](int A) { return A < 10; })); + always_check(Algorithms::AnyOf(Crr, [](int A) { return A < 10; })); + always_check(Algorithms::AnyOf(Arr, [](int A) { return A > 5; })); + always_check(Algorithms::AnyOf(Brr, [](int A) { return A > 5; })); + always_check(Algorithms::AnyOf(Crr, [](int A) { return A > 5; })); + + always_check(Algorithms::AnyOf(Arr.Begin(), Arr.End(), [](int A) { return A < 10; })); + always_check(Algorithms::AnyOf(Brr.Begin(), Brr.End(), [](int A) { return A < 10; })); + always_check(Algorithms::AnyOf(Crr.Begin(), Crr.End(), [](int A) { return A < 10; })); + always_check(Algorithms::AnyOf(Arr.Begin(), Arr.End(), [](int A) { return A > 5; })); + always_check(Algorithms::AnyOf(Brr.Begin(), Brr.End(), [](int A) { return A > 5; })); + always_check(Algorithms::AnyOf(Crr.Begin(), Crr.End(), [](int A) { return A > 5; })); + + always_check(!Algorithms::NoneOf(Arr, [](int A) { return A < 10; })); + always_check(!Algorithms::NoneOf(Brr, [](int A) { return A < 10; })); + always_check(!Algorithms::NoneOf(Crr, [](int A) { return A < 10; })); + always_check(!Algorithms::NoneOf(Arr, [](int A) { return A > 5; })); + always_check(!Algorithms::NoneOf(Brr, [](int A) { return A > 5; })); + always_check(!Algorithms::NoneOf(Crr, [](int A) { return A > 5; })); + + always_check(!Algorithms::NoneOf(Arr.Begin(), Arr.End(), [](int A) { return A < 10; })); + always_check(!Algorithms::NoneOf(Brr.Begin(), Brr.End(), [](int A) { return A < 10; })); + always_check(!Algorithms::NoneOf(Crr.Begin(), Crr.End(), [](int A) { return A < 10; })); + always_check(!Algorithms::NoneOf(Arr.Begin(), Arr.End(), [](int A) { return A > 5; })); + always_check(!Algorithms::NoneOf(Brr.Begin(), Brr.End(), [](int A) { return A > 5; })); + always_check(!Algorithms::NoneOf(Crr.Begin(), Crr.End(), [](int A) { return A > 5; })); + + always_check( Algorithms::Contains(Arr, 5)); + always_check( Algorithms::Contains(Brr, 5)); + always_check( Algorithms::Contains(Crr, 5)); + always_check(!Algorithms::Contains(Arr, 10)); + always_check(!Algorithms::Contains(Brr, 10)); + always_check(!Algorithms::Contains(Crr, 10)); + + always_check( Algorithms::Contains(Arr.Begin(), Arr.End(), 5)); + always_check( Algorithms::Contains(Brr.Begin(), Brr.End(), 5)); + always_check( Algorithms::Contains(Crr.Begin(), Crr.End(), 5)); + always_check(!Algorithms::Contains(Arr.Begin(), Arr.End(), 10)); + always_check(!Algorithms::Contains(Brr.Begin(), Brr.End(), 10)); + always_check(!Algorithms::Contains(Crr.Begin(), Crr.End(), 10)); + + auto Projection = [](int A) { return A % 4; }; // Project to { 0, 1, 2, 3, 0, 1, 2, 3, 0, 1 } + + always_check(Algorithms::Find(Arr, 2, { }, Projection) == Algorithms::Next(Arr.Begin(), 2)); + always_check(Algorithms::Find(Brr, 2, { }, Projection) == Algorithms::Next(Brr.Begin(), 2)); + always_check(Algorithms::Find(Crr, 2, { }, Projection) == Algorithms::Next(Crr.Begin(), 2)); + + always_check(Algorithms::Find(Arr.Begin(), Arr.End(), 2, { }, Projection) == Algorithms::Next(Arr.Begin(), 2)); + always_check(Algorithms::Find(Brr.Begin(), Brr.End(), 2, { }, Projection) == Algorithms::Next(Brr.Begin(), 2)); + always_check(Algorithms::Find(Crr.Begin(), Crr.End(), 2, { }, Projection) == Algorithms::Next(Crr.Begin(), 2)); + + always_check(Algorithms::Find(Arr, 10) == Arr.End()); + always_check(Algorithms::Find(Brr, 10) == Brr.End()); + always_check(Algorithms::Find(Crr, 10) == Crr.End()); + + always_check(Algorithms::Find(Arr, Ranges::Iota(1, 4), { }, Projection).Begin() == Algorithms::Next(Arr.Begin())); + always_check(Algorithms::Find(Brr, Ranges::Iota(1, 4), { }, Projection).Begin() == Algorithms::Next(Brr.Begin())); + always_check(Algorithms::Find(Crr, Ranges::Iota(1, 4), { }, Projection).Begin() == Algorithms::Next(Crr.Begin())); + + always_check(Algorithms::Find(Arr, Ranges::Iota(4, 16)).IsEmpty()); + always_check(Algorithms::Find(Brr, Ranges::Iota(4, 16)).IsEmpty()); + always_check(Algorithms::Find(Crr, Ranges::Iota(4, 16)).IsEmpty()); + + always_check(Algorithms::FindIf(Arr, [](int A) { return A == 2; }, Projection) == Algorithms::Next(Arr.Begin(), 2)); + always_check(Algorithms::FindIf(Brr, [](int A) { return A == 2; }, Projection) == Algorithms::Next(Brr.Begin(), 2)); + always_check(Algorithms::FindIf(Crr, [](int A) { return A == 2; }, Projection) == Algorithms::Next(Crr.Begin(), 2)); + + always_check(Algorithms::FindIf(Arr.Begin(), Arr.End(), [](int A) { return A == 2; }, Projection) == Algorithms::Next(Arr.Begin(), 2)); + always_check(Algorithms::FindIf(Brr.Begin(), Brr.End(), [](int A) { return A == 2; }, Projection) == Algorithms::Next(Brr.Begin(), 2)); + always_check(Algorithms::FindIf(Crr.Begin(), Crr.End(), [](int A) { return A == 2; }, Projection) == Algorithms::Next(Crr.Begin(), 2)); + + always_check(Algorithms::FindIf(Arr, [](int A) { return A == 10; }) == Arr.End()); + always_check(Algorithms::FindIf(Brr, [](int A) { return A == 10; }) == Brr.End()); + always_check(Algorithms::FindIf(Crr, [](int A) { return A == 10; }) == Crr.End()); + + always_check(Algorithms::FindIfNot(Arr, [](int A) { return A > 0; }, Projection) == Arr.Begin()); + always_check(Algorithms::FindIfNot(Brr, [](int A) { return A > 0; }, Projection) == Brr.Begin()); + always_check(Algorithms::FindIfNot(Crr, [](int A) { return A > 0; }, Projection) == Crr.Begin()); + + always_check(Algorithms::FindIfNot(Arr.Begin(), Arr.End(), [](int A) { return A > 0; }, Projection) == Arr.Begin()); + always_check(Algorithms::FindIfNot(Brr.Begin(), Brr.End(), [](int A) { return A > 0; }, Projection) == Brr.Begin()); + always_check(Algorithms::FindIfNot(Crr.Begin(), Crr.End(), [](int A) { return A > 0; }, Projection) == Crr.Begin()); + + always_check(Algorithms::FindIfNot(Arr, [](int A) { return A < 8; }) == Algorithms::Next(Arr.Begin(), 8)); + always_check(Algorithms::FindIfNot(Brr, [](int A) { return A < 8; }) == Algorithms::Next(Brr.Begin(), 8)); + always_check(Algorithms::FindIfNot(Crr, [](int A) { return A < 8; }) == Algorithms::Next(Crr.Begin(), 8)); + + always_check(Algorithms::FindLast(Arr, 2, { }, Projection) == Algorithms::Next(Arr.Begin(), 6)); + always_check(Algorithms::FindLast(Brr, 2, { }, Projection) == Algorithms::Next(Brr.Begin(), 6)); + always_check(Algorithms::FindLast(Crr, 2, { }, Projection) == Algorithms::Next(Crr.Begin(), 6)); + + always_check(Algorithms::FindLast(Arr.Begin(), Arr.End(), 2, { }, Projection) == Algorithms::Next(Arr.Begin(), 6)); + always_check(Algorithms::FindLast(Brr.Begin(), Brr.End(), 2, { }, Projection) == Algorithms::Next(Brr.Begin(), 6)); + always_check(Algorithms::FindLast(Crr.Begin(), Crr.End(), 2, { }, Projection) == Algorithms::Next(Crr.Begin(), 6)); + + always_check(Algorithms::FindLast(Arr, 10) == Arr.End()); + always_check(Algorithms::FindLast(Brr, 10) == Brr.End()); + always_check(Algorithms::FindLast(Crr, 10) == Crr.End()); + + always_check(Algorithms::FindLast(Arr, Ranges::Iota(1, 4), { }, Projection).Begin() == Algorithms::Next(Arr.Begin(), 5)); + always_check(Algorithms::FindLast(Brr, Ranges::Iota(1, 4), { }, Projection).Begin() == Algorithms::Next(Brr.Begin(), 5)); + always_check(Algorithms::FindLast(Crr, Ranges::Iota(1, 4), { }, Projection).Begin() == Algorithms::Next(Crr.Begin(), 5)); + + always_check(Algorithms::FindLast(Arr, Ranges::Iota(4, 16)).IsEmpty()); + always_check(Algorithms::FindLast(Brr, Ranges::Iota(4, 16)).IsEmpty()); + always_check(Algorithms::FindLast(Crr, Ranges::Iota(4, 16)).IsEmpty()); + + always_check(Algorithms::FindLastIf(Arr, [](int A) { return A == 2; }, Projection) == Algorithms::Next(Arr.Begin(), 6)); + always_check(Algorithms::FindLastIf(Brr, [](int A) { return A == 2; }, Projection) == Algorithms::Next(Brr.Begin(), 6)); + always_check(Algorithms::FindLastIf(Crr, [](int A) { return A == 2; }, Projection) == Algorithms::Next(Crr.Begin(), 6)); + + always_check(Algorithms::FindLastIf(Arr.Begin(), Arr.End(), [](int A) { return A == 2; }, Projection) == Algorithms::Next(Arr.Begin(), 6)); + always_check(Algorithms::FindLastIf(Brr.Begin(), Brr.End(), [](int A) { return A == 2; }, Projection) == Algorithms::Next(Brr.Begin(), 6)); + always_check(Algorithms::FindLastIf(Crr.Begin(), Crr.End(), [](int A) { return A == 2; }, Projection) == Algorithms::Next(Crr.Begin(), 6)); + + always_check(Algorithms::FindLastIf(Arr, [](int A) { return A == 10; }) == Arr.End()); + always_check(Algorithms::FindLastIf(Brr, [](int A) { return A == 10; }) == Brr.End()); + always_check(Algorithms::FindLastIf(Crr, [](int A) { return A == 10; }) == Crr.End()); + + always_check(Algorithms::FindLastIfNot(Arr, [](int A) { return A > 0; }, Projection) == Algorithms::Next(Arr.Begin(), 8)); + always_check(Algorithms::FindLastIfNot(Brr, [](int A) { return A > 0; }, Projection) == Algorithms::Next(Brr.Begin(), 8)); + always_check(Algorithms::FindLastIfNot(Crr, [](int A) { return A > 0; }, Projection) == Algorithms::Next(Crr.Begin(), 8)); + + always_check(Algorithms::FindLastIfNot(Arr.Begin(), Arr.End(), [](int A) { return A > 0; }, Projection) == Algorithms::Next(Arr.Begin(), 8)); + always_check(Algorithms::FindLastIfNot(Brr.Begin(), Brr.End(), [](int A) { return A > 0; }, Projection) == Algorithms::Next(Brr.Begin(), 8)); + always_check(Algorithms::FindLastIfNot(Crr.Begin(), Crr.End(), [](int A) { return A > 0; }, Projection) == Algorithms::Next(Crr.Begin(), 8)); + + always_check(Algorithms::FindLastIfNot(Arr, [](int A) { return A < 8; }) == Algorithms::Next(Arr.Begin(), 9)); + always_check(Algorithms::FindLastIfNot(Brr, [](int A) { return A < 8; }) == Algorithms::Next(Brr.Begin(), 9)); + always_check(Algorithms::FindLastIfNot(Crr, [](int A) { return A < 8; }) == Algorithms::Next(Crr.Begin(), 9)); + + always_check(Algorithms::FindAdjacent(Arr, { }, [](int A) { return Math::DivAndCeil(A, 2); }) == Algorithms::Next(Arr.Begin())); + always_check(Algorithms::FindAdjacent(Brr, { }, [](int A) { return Math::DivAndCeil(A, 2); }) == Algorithms::Next(Brr.Begin())); + always_check(Algorithms::FindAdjacent(Crr, { }, [](int A) { return Math::DivAndCeil(A, 2); }) == Algorithms::Next(Crr.Begin())); + + always_check(Algorithms::FindAdjacent(Arr.Begin(), Arr.End(), { }, [](int A) { return Math::DivAndCeil(A, 2); }) == Algorithms::Next(Arr.Begin())); + always_check(Algorithms::FindAdjacent(Brr.Begin(), Brr.End(), { }, [](int A) { return Math::DivAndCeil(A, 2); }) == Algorithms::Next(Brr.Begin())); + always_check(Algorithms::FindAdjacent(Crr.Begin(), Crr.End(), { }, [](int A) { return Math::DivAndCeil(A, 2); }) == Algorithms::Next(Crr.Begin())); + + always_check(Algorithms::FindAdjacent(Arr) == Arr.End()); + always_check(Algorithms::FindAdjacent(Brr) == Brr.End()); + always_check(Algorithms::FindAdjacent(Crr) == Crr.End()); + + always_check(Algorithms::Count(Arr, 2, { }, Projection) == 2); + always_check(Algorithms::Count(Brr, 2, { }, Projection) == 2); + always_check(Algorithms::Count(Crr, 2, { }, Projection) == 2); + + always_check(Algorithms::Count(Arr.Begin(), Arr.End(), 2, { }, Projection) == 2); + always_check(Algorithms::Count(Brr.Begin(), Brr.End(), 2, { }, Projection) == 2); + always_check(Algorithms::Count(Crr.Begin(), Crr.End(), 2, { }, Projection) == 2); + + always_check(Algorithms::Count(Arr, 10) == 0); + always_check(Algorithms::Count(Brr, 10) == 0); + always_check(Algorithms::Count(Crr, 10) == 0); + + always_check(Algorithms::CountIf(Arr, [](int A) { return A == 2; }, Projection) == 2); + always_check(Algorithms::CountIf(Brr, [](int A) { return A == 2; }, Projection) == 2); + always_check(Algorithms::CountIf(Crr, [](int A) { return A == 2; }, Projection) == 2); + + always_check(Algorithms::CountIf(Arr.Begin(), Arr.End(), [](int A) { return A == 2; }, Projection) == 2); + always_check(Algorithms::CountIf(Brr.Begin(), Brr.End(), [](int A) { return A == 2; }, Projection) == 2); + always_check(Algorithms::CountIf(Crr.Begin(), Crr.End(), [](int A) { return A == 2; }, Projection) == 2); + + always_check(Algorithms::CountIf(Arr, [](int A) { return A == 10; }) == 0); + always_check(Algorithms::CountIf(Brr, [](int A) { return A == 10; }) == 0); + always_check(Algorithms::CountIf(Crr, [](int A) { return A == 10; }) == 0); + + always_check(Algorithms::Mismatch(Arr, Arr, { }, Projection) == MakeTuple(Algorithms::Next(Arr.Begin(), 4), Algorithms::Next(Arr.Begin(), 4))); + always_check(Algorithms::Mismatch(Brr, Brr, { }, Projection) == MakeTuple(Algorithms::Next(Brr.Begin(), 4), Algorithms::Next(Brr.Begin(), 4))); + always_check(Algorithms::Mismatch(Crr, Crr, { }, Projection) == MakeTuple(Algorithms::Next(Crr.Begin(), 4), Algorithms::Next(Crr.Begin(), 4))); + + always_check(Algorithms::Mismatch(Arr.Begin(), Arr.End(), Brr.Begin(), Brr.End(), { }, Projection) == MakeTuple(Algorithms::Next(Arr.Begin(), 4), Algorithms::Next(Brr.Begin(), 4))); + always_check(Algorithms::Mismatch(Brr.Begin(), Brr.End(), Crr.Begin(), Crr.End(), { }, Projection) == MakeTuple(Algorithms::Next(Brr.Begin(), 4), Algorithms::Next(Crr.Begin(), 4))); + always_check(Algorithms::Mismatch(Crr.Begin(), Crr.End(), Arr.Begin(), Arr.End(), { }, Projection) == MakeTuple(Algorithms::Next(Crr.Begin(), 4), Algorithms::Next(Arr.Begin(), 4))); + + always_check(Algorithms::Mismatch(Arr, Brr, { }, Projection) == MakeTuple(Algorithms::Next(Arr.Begin(), 4), Algorithms::Next(Brr.Begin(), 4))); + always_check(Algorithms::Mismatch(Brr, Crr, { }, Projection) == MakeTuple(Algorithms::Next(Brr.Begin(), 4), Algorithms::Next(Crr.Begin(), 4))); + always_check(Algorithms::Mismatch(Crr, Arr, { }, Projection) == MakeTuple(Algorithms::Next(Crr.Begin(), 4), Algorithms::Next(Arr.Begin(), 4))); + + always_check(Algorithms::Equal(Arr, Arr)); + always_check(Algorithms::Equal(Brr, Brr)); + always_check(Algorithms::Equal(Crr, Crr)); + + always_check(Algorithms::Equal(Arr.Begin(), Arr.End(), Brr.Begin(), Brr.End())); + always_check(Algorithms::Equal(Brr.Begin(), Brr.End(), Crr.Begin(), Crr.End())); + always_check(Algorithms::Equal(Crr.Begin(), Crr.End(), Arr.Begin(), Arr.End())); + + always_check(Algorithms::Equal(Arr, Brr)); + always_check(Algorithms::Equal(Brr, Crr)); + always_check(Algorithms::Equal(Crr, Arr)); + + always_check(Algorithms::StartsWith(Arr, Ranges::Iota(0, 8))); + always_check(Algorithms::StartsWith(Brr, Ranges::Iota(0, 8))); + always_check(Algorithms::StartsWith(Crr, Ranges::Iota(0, 8))); + + always_check(!Algorithms::StartsWith(Arr, Ranges::Iota(0, 8), { }, Projection)); + always_check(!Algorithms::StartsWith(Brr, Ranges::Iota(0, 8), { }, Projection)); + always_check(!Algorithms::StartsWith(Crr, Ranges::Iota(0, 8), { }, Projection)); + + always_check(Algorithms::EndsWith(Arr, Ranges::Iota(8, 10))); + always_check(Algorithms::EndsWith(Brr, Ranges::Iota(8, 10))); + always_check(Algorithms::EndsWith(Crr, Ranges::Iota(8, 10))); + + always_check(Algorithms::EndsWith(Arr, Ranges::Iota(0, 2), { }, Projection)); + always_check(Algorithms::EndsWith(Brr, Ranges::Iota(0, 2), { }, Projection)); + always_check(Algorithms::EndsWith(Crr, Ranges::Iota(0, 2), { }, Projection)); + } +} + NAMESPACE_PRIVATE_END void TestAlgorithms() { NAMESPACE_PRIVATE::TestBasic(); + NAMESPACE_PRIVATE::TestSearch(); } NAMESPACE_END(Testing) diff --git a/Redcraft.Utility/Source/Public/Algorithms/Algorithms.h b/Redcraft.Utility/Source/Public/Algorithms/Algorithms.h index 578db61..c98b9a2 100644 --- a/Redcraft.Utility/Source/Public/Algorithms/Algorithms.h +++ b/Redcraft.Utility/Source/Public/Algorithms/Algorithms.h @@ -2,3 +2,4 @@ #include "CoreTypes.h" #include "Algorithms/Basic.h" +#include "Algorithms/Search.h" diff --git a/Redcraft.Utility/Source/Public/Algorithms/Search.h b/Redcraft.Utility/Source/Public/Algorithms/Search.h new file mode 100644 index 0000000..7bc90ff --- /dev/null +++ b/Redcraft.Utility/Source/Public/Algorithms/Search.h @@ -0,0 +1,1755 @@ +#pragma once + +#include "CoreTypes.h" +#include "TypeTraits/TypeTraits.h" +#include "Templates/Utility.h" +#include "Templates/Invoke.h" +#include "Templates/ReferenceWrapper.h" +#include "Templates/Optional.h" +#include "Templates/Tuple.h" +#include "Iterators/Utility.h" +#include "Iterators/Sentinel.h" +#include "Iterators/BasicIterator.h" +#include "Iterators/ReverseIterator.h" +#include "Ranges/Utility.h" +#include "Ranges/View.h" +#include "Algorithms/Basic.h" +#include "Miscellaneous/AssertionMacros.h" + +NAMESPACE_REDCRAFT_BEGIN +NAMESPACE_MODULE_BEGIN(Redcraft) +NAMESPACE_MODULE_BEGIN(Utility) + +NAMESPACE_BEGIN(Algorithms) + +/** + * Checks if all elements in the range satisfy the predicate. + * + * @param Range - The range to check. + * @param Predicate - The unary predicate to satisfy. + * @param Projection - The projection to apply to the elements before checking. + * + * @return true if all elements satisfy the predicate, false otherwise. + */ +template > Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CPredicate>> Pred> +NODISCARD constexpr bool AllOf(R&& Range, Pred Predicate, Proj Projection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range).")); + } + + auto Iter = Ranges::Begin(Range); + auto Sent = Ranges::End (Range); + + for (; Iter != Sent; ++Iter) + { + if (!Invoke(Predicate, Invoke(Projection, *Iter))) + { + return false; + } + } + + return true; +} + +/** + * Checks if all elements in the range satisfy the predicate. + * + * @param First - The iterator of the range. + * @param Last - The sentinel of the range. + * @param Predicate - The unary predicate to satisfy. + * @param Projection - The projection to apply to the elements before checking. + * + * @return true if all elements satisfy the predicate, false otherwise. + */ +template S, + CRegularInvocable> Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CPredicate>> Pred> +NODISCARD FORCEINLINE constexpr bool AllOf(I First, S Last, Pred Predicate, Proj Projection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last.")); + } + + return Algorithms::AllOf(Ranges::View(MoveTemp(First), Last), Ref(Predicate), Ref(Projection)); +} + +/** + * Checks if any elements in the range satisfy the predicate. + * + * @param Range - The range to check. + * @param Predicate - The unary predicate to satisfy. + * @param Projection - The projection to apply to the elements before checking. + * + * @return true if any elements satisfy the predicate, false otherwise. + */ +template > Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CPredicate>> Pred> +NODISCARD constexpr bool AnyOf(R&& Range, Pred Predicate, Proj Projection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range).")); + } + + auto Iter = Ranges::Begin(Range); + auto Sent = Ranges::End (Range); + + for (; Iter != Sent; ++Iter) + { + if (Invoke(Predicate, Invoke(Projection, *Iter))) + { + return true; + } + } + + return false; +} + +/** + * Checks if any elements in the range satisfy the predicate. + * + * @param First - The iterator of the range. + * @param Last - The sentinel of the range. + * @param Predicate - The unary predicate to satisfy. + * @param Projection - The projection to apply to the elements before checking. + * + * @return true if any elements satisfy the predicate, false otherwise. + */ +template S, + CRegularInvocable> Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CPredicate>> Pred> +NODISCARD FORCEINLINE constexpr bool AnyOf(I First, S Last, Pred Predicate, Proj Projection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last.")); + } + + return Algorithms::AnyOf(Ranges::View(MoveTemp(First), Last), Ref(Predicate), Ref(Projection)); +} + +/** + * Checks if no elements in the range satisfy the predicate. + * + * @param Range - The range to check. + * @param Predicate - The unary predicate to satisfy. + * @param Projection - The projection to apply to the elements before checking. + * + * @return true if no elements satisfy the predicate, false otherwise. + */ +template > Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CPredicate>> Pred> +NODISCARD constexpr bool NoneOf(R&& Range, Pred Predicate, Proj Projection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range).")); + } + + auto Iter = Ranges::Begin(Range); + auto Sent = Ranges::End (Range); + + for (; Iter != Sent; ++Iter) + { + if (Invoke(Predicate, Invoke(Projection, *Iter))) + { + return false; + } + } + + return true; +} + +/** + * Checks if no elements in the range satisfy the predicate. + * + * @param First - The iterator of the range. + * @param Last - The sentinel of the range. + * @param Predicate - The unary predicate to satisfy. + * @param Projection - The projection to apply to the elements before checking. + * + * @return true if no elements satisfy the predicate, false otherwise. + */ +template S, + CRegularInvocable> Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CPredicate>> Pred> +NODISCARD FORCEINLINE constexpr bool NoneOf(I First, S Last, Pred Predicate, Proj Projection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last.")); + } + + return Algorithms::NoneOf(Ranges::View(MoveTemp(First), Last), Ref(Predicate), Ref(Projection)); +} + +/** + * Checks if the range contains the given element. + * + * @param Range - The range to check. + * @param Value - The value to check. + * @param Predicate - The equivalence relation predicate between the projected elements and the value. + * @param Projection - The projection to apply to the elements before checking. + * + * @return true if the range contains the value, false otherwise. + */ +template > Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CReferenceable T = TRemoveCVRef>>, + CEquivalenceRelation>, const T&> Pred = + TConditional>, const T&>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> +NODISCARD FORCEINLINE constexpr bool Contains(R&& Range, const T& Value, Pred Predicate = { }, Proj Projection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range).")); + } + + auto Iter = Ranges::Begin(Range); + auto Sent = Ranges::End (Range); + + for (; Iter != Sent; ++Iter) + { + if (Invoke(Predicate, Invoke(Projection, *Iter), Value)) + { + return true; + } + } + + return false; +} + +/** + * Checks if the range contains the given element. + * + * @param First - The iterator of the range. + * @param Last - The sentinel of the range. + * @param Value - The value to check. + * @param Predicate - The equivalence relation predicate between the projected elements and the value. + * @param Projection - The projection to apply to the elements before checking. + * + * @return true if the range contains the value, false otherwise. + */ +template S, + CRegularInvocable> Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CReferenceable T = TRemoveCVRef>>, + CEquivalenceRelation>, const T&> Pred = + TConditional>, const T&>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> +NODISCARD FORCEINLINE constexpr bool Contains(I First, S Last, const T& Value, Pred Predicate = { }, Proj Projection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last.")); + } + + return Algorithms::Contains(Ranges::View(MoveTemp(First), Last), Value, Ref(Predicate), Ref(Projection)); +} + +/** + * Checks if the range contains the given subrange. + * + * @param Haystack - The range of elements to examine, aka the haystack. + * @param Needle - The range of elements to search for, aka the needle. + * @param Predicate - The equivalence relation predicate between the projected elements. + * @param HaystackProjection - The projection to apply to the haystack's elements before checking. + * @param NeedleProjection - The projection to apply to the needle's elements before checking. + * + * @return true if the haystack contains the needle, false otherwise. + */ +template > Proj1 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CRegularInvocable> Proj2 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>, TInvokeResult>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> +NODISCARD constexpr bool Contains(R1&& Haystack, R2&& Needle, Pred Predicate = { }, Proj1 HaystackProjection = { }, Proj2 NeedleProjection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Haystack) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Haystack).")); + } + + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Needle) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Needle).")); + } + + auto FirstA = Ranges::Begin(Haystack); + auto SentA = Ranges::End (Haystack); + + auto FirstB = Ranges::Begin(Needle); + auto SentB = Ranges::End (Needle); + + while (true) + { + auto IterA = FirstA; + auto IterB = FirstB; + + while (true) + { + if (IterB == SentB) return true; + if (IterA == SentA) return false; + + if (!Invoke(Predicate, Invoke(HaystackProjection, *IterA), Invoke(NeedleProjection, *IterB))) break; + + ++IterA; + ++IterB; + } + + ++FirstA; + } +} + +/** + * Checks if the range contains the given subrange. + * + * @param HaystackFirst - The iterator of elements to examine, aka the haystack. + * @param HaystackLast - The sentinel of elements to examine, aka the haystack. + * @param NeedleFirst - The iterator of elements to search for, aka the needle. + * @param NeedleLast - The sentinel of elements to search for, aka the needle. + * @param Predicate - The equivalence relation predicate between the projected elements. + * @param HaystackProjection - The projection to apply to the haystack's elements before checking. + * @param NeedleProjection - The projection to apply to the needle's elements before checking. + * + * @return true if the haystack contains the needle, false otherwise. + */ +template S1, CForwardIterator I2, CSentinelFor S2, + CRegularInvocable> Proj1 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CRegularInvocable> Proj2 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>, TInvokeResult>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> +NODISCARD FORCEINLINE constexpr bool Contains(I1 HaystackFirst, S1 HaystackLast, I2 NeedleFirst, S2 NeedleLast, + Pred Predicate = { }, Proj1 HaystackProjection = { }, Proj2 NeedleProjection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(HaystackFirst - HaystackLast <= 0, TEXT("Illegal range iterator. Please check HaystackFirst <= HaystackLast.")); + } + + if constexpr (CSizedSentinelFor) + { + checkf(NeedleFirst - NeedleLast <= 0, TEXT("Illegal range iterator. Please check NeedleFirst <= NeedleLast.")); + } + + return Algorithms::Contains( + Ranges::View(MoveTemp(HaystackFirst), HaystackLast), + Ranges::View(MoveTemp(NeedleFirst), NeedleLast), + Ref(Predicate), Ref(HaystackProjection), Ref(NeedleProjection)); +} + +/** + * Finds the first element in the range that equals the given value. + * + * @param Range - The range to check. + * @param Value - The value to check. + * @param Predicate - The equivalence relation predicate between the projected elements and the value. + * @param Projection - The projection to apply to the elements before checking. + * + * @return The iterator to the first element that equals the value, or the end iterator if not found. + */ +template > Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CReferenceable T = TRemoveCVRef>>, + CEquivalenceRelation>, const T&> Pred = + TConditional>, const T&>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> + requires (CBorrowedRange) +NODISCARD constexpr TRangeIterator Find(R&& Range, const T& Value, Pred Predicate = { }, Proj Projection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range).")); + } + + auto Iter = Ranges::Begin(Range); + auto Sent = Ranges::End (Range); + + for (; Iter != Sent; ++Iter) + { + if (Invoke(Predicate, Invoke(Projection, *Iter), Value)) + { + return Iter; + } + } + + return Iter; +} + +/** + * Finds the first element in the range that equals the given value. + * + * @param First - The iterator of the range. + * @param Last - The sentinel of the range. + * @param Value - The value to check. + * @param Predicate - The equivalence relation predicate between the projected elements and the value. + * @param Projection - The projection to apply to the elements before checking. + * + * @return The iterator to the first element that equals the value, or the end iterator if not found. + */ +template S, + CRegularInvocable> Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CReferenceable T = TRemoveCVRef>>, + CEquivalenceRelation>, const T&> Pred = + TConditional>, const T&>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> +NODISCARD FORCEINLINE constexpr I Find(I First, S Last, const T& Value, Pred Predicate = { }, Proj Projection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last.")); + } + + return Algorithms::Find(Ranges::View(MoveTemp(First), Last), Value, Ref(Predicate), Ref(Projection)); +} + +/** + * Finds the first subrange in the range that equals the given subrange. + * + * @param Haystack - The range of elements to examine, aka the haystack. + * @param Needle - The range of elements to search for, aka the needle. + * @param Predicate - The equivalence relation predicate between the projected elements. + * @param HaystackProjection - The projection to apply to the haystack's elements before checking. + * @param NeedleProjection - The projection to apply to the needle's elements before checking. + * + * @return The subrange to the first subrange that equals the value, or the empty subrange if not found. + */ +template > Proj1 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CRegularInvocable> Proj2 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>, TInvokeResult>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> + requires (CBorrowedRange) +NODISCARD constexpr Ranges::TRangeView> Find(R1&& Haystack, R2&& Needle, + Pred Predicate = { }, Proj1 HaystackProjection = { }, Proj2 NeedleProjection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Haystack) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Haystack).")); + } + + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Needle) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Needle).")); + } + + auto FirstA = Ranges::Begin(Haystack); + auto SentA = Ranges::End (Haystack); + + auto FirstB = Ranges::Begin(Needle); + auto SentB = Ranges::End (Needle); + + while (true) + { + auto IterA = FirstA; + auto IterB = FirstB; + + while (true) + { + if (IterB == SentB) return Ranges::View(FirstA, IterA); + if (IterA == SentA) return Ranges::View(IterA, IterA); + + if (!Invoke(Predicate, Invoke(HaystackProjection, *IterA), Invoke(NeedleProjection, *IterB))) break; + + ++IterA; + ++IterB; + } + + ++FirstA; + } +} + +/** + * Finds the first subrange in the range that equals the given subrange. + * + * @param HaystackFirst - The iterator of elements to examine, aka the haystack. + * @param HaystackLast - The sentinel of elements to examine, aka the haystack. + * @param NeedleFirst - The iterator of elements to search for, aka the needle. + * @param NeedleLast - The sentinel of elements to search for, aka the needle. + * @param Predicate - The equivalence relation predicate between the projected elements. + * @param HaystackProjection - The projection to apply to the haystack's elements before checking. + * @param NeedleProjection - The projection to apply to the needle's elements before checking. + * + * @return The subrange to the first subrange that equals the value, or the empty subrange if not found. + */ +template S1, CForwardIterator I2, CSentinelFor S2, + CRegularInvocable> Proj1 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CRegularInvocable> Proj2 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>, TInvokeResult>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> +NODISCARD FORCEINLINE constexpr Ranges::TRangeView Find(I1 HaystackFirst, S1 HaystackLast, I2 NeedleFirst, S2 NeedleLast, + Pred Predicate = { }, Proj1 HaystackProjection = { }, Proj2 NeedleProjection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(HaystackFirst - HaystackLast <= 0, TEXT("Illegal range iterator. Please check HaystackFirst <= HaystackLast.")); + } + + if constexpr (CSizedSentinelFor) + { + checkf(NeedleFirst - NeedleLast <= 0, TEXT("Illegal range iterator. Please check NeedleFirst <= NeedleLast.")); + } + + return Algorithms::Find( + Ranges::View(MoveTemp(HaystackFirst), HaystackLast), + Ranges::View(MoveTemp(NeedleFirst), NeedleLast), + Ref(Predicate), Ref(HaystackProjection), Ref(NeedleProjection)); +} + +/** + * Finds the first element in the range that satisfies the predicate. + * + * @param Range - The range to check. + * @param Predicate - The unary predicate to satisfy. + * @param Projection - The projection to apply to the elements before checking. + * + * @return The iterator to the first element that satisfies the predicate, or the end iterator if not found. + */ +template > Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CPredicate>> Pred> + requires (CBorrowedRange) +NODISCARD constexpr TRangeIterator FindIf(R&& Range, Pred Predicate, Proj Projection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range).")); + } + + auto Iter = Ranges::Begin(Range); + auto Sent = Ranges::End (Range); + + for (; Iter != Sent; ++Iter) + { + if (Invoke(Predicate, Invoke(Projection, *Iter))) + { + return Iter; + } + } + + return Iter; +} + +/** + * Finds the first element in the range that satisfies the predicate. + * + * @param First - The iterator of the range. + * @param Last - The sentinel of the range. + * @param Predicate - The unary predicate to satisfy. + * @param Projection - The projection to apply to the elements before checking. + * + * @return The iterator to the first element that satisfies the predicate, or the end iterator if not found. + */ +template S, + CRegularInvocable> Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CPredicate>> Pred> +NODISCARD FORCEINLINE constexpr I FindIf(I First, S Last, Pred Predicate, Proj Projection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last.")); + } + + return Algorithms::FindIf(Ranges::View(MoveTemp(First), Last), Ref(Predicate), Ref(Projection)); +} + +/** + * Finds the first element in the range that does not satisfy the predicate. + * + * @param Range - The range to check. + * @param Predicate - The unary predicate to satisfy. + * @param Projection - The projection to apply to the elements before checking. + * + * @return The iterator to the first element that does not satisfy the predicate, or the end iterator if not found. + */ +template > Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CPredicate>> Pred> + requires (CBorrowedRange) +NODISCARD FORCEINLINE constexpr TRangeIterator FindIfNot(R&& Range, Pred Predicate, Proj Projection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range).")); + } + + auto NotPredicate = [&Predicate](T&& A) { return !Invoke(Predicate, Forward(A)); }; + + return Algorithms::FindIf(Forward(Range), NotPredicate, Ref(Projection)); +} + +/** + * Finds the first element in the range that does not satisfy the predicate. + * + * @param First - The iterator of the range. + * @param Last - The sentinel of the range. + * @param Predicate - The unary predicate to satisfy. + * @param Projection - The projection to apply to the elements before checking. + * + * @return The iterator to the first element that does not satisfy the predicate, or the end iterator if not found. + */ +template S, + CRegularInvocable> Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CPredicate>> Pred> +NODISCARD FORCEINLINE constexpr I FindIfNot(I First, S Last, Pred Predicate, Proj Projection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last.")); + } + + return Algorithms::FindIfNot(Ranges::View(MoveTemp(First), Last), Ref(Predicate), Ref(Projection)); +} + +/** + * Finds the last element in the range that equals the given value. + * + * @param Range - The range to check. + * @param Value - The value to check. + * @param Predicate - The equivalence relation predicate between the projected elements and the value. + * @param Projection - The projection to apply to the elements before checking. + * + * @return The iterator to the last element that equals the value, or the end iterator if not found. + */ +template > Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CReferenceable T = TRemoveCVRef>>, + CEquivalenceRelation>, const T&> Pred = + TConditional>, const T&>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> + requires (CBorrowedRange) +NODISCARD constexpr TRangeIterator FindLast(R&& Range, const T& Value, Pred Predicate = { }, Proj Projection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range).")); + } + + auto Iter = Ranges::Begin(Range); + auto Sent = Ranges::End (Range); + + if constexpr (CBidirectionalRange && CCommonRange) + { + auto RIter = MakeReverseIterator(Sent); + auto RSent = MakeReverseIterator(Iter); + + for (; RIter != RSent; ++RIter) + { + if (Invoke(Predicate, Invoke(Projection, *RIter), Value)) + { + return Algorithms::Prev(RIter.GetBase()); + } + } + + return Sent; + } + + else if constexpr (CRandomAccessRange && CSizedRange) + { + const size_t Count = Ranges::Num(Range); + + auto RIter = MakeReverseIterator(Iter + Count); + auto RSent = MakeReverseIterator(Iter); + + for (; RIter != RSent; ++RIter) + { + if (Invoke(Predicate, Invoke(Projection, *RIter), Value)) + { + return Algorithms::Prev(RIter.GetBase()); + } + } + + return Iter + Count; + } + + else + { + TOptional> Result; + + for (; Iter != Sent; ++Iter) + { + if (Invoke(Predicate, Invoke(Projection, *Iter), Value)) + { + Result = Iter; + } + } + + if (!Result) return Iter; + + return *Result; + } +} + +/** + * Finds the last element in the range that equals the given value. + * + * @param First - The iterator of the range. + * @param Last - The sentinel of the range. + * @param Value - The value to check. + * @param Predicate - The equivalence relation predicate between the projected elements and the value. + * @param Projection - The projection to apply to the elements before checking. + * + * @return The iterator to the last element that equals the value, or the end iterator if not found. + */ +template S, + CRegularInvocable> Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CReferenceable T = TRemoveCVRef>>, + CEquivalenceRelation>, const T&> Pred = + TConditional>, const T&>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> +NODISCARD FORCEINLINE constexpr I FindLast(I First, S Last, const T& Value, Pred Predicate = { }, Proj Projection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last.")); + } + + return Algorithms::FindLast(Ranges::View(MoveTemp(First), Last), Value, Ref(Predicate), Ref(Projection)); +} + +/** + * Finds the last subrange in the range that equals the given subrange. + * + * @param Haystack - The range of elements to examine, aka the haystack. + * @param Needle - The range of elements to search for, aka the needle. + * @param Predicate - The equivalence relation predicate between the projected elements. + * @param HaystackProjection - The projection to apply to the haystack's elements before checking. + * @param NeedleProjection - The projection to apply to the needle's elements before checking. + * + * @return The subrange to the last subrange that equals the value, or the empty subrange if not found. + */ +template > Proj1 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CRegularInvocable> Proj2 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>, TInvokeResult>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> + requires (CBorrowedRange) +NODISCARD constexpr Ranges::TRangeView> FindLast(R1&& Haystack, R2&& Needle, + Pred Predicate = { }, Proj1 HaystackProjection = { }, Proj2 NeedleProjection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Haystack) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Haystack).")); + } + + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Needle) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Needle).")); + } + + auto FirstA = Ranges::Begin(Haystack); + auto SentA = Ranges::End (Haystack); + + auto FirstB = Ranges::Begin(Needle); + auto SentB = Ranges::End (Needle); + + if (FirstB == SentB) + { + auto LastA = Algorithms::Next(FirstA, SentA); + + return Ranges::View(LastA, LastA); + } + + if constexpr (CBidirectionalRange && CCommonRange) + { + const ptrdiff NeedleCount = Algorithms::Distance(FirstB, SentB); + + auto Iter = SentA; + + if (Algorithms::Advance(Iter, -NeedleCount, FirstA) != 0) return Ranges::View(SentA, SentA); + + for (; Iter != FirstA; --Iter) + { + auto IterA = Algorithms::Prev(Iter); + auto IterB = FirstB; + + while (true) + { + if (IterB == SentB) return Ranges::View(Algorithms::Prev(Iter), IterA); + + if (!Invoke(Predicate, Invoke(HaystackProjection, *IterA), Invoke(NeedleProjection, *IterB))) break; + + ++IterA; + ++IterB; + } + } + + return Ranges::View(SentA, SentA); + } + + else + { + auto Result = Algorithms::Find(FirstA, SentA, FirstB, SentB, + Ref(Predicate), Ref(HaystackProjection), Ref(NeedleProjection)); + + if (Result.IsEmpty()) return Result; + + while (true) + { + auto Next = Algorithms::Find(Algorithms::Next(Result.Begin()), SentA, FirstB, SentB, + Ref(Predicate), Ref(HaystackProjection), Ref(NeedleProjection)); + + if (Next.IsEmpty()) return Result; + + Result = Next; + } + } +} + +/** + * Finds the last subrange in the range that equals the given subrange. + * + * @param HaystackFirst - The iterator of elements to examine, aka the haystack. + * @param HaystackLast - The sentinel of elements to examine, aka the haystack. + * @param NeedleFirst - The iterator of elements to search for, aka the needle. + * @param NeedleLast - The sentinel of elements to search for, aka the needle. + * @param Predicate - The equivalence relation predicate between the projected elements. + * @param HaystackProjection - The projection to apply to the haystack's elements before checking. + * @param NeedleProjection - The projection to apply to the needle's elements before checking. + * + * @return The subrange to the last subrange that equals the value, or the empty subrange if not found. + */ +template S1, CForwardIterator I2, CSentinelFor S2, + CRegularInvocable> Proj1 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CRegularInvocable> Proj2 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>, TInvokeResult>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> +NODISCARD FORCEINLINE constexpr Ranges::TRangeView FindLast(I1 HaystackFirst, S1 HaystackLast, I2 NeedleFirst, S2 NeedleLast, + Pred Predicate = { }, Proj1 HaystackProjection = { }, Proj2 NeedleProjection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(HaystackFirst - HaystackLast <= 0, TEXT("Illegal range iterator. Please check HaystackFirst <= HaystackLast.")); + } + + if constexpr (CSizedSentinelFor) + { + checkf(NeedleFirst - NeedleLast <= 0, TEXT("Illegal range iterator. Please check NeedleFirst <= NeedleLast.")); + } + + return Algorithms::FindLast( + Ranges::View(MoveTemp(HaystackFirst), HaystackLast), + Ranges::View(MoveTemp(NeedleFirst), NeedleLast), + Ref(Predicate), Ref(HaystackProjection), Ref(NeedleProjection)); +} + +/** + * Finds the last element in the range that satisfies the predicate. + * + * @param Range - The range to check. + * @param Predicate - The unary predicate to satisfy. + * @param Projection - The projection to apply to the elements before checking. + * + * @return The iterator to the last element that satisfies the predicate, or the end iterator if not found. + */ +template > Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CPredicate>> Pred> + requires (CBorrowedRange) +NODISCARD constexpr TRangeIterator FindLastIf(R&& Range, Pred Predicate, Proj Projection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range).")); + } + + auto Iter = Ranges::Begin(Range); + auto Sent = Ranges::End (Range); + + if constexpr (CBidirectionalRange && CCommonRange) + { + auto RIter = MakeReverseIterator(Sent); + auto RSent = MakeReverseIterator(Iter); + + for (; RIter != RSent; ++RIter) + { + if (Invoke(Predicate, Invoke(Projection, *RIter))) + { + return Algorithms::Prev(RIter.GetBase()); + } + } + + return Sent; + } + + else if constexpr (CRandomAccessRange && CSizedRange) + { + const size_t Count = Ranges::Num(Range); + + auto RIter = MakeReverseIterator(Iter + Count); + auto RSent = MakeReverseIterator(Iter); + + for (; RIter != RSent; ++RIter) + { + if (Invoke(Predicate, Invoke(Projection, *RIter))) + { + return Algorithms::Prev(RIter.GetBase()); + } + } + + return Iter + Count; + } + + else + { + TOptional> Result; + + for (; Iter != Sent; ++Iter) + { + if (Invoke(Predicate, Invoke(Projection, *Iter))) + { + Result = Iter; + } + } + + if (!Result) return Iter; + + return *Result; + } +} + +/** + * Finds the last element in the range that satisfies the predicate. + * + * @param First - The iterator of the range. + * @param Last - The sentinel of the range. + * @param Predicate - The unary predicate to satisfy. + * @param Projection - The projection to apply to the elements before checking. + * + * @return The iterator to the last element that satisfies the predicate, or the end iterator if not found. + */ +template S, + CRegularInvocable> Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CPredicate>> Pred> +NODISCARD FORCEINLINE constexpr I FindLastIf(I First, S Last, Pred Predicate, Proj Projection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last.")); + } + + return Algorithms::FindLastIf(Ranges::View(MoveTemp(First), Last), Ref(Predicate), Ref(Projection)); +} + +/** + * Finds the last element in the range that does not satisfy the predicate. + * + * @param Range - The range to check. + * @param Predicate - The unary predicate to satisfy. + * @param Projection - The projection to apply to the elements before checking. + * + * @return The iterator to the last element that does not satisfy the predicate, or the end iterator if not found. + */ +template > Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CPredicate>> Pred> + requires (CBorrowedRange) +NODISCARD constexpr TRangeIterator FindLastIfNot(R&& Range, Pred Predicate, Proj Projection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range).")); + } + + auto NotPredicate = [&Predicate](T&& A) { return !Invoke(Predicate, Forward(A)); }; + + return Algorithms::FindLastIf(Forward(Range), NotPredicate, Ref(Projection)); +} + +/** + * Finds the last element in the range that does not satisfy the predicate. + * + * @param First - The iterator of the range. + * @param Last - The sentinel of the range. + * @param Predicate - The unary predicate to satisfy. + * @param Projection - The projection to apply to the elements before checking. + * + * @return The iterator to the last element that does not satisfy the predicate, or the end iterator if not found. + */ +template S, + CRegularInvocable> Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CPredicate>> Pred> +NODISCARD FORCEINLINE constexpr I FindLastIfNot(I First, S Last, Pred Predicate, Proj Projection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last.")); + } + + return Algorithms::FindLastIfNot(Ranges::View(MoveTemp(First), Last), Ref(Predicate), Ref(Projection)); +} + +/** + * Finds the first element in the range that also contained in another range. + * + * @param Haystack - The range of elements to examine, aka the haystack. + * @param Needle - The range of elements to search for, aka the needle. + * @param Predicate - The equivalence relation predicate between the projected elements. + * @param HaystackProjection - The projection to apply to the haystack's elements before checking. + * @param NeedleProjection - The projection to apply to the needle's elements before checking. + * + * @return The iterator to the first element that also contained in the needle, or the end iterator if not found. + */ +template > Proj1 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CRegularInvocable> Proj2 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>, TInvokeResult>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> + requires (CBorrowedRange) +NODISCARD FORCEINLINE constexpr TRangeIterator FindFirstOf(R1&& Haystack, R2&& Needle, + Pred Predicate = { }, Proj1 HaystackProjection = { }, Proj2 NeedleProjection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Haystack) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Haystack).")); + } + + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Needle) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Needle).")); + } + + auto ContainsInNeedle = [&](LHS&& A) + { + auto ForwardPredicate = [&](RHS&& B) + { + return Invoke(Predicate, Forward(A), Forward(B)); + }; + + return Algorithms::FindIf(Needle, ForwardPredicate, Ref(NeedleProjection)) != Ranges::End(Needle); + }; + + return Algorithms::FindIf(Forward(Haystack), ContainsInNeedle, Ref(HaystackProjection)); +} + +/** + * Finds the first element in the range that also contained in another range. + * + * @param HaystackFirst - The iterator of elements to examine, aka the haystack. + * @param HaystackLast - The sentinel of elements to examine, aka the haystack. + * @param NeedleFirst - The iterator of elements to search for, aka the needle. + * @param NeedleLast - The sentinel of elements to search for, aka the needle. + * @param Predicate - The equivalence relation predicate between the projected elements. + * @param HaystackProjection - The projection to apply to the haystack's elements before checking. + * @param NeedleProjection - The projection to apply to the needle's elements before checking. + * + * @return The iterator to the first element that also contained in the needle, or the end iterator if not found. + */ +template S1, CForwardIterator I2, CSentinelFor S2, + CRegularInvocable> Proj1 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CRegularInvocable> Proj2 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>, TInvokeResult>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> +NODISCARD FORCEINLINE constexpr I1 FindFirstOf(I1 HaystackFirst, S1 HaystackLast, I2 NeedleFirst, S2 NeedleLast, + Pred Predicate = { }, Proj1 HaystackProjection = { }, Proj2 NeedleProjection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(HaystackFirst - HaystackLast <= 0, TEXT("Illegal range iterator. Please check HaystackFirst <= HaystackLast.")); + } + + if constexpr (CSizedSentinelFor) + { + checkf(NeedleFirst - NeedleLast <= 0, TEXT("Illegal range iterator. Please check NeedleFirst <= NeedleLast.")); + } + + return Algorithms::FindFirstOf( + Ranges::View(MoveTemp(HaystackFirst), HaystackLast), + Ranges::View(MoveTemp(NeedleFirst), NeedleLast), + Ref(Predicate), Ref(HaystackProjection), Ref(NeedleProjection)); +} + +/** + * Finds the last element in the range that also contained in another range. + * + * @param Haystack - The range of elements to examine, aka the haystack. + * @param Needle - The range of elements to search for, aka the needle. + * @param Predicate - The equivalence relation predicate between the projected elements. + * @param HaystackProjection - The projection to apply to the haystack's elements before checking. + * @param NeedleProjection - The projection to apply to the needle's elements before checking. + * + * @return The iterator to the last element that also contained in the needle, or the end iterator if not found. + */ +template > Proj1 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CRegularInvocable> Proj2 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>, TInvokeResult>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> + requires (CBorrowedRange) +NODISCARD FORCEINLINE constexpr TRangeIterator FindLastOf(R1&& Haystack, R2&& Needle, + Pred Predicate = { }, Proj1 HaystackProjection = { }, Proj2 NeedleProjection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Haystack) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Haystack).")); + } + + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Needle) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Needle).")); + } + + auto ContainsInNeedle = [&](LHS&& A) + { + auto ForwardPredicate = [&](RHS&& B) + { + return Invoke(Predicate, Forward(A), Forward(B)); + }; + + return Algorithms::FindIf(Needle, ForwardPredicate, Ref(NeedleProjection)) != Ranges::End(Needle); + }; + + return Algorithms::FindLastIf(Forward(Haystack), ContainsInNeedle, Ref(HaystackProjection)); +} + +/** + * Finds the last element in the range that also contained in another range. + * + * @param HaystackFirst - The iterator of elements to examine, aka the haystack. + * @param HaystackLast - The sentinel of elements to examine, aka the haystack. + * @param NeedleFirst - The iterator of elements to search for, aka the needle. + * @param NeedleLast - The sentinel of elements to search for, aka the needle. + * @param Predicate - The equivalence relation predicate between the projected elements. + * @param HaystackProjection - The projection to apply to the haystack's elements before checking. + * @param NeedleProjection - The projection to apply to the needle's elements before checking. + * + * @return The iterator to the last element that also contained in the needle, or the end iterator if not found. + */ +template S1, CForwardIterator I2, CSentinelFor S2, + CRegularInvocable> Proj1 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CRegularInvocable> Proj2 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>, TInvokeResult>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> +NODISCARD FORCEINLINE constexpr I1 FindLastOf(I1 HaystackFirst, S1 HaystackLast, I2 NeedleFirst, S2 NeedleLast, + Pred Predicate = { }, Proj1 HaystackProjection = { }, Proj2 NeedleProjection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(HaystackFirst - HaystackLast <= 0, TEXT("Illegal range iterator. Please check HaystackFirst <= HaystackLast.")); + } + + if constexpr (CSizedSentinelFor) + { + checkf(NeedleFirst - NeedleLast <= 0, TEXT("Illegal range iterator. Please check NeedleFirst <= NeedleLast.")); + } + + return Algorithms::FindLastOf( + Ranges::View(MoveTemp(HaystackFirst), HaystackLast), + Ranges::View(MoveTemp(NeedleFirst), NeedleLast), + Ref(Predicate), Ref(HaystackProjection), Ref(NeedleProjection)); +} + +/** + * Finds the first pair of equal adjacent elements in the range. + * + * @param Range - The range to check. + * @param Predicate - The equivalence relation predicate between the projected elements and the value. + * @param Projection - The projection to apply to the elements before checking. + * + * @return The iterator to the first of the pair of equal adjacent elements, or the end iterator if not found. + */ +template > Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> + requires (CBorrowedRange) +NODISCARD constexpr TRangeIterator FindAdjacent(R&& Range, Pred Predicate = { }, Proj Projection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range).")); + } + + auto Iter = Ranges::Begin(Range); + auto Sent = Ranges::End (Range); + + if (Iter == Sent) return Iter; + + auto Next = Algorithms::Next(Iter); + + for (; Next != Sent; ++Iter, ++Next) + { + if (Invoke(Predicate, Invoke(Projection, *Iter), Invoke(Projection, *Next))) + { + return Iter; + } + } + + return Next; +} + +/** + * Finds the first pair of equal adjacent elements in the range. + * + * @param First - The iterator of the range. + * @param Last - The sentinel of the range. + * @param Predicate - The equivalence relation predicate between the projected elements and the value. + * @param Projection - The projection to apply to the elements before checking. + * + * @return The iterator to the first of the pair of equal adjacent elements, or the end iterator if not found. + */ +template S, + CRegularInvocable> Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> +NODISCARD FORCEINLINE constexpr I FindAdjacent(I First, S Last, Pred Predicate = { }, Proj Projection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last.")); + } + + return Algorithms::FindAdjacent(Ranges::View(MoveTemp(First), Last), Ref(Predicate), Ref(Projection)); +} + +/** + * Counts the number of elements in the range that equals the given value. + * + * @param Range - The range of elements to examine. + * @param Value - The value to search for. + * @param Predicate - The equivalence relation predicate between the projected elements and the value. + * @param Projection - The projection to apply to the elements before checking. + * + * @return The number of elements that equals the value. + */ +template > Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CReferenceable T = TRemoveCVRef>>, + CEquivalenceRelation>, T> Pred = + TConditional>, const T&>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> +NODISCARD constexpr size_t Count(R&& Range, const T& Value, Pred Predicate = { }, Proj Projection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range).")); + } + + auto Iter = Ranges::Begin(Range); + auto Sent = Ranges::End (Range); + + size_t Result = 0; + + for (; Iter != Sent; ++Iter) + { + if (Invoke(Predicate, Invoke(Projection, *Iter), Value)) + { + ++Result; + } + } + + return Result; +} + +/** + * Counts the number of elements in the range that equals the given value. + * + * @param First - The iterator of the range. + * @param Last - The sentinel of the range. + * @param Value - The value to search for. + * @param Predicate - The equivalence relation predicate between the projected elements and the value. + * @param Projection - The projection to apply to the elements before checking. + * + * @return The number of elements that equals the value. + */ +template S, + CRegularInvocable> Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CReferenceable T = TRemoveCVRef>>, + CEquivalenceRelation>, T> Pred = + TConditional>, const T&>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> +NODISCARD FORCEINLINE constexpr size_t Count(I First, S Last, const T& Value, Pred Predicate = { }, Proj Projection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last.")); + } + + return Algorithms::Count(Ranges::View(MoveTemp(First), Last), Value, Ref(Predicate), Ref(Projection)); +} + +/** + * Counts the number of elements in the range that satisfies the predicate. + * + * @param Range - The range of elements to examine. + * @param Predicate - The unary predicate to satisfy. + * @param Projection - The projection to apply to the elements before checking. + * + * @return The number of elements that satisfies the predicate. + */ +template > Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CPredicate>> Pred> +NODISCARD constexpr size_t CountIf(R&& Range, Pred Predicate, Proj Projection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range).")); + } + + auto Iter = Ranges::Begin(Range); + auto Sent = Ranges::End (Range); + + size_t Result = 0; + + for (; Iter != Sent; ++Iter) + { + if (Invoke(Predicate, Invoke(Projection, *Iter))) + { + ++Result; + } + } + + return Result; +} + +/** + * Counts the number of elements in the range that satisfies the predicate. + * + * @param First - The iterator of the range. + * @param Last - The sentinel of the range. + * @param Predicate - The unary predicate to satisfy. + * @param Projection - The projection to apply to the elements before checking. + * + * @return The number of elements that satisfies the predicate. + */ +template S, + CRegularInvocable> Proj = + decltype([](T&& A) -> T&& { return Forward(A); }), + CPredicate>> Pred> +NODISCARD FORCEINLINE constexpr size_t CountIf(I First, S Last, Pred Predicate, Proj Projection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last.")); + } + + return Algorithms::CountIf(Ranges::View(MoveTemp(First), Last), Ref(Predicate), Ref(Projection)); +} + +/** + * Finds the first mismatch between two ranges. + * + * @param LHS - The left hand side range of the elements to compare. + * @param RHS - The right hand side range of the elements to compare. + * @param Predicate - The equivalence relation predicate between the projected elements. + * @param LHSProjection - The projection to apply to the left hand side elements before checking. + * @param RHSProjection - The projection to apply to the right hand side elements before checking. + * + * @return The pair of iterators to the first mismatched elements, or the pair of end iterators if not found. + */ +template > Proj1 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CRegularInvocable> Proj2 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>, TInvokeResult>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> + requires (CBorrowedRange && CBorrowedRange) +NODISCARD constexpr TTuple, TRangeIterator> Mismatch(R1&& LHS, R2&& RHS, + Pred Predicate = { }, Proj1 LHSProjection = { }, Proj2 RHSProjection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(LHS) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(LHS).")); + } + + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(RHS) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(RHS).")); + } + + auto IterA = Ranges::Begin(LHS); + auto SentA = Ranges::End (LHS); + + auto IterB = Ranges::Begin(RHS); + auto SentB = Ranges::End (RHS); + + while (IterA != SentA && IterB != SentB) + { + if (!Invoke(Predicate, Invoke(LHSProjection, *IterA), Invoke(RHSProjection, *IterB))) + { + break; + } + + ++IterA; + ++IterB; + } + + return { IterA, IterB }; +} + +/** + * Finds the first mismatch between two ranges. + * + * @param LHSFirst - The iterator of the left hand side range. + * @param LHSLast - The sentinel of the left hand side range. + * @param RHSFirst - The iterator of the right hand side range. + * @param RHSLast - The sentinel of the right hand side range. + * @param Predicate - The equivalence relation predicate between the projected elements. + * @param LHSProjection - The projection to apply to the left hand side elements before checking. + * @param RHSProjection - The projection to apply to the right hand side elements before checking. + * + * @return The pair of iterators to the first mismatched elements, or the pair of end iterators if not found. + */ +template S1, CInputIterator I2, CSentinelFor S2, + CRegularInvocable> Proj1 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CRegularInvocable> Proj2 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>, TInvokeResult>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> +NODISCARD FORCEINLINE constexpr TTuple Mismatch(I1 LHSFirst, S1 LHSLast, I2 RHSFirst, S2 RHSLast, + Pred Predicate = { }, Proj1 LHSProjection = { }, Proj2 RHSProjection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(LHSFirst - LHSLast <= 0, TEXT("Illegal range iterator. Please check LHSFirst <= LHSLast.")); + } + + if constexpr (CSizedSentinelFor) + { + checkf(RHSFirst - RHSLast <= 0, TEXT("Illegal range iterator. Please check RHSFirst <= RHSLast.")); + } + + return Algorithms::Mismatch( + Ranges::View(MoveTemp(LHSFirst), LHSLast), + Ranges::View(MoveTemp(RHSFirst), RHSLast), + Ref(Predicate), Ref(LHSProjection), Ref(RHSProjection)); +} + +/** + * Checks if two ranges are equal. + * + * @param LHS - The left hand side range of the elements to compare. + * @param RHS - The right hand side range of the elements to compare. + * @param Predicate - The equivalence relation predicate between the projected elements. + * @param LHSProjection - The projection to apply to the left hand side elements before checking. + * @param RHSProjection - The projection to apply to the right hand side elements before checking. + * + * @return true if the ranges are equal, false otherwise. + */ +template > Proj1 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CRegularInvocable> Proj2 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>, TInvokeResult>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> +NODISCARD FORCEINLINE constexpr bool Equal(R1&& LHS, R2&& RHS, Pred Predicate = { }, Proj1 LHSProjection = { }, Proj2 RHSProjection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(LHS) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(LHS).")); + } + + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(RHS) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(RHS).")); + } + + if constexpr (CSizedRange && CSizedRange) + { + if (Ranges::Num(LHS) != Ranges::Num(RHS)) + { + return false; + } + } + + auto FirstA = Ranges::Begin(LHS); + auto SentA = Ranges::End (LHS); + + auto FirstB = Ranges::Begin(RHS); + auto SentB = Ranges::End (RHS); + + auto [IterA, IterB] = Algorithms::Mismatch( + MoveTemp(FirstA), SentA, MoveTemp(FirstB), SentB, + Ref(Predicate), Ref(LHSProjection), Ref(RHSProjection)); + + return IterA == SentA && IterB == SentB; +} + +/** + * Checks if two ranges are equal. + * + * @param LHSFirst - The iterator of the left hand side range. + * @param LHSLast - The sentinel of the left hand side range. + * @param RHSFirst - The iterator of the right hand side range. + * @param RHSLast - The sentinel of the right hand side range. + * @param Predicate - The equivalence relation predicate between the projected elements. + * @param LHSProjection - The projection to apply to the left hand side elements before checking. + * @param RHSProjection - The projection to apply to the right hand side elements before checking. + * + * @return true if the ranges are equal, false otherwise. + */ +template S1, CInputIterator I2, CSentinelFor S2, + CRegularInvocable> Proj1 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CRegularInvocable> Proj2 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>, TInvokeResult>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> +NODISCARD FORCEINLINE constexpr bool Equal(I1 LHSFirst, S1 LHSLast, I2 RHSFirst, S2 RHSLast, + Pred Predicate = { }, Proj1 LHSProjection = { }, Proj2 RHSProjection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(LHSFirst - LHSLast <= 0, TEXT("Illegal range iterator. Please check LHSFirst <= LHSLast.")); + } + + if constexpr (CSizedSentinelFor) + { + checkf(RHSFirst - RHSLast <= 0, TEXT("Illegal range iterator. Please check RHSFirst <= RHSLast.")); + } + + return Algorithms::Equal( + Ranges::View(MoveTemp(LHSFirst), LHSLast), + Ranges::View(MoveTemp(RHSFirst), RHSLast), + Ref(Predicate), Ref(LHSProjection), Ref(RHSProjection)); +} + +/** + * Checks if the range starts with the given prefix. + * + * @param Range - The range of elements to examine. + * @param Prefix - The range of elements to be used as the prefix. + * @param Predicate - The equivalence relation predicate between the projected elements. + * @param Projection - The projection to apply to the elements before checking. + * @param PrefixProjection - The projection to apply to the prefix elements before checking. + * + * @return true if the range starts with the prefix, false otherwise. + */ +template > Proj1 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CRegularInvocable> Proj2 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>, TInvokeResult>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> +NODISCARD FORCEINLINE constexpr bool StartsWith(R1&& Range, R2&& Prefix, Pred Predicate = { }, Proj1 Projection = { }, Proj2 PrefixProjection = { }) +{ + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Range) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range).")); + } + + if constexpr (CSizedRange) + { + checkf(Algorithms::Distance(Prefix) >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Prefix).")); + } + + if constexpr (CSizedRange && CSizedRange) + { + if (Ranges::Num(Range) < Ranges::Num(Prefix)) + { + return false; + } + } + + auto FirstA = Ranges::Begin(Range); + auto SentA = Ranges::End (Range); + + auto FirstB = Ranges::Begin(Prefix); + auto SentB = Ranges::End (Prefix); + + auto [IterA, IterB] = Algorithms::Mismatch( + MoveTemp(FirstA), SentA, MoveTemp(FirstB), SentB, + Ref(Predicate), Ref(Projection), Ref(PrefixProjection)); + + return IterB == SentB; +} + +/** + * Checks if the range starts with the given prefix. + * + * @param First - The iterator of the range. + * @param Last - The sentinel of the range. + * @param PrefixFirst - The iterator of the prefix range. + * @param PrefixLast - The sentinel of the prefix range. + * @param Predicate - The equivalence relation predicate between the projected elements. + * @param Projection - The projection to apply to the elements before checking. + * @param PrefixProjection - The projection to apply to the prefix elements before checking. + * + * @return true if the range starts with the prefix, false otherwise. + */ +template S1, CInputIterator I2, CSentinelFor S2, + CRegularInvocable> Proj1 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CRegularInvocable> Proj2 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>, TInvokeResult>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> +NODISCARD FORCEINLINE constexpr bool StartsWith(I1 First, S1 Last, I2 PrefixFirst, S2 PrefixLast, + Pred Predicate = { }, Proj1 Projection = { }, Proj2 PrefixProjection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last.")); + } + + if constexpr (CSizedSentinelFor) + { + checkf(PrefixFirst - PrefixLast <= 0, TEXT("Illegal range iterator. Please check PrefixFirst <= PrefixLast.")); + } + + return Algorithms::StartsWith( + Ranges::View(MoveTemp( First), Last), + Ranges::View(MoveTemp(PrefixFirst), PrefixLast), + Ref(Predicate), Ref(Projection), Ref(PrefixProjection)); +} + +/** + * Checks if the range ends with the given suffix. + * + * @param Range - The range of elements to examine. + * @param Suffix - The range of elements to be used as the suffix. + * @param Predicate - The equivalence relation predicate between the projected elements. + * @param Projection - The projection to apply to the elements before checking. + * @param SuffixProjection - The projection to apply to the suffix elements before checking. + * + * @return true if the range ends with the suffix, false otherwise. + */ +template > Proj1 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CRegularInvocable> Proj2 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>, TInvokeResult>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> + requires ((CForwardRange || CSizedRange) && (CForwardRange || CSizedRange)) +NODISCARD FORCEINLINE constexpr bool EndsWith(R1&& Range, R2&& Suffix, Pred Predicate = { }, Proj1 Projection = { }, Proj2 SuffixProjection = { }) +{ + const ptrdiff CountA = Algorithms::Distance(Range); + const ptrdiff CountB = Algorithms::Distance(Suffix); + + checkf(CountA >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Range).")); + checkf(CountB >= 0, TEXT("Illegal range. Please check Algorithms::Distance(Suffix).")); + + if (CountA < CountB) return false; + + auto Iter = Ranges::Begin(Range); + + Algorithms::Advance(Iter, CountA - CountB); + + return Algorithms::Equal( + MoveTemp(Iter), Ranges::End(Range), Ranges::Begin(Suffix), Ranges::End(Suffix), + Ref(Predicate), Ref(Projection), Ref(SuffixProjection)); +} + +/** + * Checks if the range ends with the given suffix. + * + * @param First - The iterator of the range. + * @param Last - The sentinel of the range. + * @param SuffixFirst - The iterator of the suffix range. + * @param SuffixLast - The sentinel of the suffix range. + * @param Predicate - The equivalence relation predicate between the projected elements. + * @param Projection - The projection to apply to the elements before checking. + * @param SuffixProjection - The projection to apply to the suffix elements before checking. + * + * @return true if the range ends with the suffix, false otherwise. + */ +template S1, CInputIterator I2, CSentinelFor S2, + CRegularInvocable> Proj1 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CRegularInvocable> Proj2 = + decltype([](T&& A) -> T&& { return Forward(A); }), + CEquivalenceRelation>, TInvokeResult>> Pred = + TConditional>, TInvokeResult>>, + decltype([](const LHS& A, const RHS& B) { return A == B; }), void>> + requires ((CForwardIterator || CSizedSentinelFor) && (CForwardIterator || CSizedSentinelFor)) +NODISCARD FORCEINLINE constexpr bool EndsWith(I1 First, S1 Last, I2 SuffixFirst, S2 SuffixLast, + Pred Predicate = { }, Proj1 Projection = { }, Proj2 SuffixProjection = { }) +{ + if constexpr (CSizedSentinelFor) + { + checkf(First - Last <= 0, TEXT("Illegal range iterator. Please check First <= Last.")); + } + + if constexpr (CSizedSentinelFor) + { + checkf(SuffixFirst - SuffixLast <= 0, TEXT("Illegal range iterator. Please check SuffixFirst <= SuffixLast.")); + } + + return Algorithms::EndsWith( + Ranges::View(MoveTemp( First), Last), + Ranges::View(MoveTemp(SuffixFirst), SuffixLast), + Ref(Predicate), Ref(Projection), Ref(SuffixProjection)); +} + +NAMESPACE_END(Algorithms) + +NAMESPACE_MODULE_END(Utility) +NAMESPACE_MODULE_END(Redcraft) +NAMESPACE_REDCRAFT_END