|
@@ -43,6 +43,71 @@ foreach (var m in new[] { "Max", "Min" })
|
|
|
#>
|
|
|
<#=t#> value;
|
|
|
|
|
|
+#if CSHARP8
|
|
|
+ await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
|
|
|
+ {
|
|
|
+ if (!await e.MoveNextAsync())
|
|
|
+ {
|
|
|
+ throw Error.NoElements();
|
|
|
+ }
|
|
|
+
|
|
|
+ value = e.Current;
|
|
|
+
|
|
|
+<#
|
|
|
+ if (isFloatingPoint && m == "Max")
|
|
|
+ {
|
|
|
+#>
|
|
|
+ // NaN is ordered less than all other values. We need to do explicit checks
|
|
|
+ // to ensure this, but once we've found a value that is not NaN we need no
|
|
|
+ // longer worry about it, so first loop until such a value is found (or not,
|
|
|
+ // as the case may be).
|
|
|
+
|
|
|
+ while (<#=t#>.IsNaN(value))
|
|
|
+ {
|
|
|
+ if (!await e.MoveNextAsync())
|
|
|
+ {
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+
|
|
|
+ value = e.Current;
|
|
|
+ }
|
|
|
+
|
|
|
+<#
|
|
|
+ }
|
|
|
+#>
|
|
|
+ while (await e.MoveNextAsync())
|
|
|
+ {
|
|
|
+ var x = e.Current;
|
|
|
+ if (x <#=comparison#> value)
|
|
|
+ {
|
|
|
+ value = x;
|
|
|
+ }
|
|
|
+<#
|
|
|
+ if (isFloatingPoint && m == "Min")
|
|
|
+ {
|
|
|
+#>
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Normally NaN < anything is false, as is anything < NaN
|
|
|
+ // However, this leads to some irksome outcomes in Min and Max.
|
|
|
+ // If we use those semantics then Min(NaN, 5.0) is NaN, but
|
|
|
+ // Min(5.0, NaN) is 5.0! To fix this, we impose a total
|
|
|
+ // ordering where NaN is smaller than every value, including
|
|
|
+ // negative infinity.
|
|
|
+ // Not testing for NaN therefore isn't an option, but since we
|
|
|
+ // can't find a smaller value, we can short-circuit.
|
|
|
+
|
|
|
+ if (<#=t#>.IsNaN(x))
|
|
|
+ {
|
|
|
+ return x;
|
|
|
+ }
|
|
|
+ }
|
|
|
+<#
|
|
|
+ }
|
|
|
+#>
|
|
|
+ }
|
|
|
+ }
|
|
|
+#else
|
|
|
var e = _source.GetAsyncEnumerator(_cancellationToken);
|
|
|
|
|
|
try
|
|
@@ -112,6 +177,7 @@ foreach (var m in new[] { "Max", "Min" })
|
|
|
{
|
|
|
await e.DisposeAsync().ConfigureAwait(false);
|
|
|
}
|
|
|
+#endif
|
|
|
|
|
|
return value;
|
|
|
<#
|
|
@@ -121,6 +187,162 @@ foreach (var m in new[] { "Max", "Min" })
|
|
|
#>
|
|
|
<#=t#> value = null;
|
|
|
|
|
|
+#if CSHARP8
|
|
|
+ await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
|
|
|
+ {
|
|
|
+ // Start off knowing that we've a non-null value (or exit here, knowing we don't)
|
|
|
+ // so we don't have to keep testing for nullity.
|
|
|
+ do
|
|
|
+ {
|
|
|
+ if (!await e.MoveNextAsync())
|
|
|
+ {
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+
|
|
|
+ value = e.Current;
|
|
|
+ }
|
|
|
+ while (!value.HasValue);
|
|
|
+
|
|
|
+ // Keep hold of the wrapped value, and do comparisons on that, rather than
|
|
|
+ // using the lifted operation each time.
|
|
|
+ var valueVal = value.GetValueOrDefault();
|
|
|
+
|
|
|
+<#
|
|
|
+ if (isInteger && m == "Max")
|
|
|
+ {
|
|
|
+#>
|
|
|
+ if (valueVal >= 0)
|
|
|
+ {
|
|
|
+ // We can fast-path this case where we know HasValue will
|
|
|
+ // never affect the outcome, without constantly checking
|
|
|
+ // if we're in such a state. Similar fast-paths could
|
|
|
+ // be done for other cases, but as all-positive or mostly-
|
|
|
+ // positive integer values are quite common in real-world
|
|
|
+ // uses, it's only been done for int? and long?.
|
|
|
+
|
|
|
+ while (await e.MoveNextAsync())
|
|
|
+ {
|
|
|
+ var cur = e.Current;
|
|
|
+ var x = cur.GetValueOrDefault();
|
|
|
+
|
|
|
+ if (x <#=comparison#> valueVal)
|
|
|
+ {
|
|
|
+ valueVal = x;
|
|
|
+ value = cur;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ while (await e.MoveNextAsync())
|
|
|
+ {
|
|
|
+ var cur = e.Current;
|
|
|
+ var x = cur.GetValueOrDefault();
|
|
|
+
|
|
|
+ // Do not replace & with &&. The branch prediction cost outweighs the extra operation
|
|
|
+ // unless nulls either never happen or always happen.
|
|
|
+ if (cur.HasValue & x <#=comparison#> valueVal)
|
|
|
+ {
|
|
|
+ valueVal = x;
|
|
|
+ value = cur;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+<#
|
|
|
+ }
|
|
|
+ else if (isFloatingPoint && m == "Min")
|
|
|
+ {
|
|
|
+#>
|
|
|
+ while (await e.MoveNextAsync())
|
|
|
+ {
|
|
|
+ var cur = e.Current;
|
|
|
+ if (cur.HasValue)
|
|
|
+ {
|
|
|
+ var x = cur.GetValueOrDefault();
|
|
|
+ if (x <#=comparison#> valueVal)
|
|
|
+ {
|
|
|
+ valueVal = x;
|
|
|
+ value = cur;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Normally NaN < anything is false, as is anything < NaN
|
|
|
+ // However, this leads to some irksome outcomes in Min and Max.
|
|
|
+ // If we use those semantics then Min(NaN, 5.0) is NaN, but
|
|
|
+ // Min(5.0, NaN) is 5.0! To fix this, we impose a total
|
|
|
+ // ordering where NaN is smaller than every value, including
|
|
|
+ // negative infinity.
|
|
|
+ // Not testing for NaN therefore isn't an option, but since we
|
|
|
+ // can't find a smaller value, we can short-circuit.
|
|
|
+
|
|
|
+ if (<#=t.TrimEnd('?')#>.IsNaN(x))
|
|
|
+ {
|
|
|
+ return cur;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+<#
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (isFloatingPoint && m == "Max")
|
|
|
+ {
|
|
|
+#>
|
|
|
+ // NaN is ordered less than all other values. We need to do explicit checks
|
|
|
+ // to ensure this, but once we've found a value that is not NaN we need no
|
|
|
+ // longer worry about it, so first loop until such a value is found (or not,
|
|
|
+ // as the case may be).
|
|
|
+
|
|
|
+ while (<#=t.TrimEnd('?')#>.IsNaN(valueVal))
|
|
|
+ {
|
|
|
+ if (!await e.MoveNextAsync())
|
|
|
+ {
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+
|
|
|
+ var cur = e.Current;
|
|
|
+
|
|
|
+ if (cur.HasValue)
|
|
|
+ {
|
|
|
+ valueVal = (value = cur).GetValueOrDefault();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+<#
|
|
|
+ }
|
|
|
+#>
|
|
|
+ while (await e.MoveNextAsync())
|
|
|
+ {
|
|
|
+ var cur = e.Current;
|
|
|
+ var x = cur.GetValueOrDefault();
|
|
|
+
|
|
|
+<#
|
|
|
+ if (shortCircuit)
|
|
|
+ {
|
|
|
+#>
|
|
|
+ if (cur.HasValue && x <#=comparison#> valueVal)
|
|
|
+<#
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+#>
|
|
|
+ // Do not replace & with &&. The branch prediction cost outweighs the extra operation
|
|
|
+ // unless nulls either never happen or always happen.
|
|
|
+ if (cur.HasValue & x <#=comparison#> valueVal)
|
|
|
+<#
|
|
|
+ }
|
|
|
+#>
|
|
|
+ {
|
|
|
+ valueVal = x;
|
|
|
+ value = cur;
|
|
|
+ }
|
|
|
+ }
|
|
|
+<#
|
|
|
+ }
|
|
|
+#>
|
|
|
+ }
|
|
|
+#else
|
|
|
var e = _source.GetAsyncEnumerator(_cancellationToken);
|
|
|
|
|
|
try
|
|
@@ -281,6 +503,7 @@ foreach (var m in new[] { "Max", "Min" })
|
|
|
{
|
|
|
await e.DisposeAsync().ConfigureAwait(false);
|
|
|
}
|
|
|
+#endif
|
|
|
|
|
|
return value;
|
|
|
<#
|
|
@@ -322,6 +545,71 @@ foreach (var overload in new[] {
|
|
|
#>
|
|
|
<#=t#> value;
|
|
|
|
|
|
+#if CSHARP8
|
|
|
+ await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
|
|
|
+ {
|
|
|
+ if (!await e.MoveNextAsync())
|
|
|
+ {
|
|
|
+ throw Error.NoElements();
|
|
|
+ }
|
|
|
+
|
|
|
+ value = <#=overload.invoke#>;
|
|
|
+
|
|
|
+<#
|
|
|
+ if (isFloatingPoint && m == "Max")
|
|
|
+ {
|
|
|
+#>
|
|
|
+ // NaN is ordered less than all other values. We need to do explicit checks
|
|
|
+ // to ensure this, but once we've found a value that is not NaN we need no
|
|
|
+ // longer worry about it, so first loop until such a value is found (or not,
|
|
|
+ // as the case may be).
|
|
|
+
|
|
|
+ while (<#=t#>.IsNaN(value))
|
|
|
+ {
|
|
|
+ if (!await e.MoveNextAsync())
|
|
|
+ {
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+
|
|
|
+ value = <#=overload.invoke#>;
|
|
|
+ }
|
|
|
+
|
|
|
+<#
|
|
|
+ }
|
|
|
+#>
|
|
|
+ while (await e.MoveNextAsync())
|
|
|
+ {
|
|
|
+ var x = <#=overload.invoke#>;
|
|
|
+ if (x <#=comparison#> value)
|
|
|
+ {
|
|
|
+ value = x;
|
|
|
+ }
|
|
|
+<#
|
|
|
+ if (isFloatingPoint && m == "Min")
|
|
|
+ {
|
|
|
+#>
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Normally NaN < anything is false, as is anything < NaN
|
|
|
+ // However, this leads to some irksome outcomes in Min and Max.
|
|
|
+ // If we use those semantics then Min(NaN, 5.0) is NaN, but
|
|
|
+ // Min(5.0, NaN) is 5.0! To fix this, we impose a total
|
|
|
+ // ordering where NaN is smaller than every value, including
|
|
|
+ // negative infinity.
|
|
|
+ // Not testing for NaN therefore isn't an option, but since we
|
|
|
+ // can't find a smaller value, we can short-circuit.
|
|
|
+
|
|
|
+ if (<#=t#>.IsNaN(x))
|
|
|
+ {
|
|
|
+ return x;
|
|
|
+ }
|
|
|
+ }
|
|
|
+<#
|
|
|
+ }
|
|
|
+#>
|
|
|
+ }
|
|
|
+ }
|
|
|
+#else
|
|
|
var e = _source.GetAsyncEnumerator(_cancellationToken);
|
|
|
|
|
|
try
|
|
@@ -391,6 +679,7 @@ foreach (var overload in new[] {
|
|
|
{
|
|
|
await e.DisposeAsync().ConfigureAwait(false);
|
|
|
}
|
|
|
+#endif
|
|
|
|
|
|
return value;
|
|
|
<#
|
|
@@ -400,6 +689,162 @@ foreach (var overload in new[] {
|
|
|
#>
|
|
|
<#=t#> value = null;
|
|
|
|
|
|
+#if CSHARP8
|
|
|
+ await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
|
|
|
+ {
|
|
|
+ // Start off knowing that we've a non-null value (or exit here, knowing we don't)
|
|
|
+ // so we don't have to keep testing for nullity.
|
|
|
+ do
|
|
|
+ {
|
|
|
+ if (!await e.MoveNextAsync())
|
|
|
+ {
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+
|
|
|
+ value = <#=overload.invoke#>;
|
|
|
+ }
|
|
|
+ while (!value.HasValue);
|
|
|
+
|
|
|
+ // Keep hold of the wrapped value, and do comparisons on that, rather than
|
|
|
+ // using the lifted operation each time.
|
|
|
+ var valueVal = value.GetValueOrDefault();
|
|
|
+
|
|
|
+<#
|
|
|
+ if (isInteger && m == "Max")
|
|
|
+ {
|
|
|
+#>
|
|
|
+ if (valueVal >= 0)
|
|
|
+ {
|
|
|
+ // We can fast-path this case where we know HasValue will
|
|
|
+ // never affect the outcome, without constantly checking
|
|
|
+ // if we're in such a state. Similar fast-paths could
|
|
|
+ // be done for other cases, but as all-positive or mostly-
|
|
|
+ // positive integer values are quite common in real-world
|
|
|
+ // uses, it's only been done for int? and long?.
|
|
|
+
|
|
|
+ while (await e.MoveNextAsync())
|
|
|
+ {
|
|
|
+ var cur = <#=overload.invoke#>;
|
|
|
+ var x = cur.GetValueOrDefault();
|
|
|
+
|
|
|
+ if (x <#=comparison#> valueVal)
|
|
|
+ {
|
|
|
+ valueVal = x;
|
|
|
+ value = cur;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ while (await e.MoveNextAsync())
|
|
|
+ {
|
|
|
+ var cur = <#=overload.invoke#>;
|
|
|
+ var x = cur.GetValueOrDefault();
|
|
|
+
|
|
|
+ // Do not replace & with &&. The branch prediction cost outweighs the extra operation
|
|
|
+ // unless nulls either never happen or always happen.
|
|
|
+ if (cur.HasValue & x <#=comparison#> valueVal)
|
|
|
+ {
|
|
|
+ valueVal = x;
|
|
|
+ value = cur;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+<#
|
|
|
+ }
|
|
|
+ else if (isFloatingPoint && m == "Min")
|
|
|
+ {
|
|
|
+#>
|
|
|
+ while (await e.MoveNextAsync())
|
|
|
+ {
|
|
|
+ var cur = <#=overload.invoke#>;
|
|
|
+ if (cur.HasValue)
|
|
|
+ {
|
|
|
+ var x = cur.GetValueOrDefault();
|
|
|
+ if (x <#=comparison#> valueVal)
|
|
|
+ {
|
|
|
+ valueVal = x;
|
|
|
+ value = cur;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Normally NaN < anything is false, as is anything < NaN
|
|
|
+ // However, this leads to some irksome outcomes in Min and Max.
|
|
|
+ // If we use those semantics then Min(NaN, 5.0) is NaN, but
|
|
|
+ // Min(5.0, NaN) is 5.0! To fix this, we impose a total
|
|
|
+ // ordering where NaN is smaller than every value, including
|
|
|
+ // negative infinity.
|
|
|
+ // Not testing for NaN therefore isn't an option, but since we
|
|
|
+ // can't find a smaller value, we can short-circuit.
|
|
|
+
|
|
|
+ if (<#=t.TrimEnd('?')#>.IsNaN(x))
|
|
|
+ {
|
|
|
+ return cur;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+<#
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (isFloatingPoint && m == "Max")
|
|
|
+ {
|
|
|
+#>
|
|
|
+ // NaN is ordered less than all other values. We need to do explicit checks
|
|
|
+ // to ensure this, but once we've found a value that is not NaN we need no
|
|
|
+ // longer worry about it, so first loop until such a value is found (or not,
|
|
|
+ // as the case may be).
|
|
|
+
|
|
|
+ while (<#=t.TrimEnd('?')#>.IsNaN(valueVal))
|
|
|
+ {
|
|
|
+ if (!await e.MoveNextAsync())
|
|
|
+ {
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+
|
|
|
+ var cur = <#=overload.invoke#>;
|
|
|
+
|
|
|
+ if (cur.HasValue)
|
|
|
+ {
|
|
|
+ valueVal = (value = cur).GetValueOrDefault();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+<#
|
|
|
+ }
|
|
|
+#>
|
|
|
+ while (await e.MoveNextAsync())
|
|
|
+ {
|
|
|
+ var cur = <#=overload.invoke#>;
|
|
|
+ var x = cur.GetValueOrDefault();
|
|
|
+
|
|
|
+<#
|
|
|
+ if (shortCircuit)
|
|
|
+ {
|
|
|
+#>
|
|
|
+ if (cur.HasValue && x <#=comparison#> valueVal)
|
|
|
+<#
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+#>
|
|
|
+ // Do not replace & with &&. The branch prediction cost outweighs the extra operation
|
|
|
+ // unless nulls either never happen or always happen.
|
|
|
+ if (cur.HasValue & x <#=comparison#> valueVal)
|
|
|
+<#
|
|
|
+ }
|
|
|
+#>
|
|
|
+ {
|
|
|
+ valueVal = x;
|
|
|
+ value = cur;
|
|
|
+ }
|
|
|
+ }
|
|
|
+<#
|
|
|
+ }
|
|
|
+#>
|
|
|
+ }
|
|
|
+#else
|
|
|
var e = _source.GetAsyncEnumerator(_cancellationToken);
|
|
|
|
|
|
try
|
|
@@ -560,6 +1005,7 @@ foreach (var overload in new[] {
|
|
|
{
|
|
|
await e.DisposeAsync().ConfigureAwait(false);
|
|
|
}
|
|
|
+#endif
|
|
|
|
|
|
return value;
|
|
|
<#
|