MinMax.Generated.tt 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031
  1. <#@ template debug="false" hostspecific="false" language="C#" #>
  2. <#@ assembly name="System.Core" #>
  3. <#@ import namespace="System.Linq" #>
  4. <#@ import namespace="System.Text" #>
  5. <#@ import namespace="System.Collections.Generic" #>
  6. <#@ output extension=".cs" #>
  7. // Licensed to the .NET Foundation under one or more agreements.
  8. // The .NET Foundation licenses this file to you under the MIT license.
  9. // See the LICENSE file in the project root for more information.
  10. using System.Collections.Generic;
  11. using System.Threading;
  12. using System.Threading.Tasks;
  13. namespace System.Linq
  14. {
  15. public static partial class AsyncEnumerable
  16. {
  17. <#
  18. foreach (var m in new[] { "Max", "Min" })
  19. {
  20. var comparison = m == "Max" ? ">" : "<";
  21. foreach (var t in new[] { "int", "int?", "long", "long?", "float", "float?", "double", "double?", "decimal", "decimal?" })
  22. {
  23. var isFloatingPoint = t.StartsWith("float") || t.StartsWith("double");
  24. var isInteger = t.StartsWith("int") || t.StartsWith("long");
  25. var isNullable = t.EndsWith("?");
  26. var shortCircuit = t.StartsWith("decimal");
  27. #>
  28. public static Task<<#=t#>> <#=m#>Async(this IAsyncEnumerable<<#=t#>> source, CancellationToken cancellationToken = default)
  29. {
  30. if (source == null)
  31. throw Error.ArgumentNull(nameof(source));
  32. return Core(source, cancellationToken);
  33. static async Task<<#=t#>> Core(IAsyncEnumerable<<#=t#>> _source, CancellationToken _cancellationToken)
  34. {
  35. <#
  36. if (!isNullable)
  37. {
  38. #>
  39. <#=t#> value;
  40. #if CSHARP8
  41. await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
  42. {
  43. if (!await e.MoveNextAsync())
  44. {
  45. throw Error.NoElements();
  46. }
  47. value = e.Current;
  48. <#
  49. if (isFloatingPoint && m == "Max")
  50. {
  51. #>
  52. // NaN is ordered less than all other values. We need to do explicit checks
  53. // to ensure this, but once we've found a value that is not NaN we need no
  54. // longer worry about it, so first loop until such a value is found (or not,
  55. // as the case may be).
  56. while (<#=t#>.IsNaN(value))
  57. {
  58. if (!await e.MoveNextAsync())
  59. {
  60. return value;
  61. }
  62. value = e.Current;
  63. }
  64. <#
  65. }
  66. #>
  67. while (await e.MoveNextAsync())
  68. {
  69. var x = e.Current;
  70. if (x <#=comparison#> value)
  71. {
  72. value = x;
  73. }
  74. <#
  75. if (isFloatingPoint && m == "Min")
  76. {
  77. #>
  78. else
  79. {
  80. // Normally NaN < anything is false, as is anything < NaN
  81. // However, this leads to some irksome outcomes in Min and Max.
  82. // If we use those semantics then Min(NaN, 5.0) is NaN, but
  83. // Min(5.0, NaN) is 5.0! To fix this, we impose a total
  84. // ordering where NaN is smaller than every value, including
  85. // negative infinity.
  86. // Not testing for NaN therefore isn't an option, but since we
  87. // can't find a smaller value, we can short-circuit.
  88. if (<#=t#>.IsNaN(x))
  89. {
  90. return x;
  91. }
  92. }
  93. <#
  94. }
  95. #>
  96. }
  97. }
  98. #else
  99. var e = _source.GetAsyncEnumerator(_cancellationToken);
  100. try
  101. {
  102. if (!await e.MoveNextAsync().ConfigureAwait(false))
  103. {
  104. throw Error.NoElements();
  105. }
  106. value = e.Current;
  107. <#
  108. if (isFloatingPoint && m == "Max")
  109. {
  110. #>
  111. // NaN is ordered less than all other values. We need to do explicit checks
  112. // to ensure this, but once we've found a value that is not NaN we need no
  113. // longer worry about it, so first loop until such a value is found (or not,
  114. // as the case may be).
  115. while (<#=t#>.IsNaN(value))
  116. {
  117. if (!await e.MoveNextAsync().ConfigureAwait(false))
  118. {
  119. return value;
  120. }
  121. value = e.Current;
  122. }
  123. <#
  124. }
  125. #>
  126. while (await e.MoveNextAsync().ConfigureAwait(false))
  127. {
  128. var x = e.Current;
  129. if (x <#=comparison#> value)
  130. {
  131. value = x;
  132. }
  133. <#
  134. if (isFloatingPoint && m == "Min")
  135. {
  136. #>
  137. else
  138. {
  139. // Normally NaN < anything is false, as is anything < NaN
  140. // However, this leads to some irksome outcomes in Min and Max.
  141. // If we use those semantics then Min(NaN, 5.0) is NaN, but
  142. // Min(5.0, NaN) is 5.0! To fix this, we impose a total
  143. // ordering where NaN is smaller than every value, including
  144. // negative infinity.
  145. // Not testing for NaN therefore isn't an option, but since we
  146. // can't find a smaller value, we can short-circuit.
  147. if (<#=t#>.IsNaN(x))
  148. {
  149. return x;
  150. }
  151. }
  152. <#
  153. }
  154. #>
  155. }
  156. }
  157. finally
  158. {
  159. await e.DisposeAsync().ConfigureAwait(false);
  160. }
  161. #endif
  162. return value;
  163. <#
  164. }
  165. else
  166. {
  167. #>
  168. <#=t#> value = null;
  169. #if CSHARP8
  170. await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
  171. {
  172. // Start off knowing that we've a non-null value (or exit here, knowing we don't)
  173. // so we don't have to keep testing for nullity.
  174. do
  175. {
  176. if (!await e.MoveNextAsync())
  177. {
  178. return value;
  179. }
  180. value = e.Current;
  181. }
  182. while (!value.HasValue);
  183. // Keep hold of the wrapped value, and do comparisons on that, rather than
  184. // using the lifted operation each time.
  185. var valueVal = value.GetValueOrDefault();
  186. <#
  187. if (isInteger && m == "Max")
  188. {
  189. #>
  190. if (valueVal >= 0)
  191. {
  192. // We can fast-path this case where we know HasValue will
  193. // never affect the outcome, without constantly checking
  194. // if we're in such a state. Similar fast-paths could
  195. // be done for other cases, but as all-positive or mostly-
  196. // positive integer values are quite common in real-world
  197. // uses, it's only been done for int? and long?.
  198. while (await e.MoveNextAsync())
  199. {
  200. var cur = e.Current;
  201. var x = cur.GetValueOrDefault();
  202. if (x <#=comparison#> valueVal)
  203. {
  204. valueVal = x;
  205. value = cur;
  206. }
  207. }
  208. }
  209. else
  210. {
  211. while (await e.MoveNextAsync())
  212. {
  213. var cur = e.Current;
  214. var x = cur.GetValueOrDefault();
  215. // Do not replace & with &&. The branch prediction cost outweighs the extra operation
  216. // unless nulls either never happen or always happen.
  217. if (cur.HasValue & x <#=comparison#> valueVal)
  218. {
  219. valueVal = x;
  220. value = cur;
  221. }
  222. }
  223. }
  224. <#
  225. }
  226. else if (isFloatingPoint && m == "Min")
  227. {
  228. #>
  229. while (await e.MoveNextAsync())
  230. {
  231. var cur = e.Current;
  232. if (cur.HasValue)
  233. {
  234. var x = cur.GetValueOrDefault();
  235. if (x <#=comparison#> valueVal)
  236. {
  237. valueVal = x;
  238. value = cur;
  239. }
  240. else
  241. {
  242. // Normally NaN < anything is false, as is anything < NaN
  243. // However, this leads to some irksome outcomes in Min and Max.
  244. // If we use those semantics then Min(NaN, 5.0) is NaN, but
  245. // Min(5.0, NaN) is 5.0! To fix this, we impose a total
  246. // ordering where NaN is smaller than every value, including
  247. // negative infinity.
  248. // Not testing for NaN therefore isn't an option, but since we
  249. // can't find a smaller value, we can short-circuit.
  250. if (<#=t.TrimEnd('?')#>.IsNaN(x))
  251. {
  252. return cur;
  253. }
  254. }
  255. }
  256. }
  257. <#
  258. }
  259. else
  260. {
  261. if (isFloatingPoint && m == "Max")
  262. {
  263. #>
  264. // NaN is ordered less than all other values. We need to do explicit checks
  265. // to ensure this, but once we've found a value that is not NaN we need no
  266. // longer worry about it, so first loop until such a value is found (or not,
  267. // as the case may be).
  268. while (<#=t.TrimEnd('?')#>.IsNaN(valueVal))
  269. {
  270. if (!await e.MoveNextAsync())
  271. {
  272. return value;
  273. }
  274. var cur = e.Current;
  275. if (cur.HasValue)
  276. {
  277. valueVal = (value = cur).GetValueOrDefault();
  278. }
  279. }
  280. <#
  281. }
  282. #>
  283. while (await e.MoveNextAsync())
  284. {
  285. var cur = e.Current;
  286. var x = cur.GetValueOrDefault();
  287. <#
  288. if (shortCircuit)
  289. {
  290. #>
  291. if (cur.HasValue && x <#=comparison#> valueVal)
  292. <#
  293. }
  294. else
  295. {
  296. #>
  297. // Do not replace & with &&. The branch prediction cost outweighs the extra operation
  298. // unless nulls either never happen or always happen.
  299. if (cur.HasValue & x <#=comparison#> valueVal)
  300. <#
  301. }
  302. #>
  303. {
  304. valueVal = x;
  305. value = cur;
  306. }
  307. }
  308. <#
  309. }
  310. #>
  311. }
  312. #else
  313. var e = _source.GetAsyncEnumerator(_cancellationToken);
  314. try
  315. {
  316. // Start off knowing that we've a non-null value (or exit here, knowing we don't)
  317. // so we don't have to keep testing for nullity.
  318. do
  319. {
  320. if (!await e.MoveNextAsync().ConfigureAwait(false))
  321. {
  322. return value;
  323. }
  324. value = e.Current;
  325. }
  326. while (!value.HasValue);
  327. // Keep hold of the wrapped value, and do comparisons on that, rather than
  328. // using the lifted operation each time.
  329. var valueVal = value.GetValueOrDefault();
  330. <#
  331. if (isInteger && m == "Max")
  332. {
  333. #>
  334. if (valueVal >= 0)
  335. {
  336. // We can fast-path this case where we know HasValue will
  337. // never affect the outcome, without constantly checking
  338. // if we're in such a state. Similar fast-paths could
  339. // be done for other cases, but as all-positive or mostly-
  340. // positive integer values are quite common in real-world
  341. // uses, it's only been done for int? and long?.
  342. while (await e.MoveNextAsync().ConfigureAwait(false))
  343. {
  344. var cur = e.Current;
  345. var x = cur.GetValueOrDefault();
  346. if (x <#=comparison#> valueVal)
  347. {
  348. valueVal = x;
  349. value = cur;
  350. }
  351. }
  352. }
  353. else
  354. {
  355. while (await e.MoveNextAsync().ConfigureAwait(false))
  356. {
  357. var cur = e.Current;
  358. var x = cur.GetValueOrDefault();
  359. // Do not replace & with &&. The branch prediction cost outweighs the extra operation
  360. // unless nulls either never happen or always happen.
  361. if (cur.HasValue & x <#=comparison#> valueVal)
  362. {
  363. valueVal = x;
  364. value = cur;
  365. }
  366. }
  367. }
  368. <#
  369. }
  370. else if (isFloatingPoint && m == "Min")
  371. {
  372. #>
  373. while (await e.MoveNextAsync().ConfigureAwait(false))
  374. {
  375. var cur = e.Current;
  376. if (cur.HasValue)
  377. {
  378. var x = cur.GetValueOrDefault();
  379. if (x <#=comparison#> valueVal)
  380. {
  381. valueVal = x;
  382. value = cur;
  383. }
  384. else
  385. {
  386. // Normally NaN < anything is false, as is anything < NaN
  387. // However, this leads to some irksome outcomes in Min and Max.
  388. // If we use those semantics then Min(NaN, 5.0) is NaN, but
  389. // Min(5.0, NaN) is 5.0! To fix this, we impose a total
  390. // ordering where NaN is smaller than every value, including
  391. // negative infinity.
  392. // Not testing for NaN therefore isn't an option, but since we
  393. // can't find a smaller value, we can short-circuit.
  394. if (<#=t.TrimEnd('?')#>.IsNaN(x))
  395. {
  396. return cur;
  397. }
  398. }
  399. }
  400. }
  401. <#
  402. }
  403. else
  404. {
  405. if (isFloatingPoint && m == "Max")
  406. {
  407. #>
  408. // NaN is ordered less than all other values. We need to do explicit checks
  409. // to ensure this, but once we've found a value that is not NaN we need no
  410. // longer worry about it, so first loop until such a value is found (or not,
  411. // as the case may be).
  412. while (<#=t.TrimEnd('?')#>.IsNaN(valueVal))
  413. {
  414. if (!await e.MoveNextAsync().ConfigureAwait(false))
  415. {
  416. return value;
  417. }
  418. var cur = e.Current;
  419. if (cur.HasValue)
  420. {
  421. valueVal = (value = cur).GetValueOrDefault();
  422. }
  423. }
  424. <#
  425. }
  426. #>
  427. while (await e.MoveNextAsync().ConfigureAwait(false))
  428. {
  429. var cur = e.Current;
  430. var x = cur.GetValueOrDefault();
  431. <#
  432. if (shortCircuit)
  433. {
  434. #>
  435. if (cur.HasValue && x <#=comparison#> valueVal)
  436. <#
  437. }
  438. else
  439. {
  440. #>
  441. // Do not replace & with &&. The branch prediction cost outweighs the extra operation
  442. // unless nulls either never happen or always happen.
  443. if (cur.HasValue & x <#=comparison#> valueVal)
  444. <#
  445. }
  446. #>
  447. {
  448. valueVal = x;
  449. value = cur;
  450. }
  451. }
  452. <#
  453. }
  454. #>
  455. }
  456. finally
  457. {
  458. await e.DisposeAsync().ConfigureAwait(false);
  459. }
  460. #endif
  461. return value;
  462. <#
  463. }
  464. #>
  465. }
  466. }
  467. <#
  468. foreach (var overload in new[] {
  469. new { selector = "Func<TSource, " + t + ">", invoke = "_selector(e.Current)" },
  470. new { selector = "Func<TSource, ValueTask<" + t + ">>", invoke = "await _selector(e.Current).ConfigureAwait(false)" },
  471. new { selector = "Func<TSource, CancellationToken, ValueTask<" + t + ">>", invoke = "await _selector(e.Current, _cancellationToken).ConfigureAwait(false)" },
  472. })
  473. {
  474. var isDeepCancellation = overload.selector.Contains("CancellationToken");
  475. if (isDeepCancellation)
  476. {
  477. #>
  478. #if !NO_DEEP_CANCELLATION
  479. <#
  480. }
  481. #>
  482. public static Task<<#=t#>> <#=m#>Async<TSource>(this IAsyncEnumerable<TSource> source, <#=overload.selector#> selector, CancellationToken cancellationToken = default)
  483. {
  484. if (source == null)
  485. throw Error.ArgumentNull(nameof(source));
  486. if (selector == null)
  487. throw Error.ArgumentNull(nameof(selector));
  488. return Core(source, selector, cancellationToken);
  489. static async Task<<#=t#>> Core(IAsyncEnumerable<TSource> _source, <#=overload.selector#> _selector, CancellationToken _cancellationToken)
  490. {
  491. <#
  492. if (!isNullable)
  493. {
  494. #>
  495. <#=t#> value;
  496. #if CSHARP8
  497. await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
  498. {
  499. if (!await e.MoveNextAsync())
  500. {
  501. throw Error.NoElements();
  502. }
  503. value = <#=overload.invoke#>;
  504. <#
  505. if (isFloatingPoint && m == "Max")
  506. {
  507. #>
  508. // NaN is ordered less than all other values. We need to do explicit checks
  509. // to ensure this, but once we've found a value that is not NaN we need no
  510. // longer worry about it, so first loop until such a value is found (or not,
  511. // as the case may be).
  512. while (<#=t#>.IsNaN(value))
  513. {
  514. if (!await e.MoveNextAsync())
  515. {
  516. return value;
  517. }
  518. value = <#=overload.invoke#>;
  519. }
  520. <#
  521. }
  522. #>
  523. while (await e.MoveNextAsync())
  524. {
  525. var x = <#=overload.invoke#>;
  526. if (x <#=comparison#> value)
  527. {
  528. value = x;
  529. }
  530. <#
  531. if (isFloatingPoint && m == "Min")
  532. {
  533. #>
  534. else
  535. {
  536. // Normally NaN < anything is false, as is anything < NaN
  537. // However, this leads to some irksome outcomes in Min and Max.
  538. // If we use those semantics then Min(NaN, 5.0) is NaN, but
  539. // Min(5.0, NaN) is 5.0! To fix this, we impose a total
  540. // ordering where NaN is smaller than every value, including
  541. // negative infinity.
  542. // Not testing for NaN therefore isn't an option, but since we
  543. // can't find a smaller value, we can short-circuit.
  544. if (<#=t#>.IsNaN(x))
  545. {
  546. return x;
  547. }
  548. }
  549. <#
  550. }
  551. #>
  552. }
  553. }
  554. #else
  555. var e = _source.GetAsyncEnumerator(_cancellationToken);
  556. try
  557. {
  558. if (!await e.MoveNextAsync().ConfigureAwait(false))
  559. {
  560. throw Error.NoElements();
  561. }
  562. value = <#=overload.invoke#>;
  563. <#
  564. if (isFloatingPoint && m == "Max")
  565. {
  566. #>
  567. // NaN is ordered less than all other values. We need to do explicit checks
  568. // to ensure this, but once we've found a value that is not NaN we need no
  569. // longer worry about it, so first loop until such a value is found (or not,
  570. // as the case may be).
  571. while (<#=t#>.IsNaN(value))
  572. {
  573. if (!await e.MoveNextAsync().ConfigureAwait(false))
  574. {
  575. return value;
  576. }
  577. value = <#=overload.invoke#>;
  578. }
  579. <#
  580. }
  581. #>
  582. while (await e.MoveNextAsync().ConfigureAwait(false))
  583. {
  584. var x = <#=overload.invoke#>;
  585. if (x <#=comparison#> value)
  586. {
  587. value = x;
  588. }
  589. <#
  590. if (isFloatingPoint && m == "Min")
  591. {
  592. #>
  593. else
  594. {
  595. // Normally NaN < anything is false, as is anything < NaN
  596. // However, this leads to some irksome outcomes in Min and Max.
  597. // If we use those semantics then Min(NaN, 5.0) is NaN, but
  598. // Min(5.0, NaN) is 5.0! To fix this, we impose a total
  599. // ordering where NaN is smaller than every value, including
  600. // negative infinity.
  601. // Not testing for NaN therefore isn't an option, but since we
  602. // can't find a smaller value, we can short-circuit.
  603. if (<#=t#>.IsNaN(x))
  604. {
  605. return x;
  606. }
  607. }
  608. <#
  609. }
  610. #>
  611. }
  612. }
  613. finally
  614. {
  615. await e.DisposeAsync().ConfigureAwait(false);
  616. }
  617. #endif
  618. return value;
  619. <#
  620. }
  621. else
  622. {
  623. #>
  624. <#=t#> value = null;
  625. #if CSHARP8
  626. await using (var e = _source.GetAsyncEnumerator(_cancellationToken).ConfigureAwait(false))
  627. {
  628. // Start off knowing that we've a non-null value (or exit here, knowing we don't)
  629. // so we don't have to keep testing for nullity.
  630. do
  631. {
  632. if (!await e.MoveNextAsync())
  633. {
  634. return value;
  635. }
  636. value = <#=overload.invoke#>;
  637. }
  638. while (!value.HasValue);
  639. // Keep hold of the wrapped value, and do comparisons on that, rather than
  640. // using the lifted operation each time.
  641. var valueVal = value.GetValueOrDefault();
  642. <#
  643. if (isInteger && m == "Max")
  644. {
  645. #>
  646. if (valueVal >= 0)
  647. {
  648. // We can fast-path this case where we know HasValue will
  649. // never affect the outcome, without constantly checking
  650. // if we're in such a state. Similar fast-paths could
  651. // be done for other cases, but as all-positive or mostly-
  652. // positive integer values are quite common in real-world
  653. // uses, it's only been done for int? and long?.
  654. while (await e.MoveNextAsync())
  655. {
  656. var cur = <#=overload.invoke#>;
  657. var x = cur.GetValueOrDefault();
  658. if (x <#=comparison#> valueVal)
  659. {
  660. valueVal = x;
  661. value = cur;
  662. }
  663. }
  664. }
  665. else
  666. {
  667. while (await e.MoveNextAsync())
  668. {
  669. var cur = <#=overload.invoke#>;
  670. var x = cur.GetValueOrDefault();
  671. // Do not replace & with &&. The branch prediction cost outweighs the extra operation
  672. // unless nulls either never happen or always happen.
  673. if (cur.HasValue & x <#=comparison#> valueVal)
  674. {
  675. valueVal = x;
  676. value = cur;
  677. }
  678. }
  679. }
  680. <#
  681. }
  682. else if (isFloatingPoint && m == "Min")
  683. {
  684. #>
  685. while (await e.MoveNextAsync())
  686. {
  687. var cur = <#=overload.invoke#>;
  688. if (cur.HasValue)
  689. {
  690. var x = cur.GetValueOrDefault();
  691. if (x <#=comparison#> valueVal)
  692. {
  693. valueVal = x;
  694. value = cur;
  695. }
  696. else
  697. {
  698. // Normally NaN < anything is false, as is anything < NaN
  699. // However, this leads to some irksome outcomes in Min and Max.
  700. // If we use those semantics then Min(NaN, 5.0) is NaN, but
  701. // Min(5.0, NaN) is 5.0! To fix this, we impose a total
  702. // ordering where NaN is smaller than every value, including
  703. // negative infinity.
  704. // Not testing for NaN therefore isn't an option, but since we
  705. // can't find a smaller value, we can short-circuit.
  706. if (<#=t.TrimEnd('?')#>.IsNaN(x))
  707. {
  708. return cur;
  709. }
  710. }
  711. }
  712. }
  713. <#
  714. }
  715. else
  716. {
  717. if (isFloatingPoint && m == "Max")
  718. {
  719. #>
  720. // NaN is ordered less than all other values. We need to do explicit checks
  721. // to ensure this, but once we've found a value that is not NaN we need no
  722. // longer worry about it, so first loop until such a value is found (or not,
  723. // as the case may be).
  724. while (<#=t.TrimEnd('?')#>.IsNaN(valueVal))
  725. {
  726. if (!await e.MoveNextAsync())
  727. {
  728. return value;
  729. }
  730. var cur = <#=overload.invoke#>;
  731. if (cur.HasValue)
  732. {
  733. valueVal = (value = cur).GetValueOrDefault();
  734. }
  735. }
  736. <#
  737. }
  738. #>
  739. while (await e.MoveNextAsync())
  740. {
  741. var cur = <#=overload.invoke#>;
  742. var x = cur.GetValueOrDefault();
  743. <#
  744. if (shortCircuit)
  745. {
  746. #>
  747. if (cur.HasValue && x <#=comparison#> valueVal)
  748. <#
  749. }
  750. else
  751. {
  752. #>
  753. // Do not replace & with &&. The branch prediction cost outweighs the extra operation
  754. // unless nulls either never happen or always happen.
  755. if (cur.HasValue & x <#=comparison#> valueVal)
  756. <#
  757. }
  758. #>
  759. {
  760. valueVal = x;
  761. value = cur;
  762. }
  763. }
  764. <#
  765. }
  766. #>
  767. }
  768. #else
  769. var e = _source.GetAsyncEnumerator(_cancellationToken);
  770. try
  771. {
  772. // Start off knowing that we've a non-null value (or exit here, knowing we don't)
  773. // so we don't have to keep testing for nullity.
  774. do
  775. {
  776. if (!await e.MoveNextAsync().ConfigureAwait(false))
  777. {
  778. return value;
  779. }
  780. value = <#=overload.invoke#>;
  781. }
  782. while (!value.HasValue);
  783. // Keep hold of the wrapped value, and do comparisons on that, rather than
  784. // using the lifted operation each time.
  785. var valueVal = value.GetValueOrDefault();
  786. <#
  787. if (isInteger && m == "Max")
  788. {
  789. #>
  790. if (valueVal >= 0)
  791. {
  792. // We can fast-path this case where we know HasValue will
  793. // never affect the outcome, without constantly checking
  794. // if we're in such a state. Similar fast-paths could
  795. // be done for other cases, but as all-positive or mostly-
  796. // positive integer values are quite common in real-world
  797. // uses, it's only been done for int? and long?.
  798. while (await e.MoveNextAsync().ConfigureAwait(false))
  799. {
  800. var cur = <#=overload.invoke#>;
  801. var x = cur.GetValueOrDefault();
  802. if (x <#=comparison#> valueVal)
  803. {
  804. valueVal = x;
  805. value = cur;
  806. }
  807. }
  808. }
  809. else
  810. {
  811. while (await e.MoveNextAsync().ConfigureAwait(false))
  812. {
  813. var cur = <#=overload.invoke#>;
  814. var x = cur.GetValueOrDefault();
  815. // Do not replace & with &&. The branch prediction cost outweighs the extra operation
  816. // unless nulls either never happen or always happen.
  817. if (cur.HasValue & x <#=comparison#> valueVal)
  818. {
  819. valueVal = x;
  820. value = cur;
  821. }
  822. }
  823. }
  824. <#
  825. }
  826. else if (isFloatingPoint && m == "Min")
  827. {
  828. #>
  829. while (await e.MoveNextAsync().ConfigureAwait(false))
  830. {
  831. var cur = <#=overload.invoke#>;
  832. if (cur.HasValue)
  833. {
  834. var x = cur.GetValueOrDefault();
  835. if (x <#=comparison#> valueVal)
  836. {
  837. valueVal = x;
  838. value = cur;
  839. }
  840. else
  841. {
  842. // Normally NaN < anything is false, as is anything < NaN
  843. // However, this leads to some irksome outcomes in Min and Max.
  844. // If we use those semantics then Min(NaN, 5.0) is NaN, but
  845. // Min(5.0, NaN) is 5.0! To fix this, we impose a total
  846. // ordering where NaN is smaller than every value, including
  847. // negative infinity.
  848. // Not testing for NaN therefore isn't an option, but since we
  849. // can't find a smaller value, we can short-circuit.
  850. if (<#=t.TrimEnd('?')#>.IsNaN(x))
  851. {
  852. return cur;
  853. }
  854. }
  855. }
  856. }
  857. <#
  858. }
  859. else
  860. {
  861. if (isFloatingPoint && m == "Max")
  862. {
  863. #>
  864. // NaN is ordered less than all other values. We need to do explicit checks
  865. // to ensure this, but once we've found a value that is not NaN we need no
  866. // longer worry about it, so first loop until such a value is found (or not,
  867. // as the case may be).
  868. while (<#=t.TrimEnd('?')#>.IsNaN(valueVal))
  869. {
  870. if (!await e.MoveNextAsync().ConfigureAwait(false))
  871. {
  872. return value;
  873. }
  874. var cur = <#=overload.invoke#>;
  875. if (cur.HasValue)
  876. {
  877. valueVal = (value = cur).GetValueOrDefault();
  878. }
  879. }
  880. <#
  881. }
  882. #>
  883. while (await e.MoveNextAsync().ConfigureAwait(false))
  884. {
  885. var cur = <#=overload.invoke#>;
  886. var x = cur.GetValueOrDefault();
  887. <#
  888. if (shortCircuit)
  889. {
  890. #>
  891. if (cur.HasValue && x <#=comparison#> valueVal)
  892. <#
  893. }
  894. else
  895. {
  896. #>
  897. // Do not replace & with &&. The branch prediction cost outweighs the extra operation
  898. // unless nulls either never happen or always happen.
  899. if (cur.HasValue & x <#=comparison#> valueVal)
  900. <#
  901. }
  902. #>
  903. {
  904. valueVal = x;
  905. value = cur;
  906. }
  907. }
  908. <#
  909. }
  910. #>
  911. }
  912. finally
  913. {
  914. await e.DisposeAsync().ConfigureAwait(false);
  915. }
  916. #endif
  917. return value;
  918. <#
  919. }
  920. #>
  921. }
  922. }
  923. <#
  924. if (isDeepCancellation)
  925. {
  926. #>
  927. #endif
  928. <#
  929. }
  930. }
  931. #>
  932. <#
  933. }
  934. }
  935. #>
  936. }
  937. }