From aec0d6cead3021963a027e05f3df306cda45462d Mon Sep 17 00:00:00 2001 From: manavggupta Date: Sat, 21 Feb 2026 20:57:49 +0530 Subject: [PATCH 1/5] Add length property to cartesianProduct for finite forward ranges Fixes issue 9871 --- std/algorithm/setops.d | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/std/algorithm/setops.d b/std/algorithm/setops.d index 10873a7cabc..e10d5d43db8 100644 --- a/std/algorithm/setops.d +++ b/std/algorithm/setops.d @@ -365,6 +365,8 @@ if (ranges.length >= 2 && // For infinite ranges or non-forward ranges, we fall back to the old // implementation which expands an exponential number of templates. import std.typecons : tuple; + import std.algorithm : allSatisfy; + import std.range.primitives : hasLength; static struct Result { @@ -413,6 +415,18 @@ if (ranges.length >= 2 && } return copy; } + static if (allSatisfy!(hasLength, RR)) + { + @property size_t length() const + { + size_t result = 1; + foreach (r; ranges) + { + result *= r.length; + } + return result; + } + } } static assert(isForwardRange!Result, Result.stringof ~ " must be a forward" ~ " range"); From 0d9d772d43e086929bc423231abbf6bc9b31bd70 Mon Sep 17 00:00:00 2001 From: manavggupta Date: Sat, 21 Feb 2026 21:14:06 +0530 Subject: [PATCH 2/5] Add unit tests for cartesianProduct length Fixes issue 9871 --- std/algorithm/setops.d | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/std/algorithm/setops.d b/std/algorithm/setops.d index e10d5d43db8..53ab638a5be 100644 --- a/std/algorithm/setops.d +++ b/std/algorithm/setops.d @@ -433,7 +433,19 @@ if (ranges.length >= 2 && return Result(ranges); } +@safe unittest +{ + import std.algorithm.setops : cartesianProduct; + + // 2 ranges + assert(cartesianProduct([1, 2], [3, 4]).length == 4); + // different sizes + assert(cartesianProduct([1, 2, 3], [4, 5]).length == 6); + + // 3 ranges + assert(cartesianProduct([1, 2], [3, 4], [5]).length == 4); +} // cartesian product of empty ranges should be empty // https://issues.dlang.org/show_bug.cgi?id=10693 @safe unittest From 0e9cc0ea2ccb35ddbc455ac4d52853db93d5dbc8 Mon Sep 17 00:00:00 2001 From: manavggupta Date: Sat, 21 Feb 2026 22:16:28 +0530 Subject: [PATCH 3/5] fixed the import --- std/algorithm/setops.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/algorithm/setops.d b/std/algorithm/setops.d index 53ab638a5be..f2534666be3 100644 --- a/std/algorithm/setops.d +++ b/std/algorithm/setops.d @@ -365,7 +365,7 @@ if (ranges.length >= 2 && // For infinite ranges or non-forward ranges, we fall back to the old // implementation which expands an exponential number of templates. import std.typecons : tuple; - import std.algorithm : allSatisfy; + import std.meta : allSatisfy; import std.range.primitives : hasLength; static struct Result From a3f90aa430791b12b5e409deba289777ca6d665e Mon Sep 17 00:00:00 2001 From: manavggupta Date: Sun, 22 Feb 2026 00:35:58 +0530 Subject: [PATCH 4/5] removed const --- std/algorithm/setops.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/algorithm/setops.d b/std/algorithm/setops.d index f2534666be3..025ad5bf935 100644 --- a/std/algorithm/setops.d +++ b/std/algorithm/setops.d @@ -417,7 +417,7 @@ if (ranges.length >= 2 && } static if (allSatisfy!(hasLength, RR)) { - @property size_t length() const + @property size_t length() { size_t result = 1; foreach (r; ranges) From 4506f5080a853ed880f48507eb7faf3074ba9027 Mon Sep 17 00:00:00 2001 From: manavggupta Date: Sun, 22 Feb 2026 00:54:56 +0530 Subject: [PATCH 5/5] fixed extra spacing --- std/algorithm/setops.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/algorithm/setops.d b/std/algorithm/setops.d index 025ad5bf935..f373fe74e70 100644 --- a/std/algorithm/setops.d +++ b/std/algorithm/setops.d @@ -417,7 +417,7 @@ if (ranges.length >= 2 && } static if (allSatisfy!(hasLength, RR)) { - @property size_t length() + @property size_t length() { size_t result = 1; foreach (r; ranges)