| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566 | <#@ template debug="false" hostspecific="false" language="C#" #><#@ assembly name="System.Core" #><#@ import namespace="System.Linq" #><#@ import namespace="System.Text" #><#@ import namespace="System.Collections.Generic" #><#@ output extension=".cs" #>// Licensed to the .NET Foundation under one or more agreements.// The .NET Foundation licenses this file to you under the MIT license.// See the LICENSE file in the project root for more information.using System.Collections.Generic;using System.Threading;using System.Threading.Tasks;namespace System.Linq{    public static partial class AsyncEnumerable    {<#foreach (var m in new[] { "Max", "Min" }){    var comparison = m == "Max" ? ">" : "<";    foreach (var t in new[] { "int", "int?", "long", "long?", "float", "float?", "double", "double?", "decimal", "decimal?" })    {        var isFloatingPoint = t.StartsWith("float") || t.StartsWith("double");        var isInteger = t.StartsWith("int") || t.StartsWith("long");        var isNullable = t.EndsWith("?");        var shortCircuit = t.StartsWith("decimal");#>        public static ValueTask<<#=t#>> <#=m#>Async(this IAsyncEnumerable<<#=t#>> source, CancellationToken cancellationToken = default)        {            if (source == null)                throw Error.ArgumentNull(nameof(source));            return Core(source, cancellationToken);            static async ValueTask<<#=t#>> Core(IAsyncEnumerable<<#=t#>> source, CancellationToken cancellationToken)            {<#        if (!isNullable)        {#>                <#=t#> value;                await using (var e = source.GetConfiguredAsyncEnumerator(cancellationToken, 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;                            }                        }<#            }#>                    }                }                return value;<#        }        else        {#>                <#=t#> value = null;                await using (var e = source.GetConfiguredAsyncEnumerator(cancellationToken, 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;                        }                    }<#            }#>                }                return value;<#        }#>            }        }<#foreach (var overload in new[] {    new { selector = "Func<TSource, " + t + ">", invoke = "selector(e.Current)" },    new { selector = "Func<TSource, ValueTask<" + t + ">>", invoke = "await selector(e.Current).ConfigureAwait(false)" },    new { selector = "Func<TSource, CancellationToken, ValueTask<" + t + ">>", invoke = "await selector(e.Current, cancellationToken).ConfigureAwait(false)" },}){    var isAsync = overload.invoke.StartsWith("await");    var isDeepCancellation = overload.selector.Contains("CancellationToken");    var suffix = isAsync ? "Await" : "";    var visibility = isAsync ? "internal" : "public";    var core = isAsync ? "Core" : "";    if (isDeepCancellation)    {        suffix += "WithCancellation";#>#if !NO_DEEP_CANCELLATION<#    }#>        <#=visibility#> static ValueTask<<#=t#>> <#=m#><#=suffix#>Async<#=core#><TSource>(this IAsyncEnumerable<TSource> source, <#=overload.selector#> selector, CancellationToken cancellationToken = default)        {            if (source == null)                throw Error.ArgumentNull(nameof(source));            if (selector == null)                throw Error.ArgumentNull(nameof(selector));            return Core(source, selector, cancellationToken);            static async ValueTask<<#=t#>> Core(IAsyncEnumerable<TSource> source, <#=overload.selector#> selector, CancellationToken cancellationToken)            {<#        if (!isNullable)        {#>                <#=t#> value;                await using (var e = source.GetConfiguredAsyncEnumerator(cancellationToken, 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;                            }                        }<#            }#>                    }                }                return value;<#        }        else        {#>                <#=t#> value = null;                await using (var e = source.GetConfiguredAsyncEnumerator(cancellationToken, 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;                        }                    }<#            }#>                }                return value;<#        }#>            }        }<#    if (isDeepCancellation)    {#>#endif<#    }}#><#    }}#>    }}
 |