<!doctype html><html lang="en">
 <head>
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
  <meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
  <title>P3300R0: C++ Asynchronous Parallel Algorithms</title>
<style data-fill-with="stylesheet">/******************************************************************************
 *                   Style sheet for the W3C specifications                   *
 *
 * Special classes handled by this style sheet include:
 *
 * Indices
 *   - .toc for the Table of Contents (<ol class="toc">)
 *     + <span class="secno"> for the section numbers
 *   - #toc for the Table of Contents (<nav id="toc">)
 *   - ul.index for Indices (<a href="#ref">term</a><span>, in § N.M</span>)
 *   - table.index for Index Tables (e.g. for properties or elements)
 *
 * Structural Markup
 *   - table.data for general data tables
 *     -> use 'scope' attribute, <colgroup>, <thead>, and <tbody> for best results !
 *     -> use <table class='complex data'> for extra-complex tables
 *     -> use <td class='long'> for paragraph-length cell content
 *     -> use <td class='pre'> when manual line breaks/indentation would help readability
 *   - dl.switch for switch statements
 *   - ol.algorithm for algorithms (helps to visualize nesting)
 *   - .figure and .caption (HTML4) and figure and figcaption (HTML5)
 *     -> .sidefigure for right-floated figures
 *   - ins/del
 *     -> ins/del.c### for candidate and proposed changes (amendments)
 *
 * Code
 *   - pre and code
 *
 * Special Sections
 *   - .note       for informative notes             (div, p, span, aside, details)
 *   - .example    for informative examples          (div, p, pre, span)
 *   - .issue      for issues                        (div, p, span)
 *   - .advisement for loud normative statements     (div, p, strong)
 *   - .annoying-warning for spec obsoletion notices (div, aside, details)
 *   - .correction for "candidate corrections"       (div, aside, details, section)
 *   - .addition   for "candidate additions"         (div, aside, details, section)
 *   - .correction.proposed for "proposed corrections" (div, aside, details, section)
 *   - .addition.proposed   for "proposed additions"   (div, aside, details, section)
 *
 * Definition Boxes
 *   - pre.def   for WebIDL definitions
 *   - table.def for tables that define other entities (e.g. CSS properties)
 *   - dl.def    for definition lists that define other entitles (e.g. HTML elements)
 *
 * Numbering
 *   - .secno for section numbers in .toc and headings (<span class='secno'>3.2</span>)
 *   - .marker for source-inserted example/figure/issue numbers (<span class='marker'>Issue 4</span>)
 *   - ::before styled for CSS-generated issue/example/figure numbers:
 *     -> Documents wishing to use this only need to add
 *        figcaption::before,
 *        .caption::before { content: "Figure "  counter(figure) " ";  }
 *        .example::before { content: "Example " counter(example) " "; }
 *        .issue::before   { content: "Issue "   counter(issue) " ";   }
 *
 * Header Stuff (ignore, just don't conflict with these classes)
 *   - .head for the header
 *   - .copyright for the copyright
 *
 * Outdated warning for old specs
 *
 * Miscellaneous
 *   - .overlarge for things that should be as wide as possible, even if
 *     that overflows the body text area. This can be used on an item or
 *     on its container, depending on the effect desired.
 *     Note that this styling basically doesn't help at all when printing,
 *     since A4 paper isn't much wider than the max-width here.
 *     It's better to design things to fit into a narrower measure if possible.
 *
 *   - js-added ToC jump links (see fixup.js)
 *
 ******************************************************************************/

/* color variables included separately for reliability */

/******************************************************************************/
/*                                    Body                                    */
/******************************************************************************/

	html {
	}

	body {
		counter-reset: example figure issue;

		/* Layout */
		max-width: 50em;			  /* limit line length to 50em for readability   */
		margin: 0 auto;				/* center text within page                    */
		padding: 1.6em 1.5em 2em 50px; /* assume 16px font size for downlevel clients */
		padding: 1.6em 1.5em 2em calc(26px + 1.5em); /* leave space for status flag    */

		/* Typography */
		line-height: 1.5;
		font-family: sans-serif;
		widows: 2;
		orphans: 2;
		word-wrap: break-word;
		overflow-wrap: break-word;
		hyphens: auto;

		color: black;
		color: var(--text);
		background: white top left fixed no-repeat;
		background: var(--bg) top left fixed no-repeat;
		background-size: 25px auto;
	}


/******************************************************************************/
/*                         Front Matter & Navigation                          */
/******************************************************************************/

/** Header ********************************************************************/

	div.head { margin-bottom: 1em; }
	div.head hr { border-style: solid; }

	div.head h1 {
		font-weight: bold;
		margin: 0 0 .1em;
		font-size: 220%;
	}

	div.head h2 { margin-bottom: 1.5em;}

/** W3C Logo ******************************************************************/

	.head .logo {
		float: right;
		margin: 0.4rem 0 0.2rem .4rem;
	}

	.head img[src*="logos/W3C"] {
		display: block;
		border: solid #1a5e9a;
		border: solid var(--logo-bg);
		border-width: .65rem .7rem .6rem;
		border-radius: .4rem;
		background: #1a5e9a;
		background: var(--logo-bg);
		color: white;
		color: var(--logo-text);
		font-weight: bold;
	}

	.head a:hover > img[src*="logos/W3C"],
	.head a:focus > img[src*="logos/W3C"] {
		opacity: .8;
	}

	.head a:active > img[src*="logos/W3C"] {
		background: #c00;
		background: var(--logo-active-bg);
		border-color: #c00;
		border-color: var(--logo-active-bg);
	}

	/* see also additional rules in Link Styling section */

/** Copyright *****************************************************************/

	p.copyright,
	p.copyright small { font-size: small; }

/** Back to Top / ToC Toggle **************************************************/

	@media print {
		#toc-nav {
			display: none;
		}
	}
	@media not print {
		#toc-nav {
			position: fixed;
			z-index: 3;
			bottom: 0; left: 0;
			margin: 0;
			min-width: 1.33em;
			border-top-right-radius: 2rem;
			box-shadow: 0 0 2px;
			font-size: 1.5em;
		}
		#toc-nav > a {
			display: block;
			white-space: nowrap;

			height: 1.33em;
			padding: .1em 0.3em;
			margin: 0;

			box-shadow: 0 0 2px;
			border: none;
			border-top-right-radius: 1.33em;

			color: #707070;
			color: var(--tocnav-normal-text);
			background: white;
			background: var(--tocnav-normal-bg);
		}
		#toc-nav > a:hover,
		#toc-nav > a:focus {
			color: black;
			color: var(--tocnav-hover-text);
			background: #f8f8f8;
			background: var(--tocnav-hover-bg);
		}
		#toc-nav > a:active {
			color: #c00;
			color: var(--tocnav-active-text);
			background: white;
			background: var(--tocnav-active-bg);
		}

		#toc-nav > #toc-jump {
			padding-bottom: 2em;
			margin-bottom: -1.9em;
		}

		/* statusbar gets in the way on keyboard focus; remove once browsers fix */
		#toc-nav > a[href="#toc"]:not(:hover):focus:last-child {
			padding-bottom: 1.5rem;
		}

		#toc-nav:not(:hover) > a:not(:focus) > span + span {
			/* Ideally this uses :focus-within on #toc-nav */
			display: none;
		}
		#toc-nav > a > span + span {
			padding-right: 0.2em;
		}
	}

/** ToC Sidebar ***************************************************************/

	/* Floating sidebar */
	@media screen {
		body.toc-sidebar #toc {
			position: fixed;
			top: 0; bottom: 0;
			left: 0;
			width: 23.5em;
			max-width: 80%;
			max-width: calc(100% - 2em - 26px);
			overflow: auto;
			padding: 0 1em;
			padding-left: 42px;
			padding-left: calc(1em + 26px);
			color: black;
			color: var(--tocsidebar-text);
			background: inherit;
			background-color: #f7f8f9;
			background-color: var(--tocsidebar-bg);
			z-index: 1;
			box-shadow: -.1em 0 .25em rgba(0,0,0,.1) inset;
			box-shadow: -.1em 0 .25em var(--tocsidebar-shadow) inset;
		}
		body.toc-sidebar #toc h2 {
			margin-top: .8rem;
			font-variant: small-caps;
			font-variant: all-small-caps;
			text-transform: lowercase;
			font-weight: bold;
			color: gray;
			color: hsla(203,20%,40%,.7);
			color: var(--tocsidebar-heading-text);
		}
		body.toc-sidebar #toc-jump:not(:focus) {
			width: 0;
			height: 0;
			padding: 0;
			position: absolute;
			overflow: hidden;
		}
	}
	/* Hide main scroller when only the ToC is visible anyway */
	@media screen and (max-width: 28em) {
		body.toc-sidebar {
			overflow: hidden;
		}
	}

	/* Sidebar with its own space */
	@media screen and (min-width: 78em) {
		body:not(.toc-inline) #toc {
			position: fixed;
			top: 0; bottom: 0;
			left: 0;
			width: 23.5em;
			overflow: auto;
			padding: 0 1em;
			padding-left: 42px;
			padding-left: calc(1em + 26px);
			color: black;
			color: var(--tocsidebar-text);
			background: inherit;
			background-color: #f7f8f9;
			background-color: var(--tocsidebar-bg);
			z-index: 1;
			box-shadow: -.1em 0 .25em rgba(0,0,0,.1) inset;
			box-shadow: -.1em 0 .25em var(--tocsidebar-shadow) inset;
		}
		body:not(.toc-inline) #toc h2 {
			margin-top: .8rem;
			font-variant: small-caps;
			font-variant: all-small-caps;
			text-transform: lowercase;
			font-weight: bold;
			color: gray;
			color: hsla(203,20%,40%,.7);
			color: var(--tocsidebar-heading-text);
		}

		body:not(.toc-inline) {
			padding-left: 29em;
		}
		/* See also Overflow section at the bottom */

		body:not(.toc-inline) #toc-jump:not(:focus) {
			width: 0;
			height: 0;
			padding: 0;
			position: absolute;
			overflow: hidden;
		}
	}
	@media screen and (min-width: 90em) {
		body:not(.toc-inline) {
			margin: 0 4em;
		}
	}

/******************************************************************************/
/*                                Sectioning                                  */
/******************************************************************************/

/** Headings ******************************************************************/

	h1, h2, h3, h4, h5, h6, dt {
		page-break-after: avoid;
		page-break-inside: avoid;
		font: 100% sans-serif;   /* Reset all font styling to clear out UA styles */
		font-family: inherit;	/* Inherit the font family. */
		line-height: 1.2;		/* Keep wrapped headings compact */
		hyphens: manual;		/* Hyphenated headings look weird */
	}

	h2, h3, h4, h5, h6 {
		margin-top: 3rem;
	}

	h1, h2, h3 {
		color: #005A9C;
		color: var(--heading-text);
	}

	h1 { font-size: 170%; }
	h2 { font-size: 140%; }
	h3 { font-size: 120%; }
	h4 { font-weight: bold; }
	h5 { font-style: italic; }
	h6 { font-variant: small-caps; }
	dt { font-weight: bold; }

/** Subheadings ***************************************************************/

	h1 + h2,
	#profile-and-date {
		/* #profile-and-date is a subtitle in an H2 under the H1 */
		margin-top: 0;
	}
	h2 + h3,
	h3 + h4,
	h4 + h5,
	h5 + h6 {
		margin-top: 1.2em; /* = 1 x line-height */
	}

/** Section divider ***********************************************************/

	:not(.head) > :not(.head) + hr {
		font-size: 1.5em;
		text-align: center;
		margin: 1em auto;
		height: auto;
		color: black;
		color: var(--hr-text);
		border: transparent solid 0;
		background: transparent;
	}
	:not(.head) > hr::before {
		content: "\2727\2003\2003\2727\2003\2003\2727";
	}

/******************************************************************************/
/*                            Paragraphs and Lists                            */
/******************************************************************************/

	p {
		margin: 1em 0;
	}

	dd > p:first-child,
	li > p:first-child {
		margin-top: 0;
	}

	ul, ol {
		margin-left: 0;
		padding-left: 2em;
	}

	li {
		margin: 0.25em 0 0.5em;
		padding: 0;
	}

	dl dd {
		margin: 0 0 .5em 2em;
	}

	.head dd + dd { /* compact for header */
		margin-top: -.5em;
	}

	/* Style for algorithms */
	ol.algorithm ol:not(.algorithm),
	.algorithm > ol ol:not(.algorithm) {
	border-left: 0.5em solid #DEF;
	border-left: 0.5em solid var(--algo-border);
	}

	/* Put nice boxes around each algorithm. */
	[data-algorithm]:not(.heading) {
	 padding: .5em;
	 border: thin solid #ddd;
	 border: thin solid var(--algo-border);
	 border-radius: .5em;
	 margin: .5em calc(-0.5em - 1px);
	}
	[data-algorithm]:not(.heading) > :first-child {
	 margin-top: 0;
	}
	[data-algorithm]:not(.heading) > :last-child {
	 margin-bottom: 0;
	}

	/* Style for switch/case <dl>s */
	dl.switch > dd > ol.only,
	dl.switch > dd > .only > ol {
	margin-left: 0;
	}
	dl.switch > dd > ol.algorithm,
	dl.switch > dd > .algorithm > ol {
	margin-left: -2em;
	}
	dl.switch {
	padding-left: 2em;
	}
	dl.switch > dt {
	text-indent: -1.5em;
	margin-top: 1em;
	}
	dl.switch > dt + dt {
	margin-top: 0;
	}
	dl.switch > dt::before {
	content: '\21AA';
	padding: 0 0.5em 0 0;
	display: inline-block;
	width: 1em;
	text-align: right;
	line-height: 0.5em;
	}

/** Terminology Markup ********************************************************/


/******************************************************************************/
/*                                 Inline Markup                              */
/******************************************************************************/

/** Terminology Markup ********************************************************/
	dfn   { /* Defining instance */
		font-weight: bolder;
	}
	a > i { /* Instance of term */
		font-style: normal;
	}
	dt dfn code, code.idl {
		font-size: inherit;
	}
	dfn var {
		font-style: normal;
	}

/** Change Marking ************************************************************/

	del {
		color: #aa0000;
		color: var(--del-text);
		background: transparent;
		background: var(--del-bg);
		text-decoration: line-through;
	}
	ins {
		color: #006100;
		color: var(--ins-text);
		background: transparent;
		background: var(--ins-bg);
		text-decoration: underline;
	}

	/* for amendments (candidate/proposed changes) */

	.amendment ins, .correction ins, .addition ins,
	ins[class^=c] {
		text-decoration-style: dotted;
	}
	.amendment del, .correction del, .addition del,
	del[class^=c] {
		text-decoration-style: dotted;
	}
	.amendment.proposed ins, .correction.proposed ins, .addition.proposed ins,
	ins[class^=c].proposed {
		text-decoration-style: double;
	}
	.amendment.proposed del, .correction.proposed del, .addition.proposed del,
	del[class^=c].proposed {
		text-decoration-style: double;
	}

/** Miscellaneous improvements to inline formatting ***************************/

	sup {
		vertical-align: super;
		font-size: 80%
	}

/******************************************************************************/
/*                                    Code                                    */
/******************************************************************************/

/** General monospace/pre rules ***********************************************/

	pre, code, samp {
		font-family: Menlo, Consolas, "DejaVu Sans Mono", Monaco, monospace;
		font-size: .9em;
		hyphens: none;
		text-transform: none;
		text-align: left;
		text-align: start;
		font-variant: normal;
		orphans: 3;
		widows: 3;
		page-break-before: avoid;
	}
	pre code,
	code code {
		font-size: 100%;
	}

	pre {
		margin-top: 1em;
		margin-bottom: 1em;
		overflow: auto;
	}

/** Inline Code fragments *****************************************************/

	/* Do something nice. */

/******************************************************************************/
/*                                    Links                                   */
/******************************************************************************/

/** General Hyperlinks ********************************************************/

	/* We hyperlink a lot, so make it less intrusive */
	a[href] {
		color: #034575;
		color: var(--a-normal-text);
		text-decoration: underline #707070;
		text-decoration: underline var(--a-normal-underline);
		text-decoration-skip-ink: none;
	}
	a:visited {
		color: #034575;
		color: var(--a-visited-text);
		text-decoration-color: #bbb;
		text-decoration-color: var(--a-visited-underline);
	}

	/* Indicate interaction with the link */
	a[href]:focus,
	a[href]:hover {
		text-decoration-thickness: 2px;
	}
	a[href]:active {
		color: #c00;
		color: var(--a-active-text);
		text-decoration-color: #c00;
		text-decoration-color: var(--a-active-underline);
	}

	/* Backout above styling for W3C logo */
	.head .logo,
	.head .logo a {
		border: none;
		text-decoration: none;
		background: transparent;
	}

/******************************************************************************/
/*                                    Images                                  */
/******************************************************************************/

	img {
		border-style: none;
	}

	img, svg {
		/* Intentionally not color-scheme aware. */
		background: white;
	}

	/* For autogen numbers, add
	  .caption::before, figcaption::before { content: "Figure " counter(figure) ". "; }
	*/

	figure, .figure, .sidefigure {
		page-break-inside: avoid;
		text-align: center;
		margin: 2.5em 0;
	}
	.figure img,	.sidefigure img,	figure img,
	.figure object, .sidefigure object, figure object {
		max-width: 100%;
		margin: auto;
		height: auto;
	}
	.figure pre, .sidefigure pre, figure pre {
		text-align: left;
		display: table;
		margin: 1em auto;
	}
	.figure table, figure table {
		margin: auto;
	}
	@media screen and (min-width: 20em) {
		.sidefigure {
			float: right;
			width: 50%;
			margin: 0 0 0.5em 0.5em;
		}
	}
	.caption, figcaption, caption {
		font-style: italic;
		font-size: 90%;
	}
	.caption::before, figcaption::before, figcaption > .marker {
		font-weight: bold;
	}
	.caption, figcaption {
		counter-increment: figure;
	}

	/* DL list is indented 2em, but figure inside it is not */
	dd > .figure, dd > figure { margin-left: -2em; }

/******************************************************************************/
/*                             Colored Boxes                                  */
/******************************************************************************/

	.issue, .note, .example, .assertion, .advisement, blockquote,
	.amendment, .correction, .addition {
		margin: 1em auto;
		padding: .5em;
		border: .5em;
		border-left-style: solid;
		page-break-inside: avoid;
	}
	span.issue, span.note {
		padding: .1em .5em .15em;
		border-right-style: solid;
	}

	blockquote > :first-child,
	.note  > p:first-child,
	.issue > p:first-child,
	.amendment > p:first-child,
	.correction > p:first-child,
	.addition > p:first-child {
		margin-top: 0;
	}
	blockquote > :last-child,
	.note  > p:last-child,
	.issue > p:last-child,
	.amendment > p:last-child,
	.correction > p:last-child,
	.addition > p:last-child {
		margin-bottom: 0;
	}


	.issue::before, .issue > .marker,
	.example::before, .example > .marker,
	.note::before, .note > .marker,
	details.note > summary > .marker,
	.amendment::before, .amendment > .marker,
	details.amendment > summary > .marker,
	.addition::before, .addition > .marker,
	addition.amendment > summary > .marker,
	.correction::before, .correction > .marker,
	correction.amendment > summary > .marker
	{
		text-transform: uppercase;
		padding-right: 1em;
	}

	.example::before, .example > .marker {
		display: block;
		padding-right: 0em;
	}

/** Blockquotes ***************************************************************/

	blockquote {
		border-color: silver;
		border-color: var(--blockquote-border);
		background: transparent;
		background: var(--blockquote-bg);
		color: currentcolor;
		color: var(--blockquote-text);
	}

/** Open issue ****************************************************************/

	.issue {
		border-color: #e05252;
		border-color: var(--issue-border);
		background: #fbe9e9;
		background: var(--issue-bg);
		color: black;
		color: var(--issue-text);
		counter-increment: issue;
		overflow: auto;
	}
	.issue::before, .issue > .marker {
		color: #831616;
		color: var(--issueheading-text);
	}
	/* Add .issue::before { content: "Issue " counter(issue) " "; } for autogen numbers,
	  or use class="marker" to mark up the issue number in source. */

/** Example *******************************************************************/

	.example {
		border-color: #e0cb52;
		border-color: var(--example-border);
		background: #fcfaee;
		background: var(--example-bg);
		color: black;
		color: var(--example-text);
		counter-increment: example;
		overflow: auto;
		clear: both;
	}
	.example::before, .example > .marker {
		color: #574b0f;
		color: var(--exampleheading-text);
	}
	/* Add .example::before { content: "Example " counter(example) " "; } for autogen numbers,
	  or use class="marker" to mark up the example number in source. */

/** Non-normative Note ********************************************************/

	.note {
		border-color: #52e052;
		border-color: var(--note-border);
		background: #e9fbe9;
		background: var(--note-bg);
		color: black;
		color: var(--note-text);
		overflow: auto;
	}

	.note::before, .note > .marker,
	details.note > summary {
		color: hsl(120, 70%, 30%);
		color: var(--noteheading-text);
	}
	/* Add .note::before { content: "Note "; } for autogen label,
	  or use class="marker" to mark up the label in source. */

	details.note[open] > summary {
		border-bottom: 1px silver solid;
		border-bottom: 1px var(--notesummary-underline) solid;
	}

/** Assertion Box *************************************************************/
	/*  for assertions in algorithms */

	.assertion {
		border-color: #AAA;
		border-color: var(--assertion-border);
		background: #EEE;
		background: var(--assertion-bg);
		color: black;
		color: var(--assertion-text);
	}

/** Advisement Box ************************************************************/
	/*  for attention-grabbing normative statements */

	.advisement {
		border-color: orange;
		border-color: var(--advisement-border);
		border-style: none solid;
		background: #fec;
		background: var(--advisement-bg);
		color: black;
		color: var(--advisement-text);
	}
	strong.advisement {
		display: block;
		text-align: center;
	}
	.advisement::before, .advisement > .marker {
		color: #b35f00;
		color: var(--advisementheading-text);
	}

/** Amendment Box *************************************************************/

	.amendment, .correction, .addition {
		border-color: #330099;
		border-color: var(--amendment-border);
		background: #F5F0FF;
		background: var(--amendment-bg);
		color: black;
		color: var(--amendment-text);
	}
	.amendment.proposed, .correction.proposed, .addition.proposed {
		border-style: solid;
		border-block-width: 0.25em;
	}
	.amendment::before, .amendment > .marker,
	details.amendment > summary::before, details.amendment > summary > .marker,
	.correction::before, .correction > .marker,
	details.correction > summary::before, details.correction > summary > .marker,
	.addition::before, .addition > .marker,
	details.addition > summary::before, details.addition > summary > .marker {
		color: #220066;
		color: var(--amendmentheading-text);
	}
	.amendment.proposed::before, .amendment.proposed > .marker,
	details.amendment.proposed > summary::before, details.amendment.proposed > summary > .marker,
	.correction.proposed::before, .correction.proposed > .marker,
	details.correction.proposed > summary::before, details.correction.proposed > summary > .marker,
	.addition.proposed::before, .addition.proposed > .marker,
	details.addition.proposed > summary::before, details.addition.proposed > summary > .marker {
		font-weight: bold;
	}

/** Spec Obsoletion Notice ****************************************************/
	/* obnoxious obsoletion notice for older/abandoned specs. */

	details {
		display: block;
	}
	summary {
		font-weight: bolder;
	}

	.annoying-warning:not(details),
	details.annoying-warning:not([open]) > summary,
	details.annoying-warning[open] {
		background: hsla(40,100%,50%,0.95);
		background: var(--warning-bg);
		color: black;
		color: var(--warning-text);
		padding: .75em 1em;
		border: red;
		border: var(--warning-border);
		border-style: solid none;
		box-shadow: 0 2px 8px black;
		text-align: center;
	}
	.annoying-warning :last-child {
		margin-bottom: 0;
	}

@media not print {
	details.annoying-warning[open] {
		position: fixed;
		left: 0;
		right: 0;
		bottom: 2em;
		z-index: 1000;
	}
}

	details.annoying-warning:not([open]) > summary {
		text-align: center;
	}

/** Entity Definition Boxes ***************************************************/

	.def {
		padding: .5em 1em;
		background: #def;
		background: var(--def-bg);
		margin: 1.2em 0;
		border-left: 0.5em solid #8ccbf2;
		border-left: 0.5em solid var(--def-border);
		color: black;
		color: var(--def-text);
	}

/******************************************************************************/
/*                                    Tables                                  */
/******************************************************************************/

	th, td {
		text-align: left;
		text-align: start;
	}

/** Property/Descriptor Definition Tables *************************************/

	table.def {
		/* inherits .def box styling, see above */
		width: 100%;
		border-spacing: 0;
	}

	table.def td,
	table.def th {
		padding: 0.5em;
		vertical-align: baseline;
		border-bottom: 1px solid #bbd7e9;
		border-bottom: 1px solid var(--defrow-border);
	}

	table.def > tbody > tr:last-child th,
	table.def > tbody > tr:last-child td {
		border-bottom: 0;
	}

	table.def th {
		font-style: italic;
		font-weight: normal;
		padding-left: 1em;
		width: 3em;
	}

	/* For when values are extra-complex and need formatting for readability */
	table td.pre {
		white-space: pre-wrap;
	}

	/* A footnote at the bottom of a def table */
	table.def td.footnote {
		padding-top: 0.6em;
	}
	table.def td.footnote::before {
		content: " ";
		display: block;
		height: 0.6em;
		width: 4em;
		border-top: thin solid;
	}

/** Data tables (and properly marked-up index tables) *************************/
	/*
		<table class="data"> highlights structural relationships in a table
		when correct markup is used (e.g. thead/tbody, th vs. td, scope attribute)

		Use class="complex data" for particularly complicated tables --
		(This will draw more lines: busier, but clearer.)

		Use class="long" on table cells with paragraph-like contents
		(This will adjust text alignment accordingly.)
		Alternately use class="longlastcol" on tables, to have the last column assume "long".
	*/

	table {
		word-wrap: normal;
		overflow-wrap: normal;
		hyphens: manual;
	}

	table.data,
	table.index {
		margin: 1em auto;
		border-collapse: collapse;
		border: hidden;
		width: 100%;
	}
	table.data caption,
	table.index caption {
		max-width: 50em;
		margin: 0 auto 1em;
	}

	table.data td,  table.data th,
	table.index td, table.index th {
		padding: 0.5em 1em;
		border-width: 1px;
		border-color: silver;
		border-color: var(--datacell-border);
		border-top-style: solid;
	}

	table.data thead td:empty {
		padding: 0;
		border: 0;
	}

	table.data  thead,
	table.index thead,
	table.data  tbody,
	table.index tbody {
		border-bottom: 2px solid;
	}

	table.data colgroup,
	table.index colgroup {
		border-left: 2px solid;
	}

	table.data  tbody th:first-child,
	table.index tbody th:first-child  {
		border-right: 2px solid;
		border-top: 1px solid silver;
		border-top: 1px solid var(--datacell-border);
		padding-right: 1em;
	}

	table.data th[colspan],
	table.data td[colspan] {
		text-align: center;
	}

	table.complex.data th,
	table.complex.data td {
		border: 1px solid silver;
		border: 1px solid var(--datacell-border);
		text-align: center;
	}

	table.data.longlastcol td:last-child,
	table.data td.long {
		vertical-align: baseline;
		text-align: left;
	}

	table.data img {
		vertical-align: middle;
	}


/*
Alternate table alignment rules

	table.data,
	table.index {
		text-align: center;
	}

	table.data  thead th[scope="row"],
	table.index thead th[scope="row"] {
		text-align: right;
	}

	table.data  tbody th:first-child,
	table.index tbody th:first-child  {
		text-align: right;
	}

Possible extra rowspan handling

	table.data  tbody th[rowspan]:not([rowspan='1']),
	table.index tbody th[rowspan]:not([rowspan='1']),
	table.data  tbody td[rowspan]:not([rowspan='1']),
	table.index tbody td[rowspan]:not([rowspan='1']) {
		border-left: 1px solid silver;
	}

	table.data  tbody th[rowspan]:first-child,
	table.index tbody th[rowspan]:first-child,
	table.data  tbody td[rowspan]:first-child,
	table.index tbody td[rowspan]:first-child{
		border-left: 0;
		border-right: 1px solid silver;
	}
*/

/******************************************************************************/
/*                                  Indices                                   */
/******************************************************************************/


/** Table of Contents *********************************************************/

	.toc a {
		/* More spacing; use padding to make it part of the click target. */
		padding: 0.1rem 1px 0;
		/* Larger, more consistently-sized click target */
		display: block;
		/* Switch to using border-bottom for underlines */
		text-decoration: none;
		border-bottom: 1px solid;
		/* Reverse color scheme */
		color: black;
		color: var(--toclink-text);
		border-color: #3980b5;
		border-color: var(--toclink-underline);
	}
	.toc a:visited {
		color: black;
		color: var(--toclink-visited-text);
		border-color: #054572;
		border-color: var(--toclink-visited-underline);
	}
	.toc a:focus,
	.toc a:hover {
		background: rgba(75%, 75%, 75%, .25);
		background: var(--a-hover-bg);
		border-bottom-width: 3px;
		margin-bottom: -2px;
	}
	.toc a:not(:focus):not(:hover) {
		/* Allow colors to cascade through from link styling */
		border-bottom-color: transparent;
	}

	.toc, .toc ol, .toc ul, .toc li {
		list-style: none; /* Numbers must be inlined into source */
		/* because generated content isn't search/selectable and markers can't do multilevel yet */
		margin:  0;
		padding: 0;
	}
	.toc {
		line-height: 1.1em;
	}

	/* ToC not indented until third level, but font style & margins show hierarchy */
	.toc > li			{ font-weight: bold;   }
	.toc > li li		 { font-weight: normal; }
	.toc > li li li	  { font-size:   95%;	}
	.toc > li li li li	{ font-size:   90%;	}
	.toc > li li li li li { font-size:   85%;	}

	/* @supports not (display:grid) { */
		.toc > li			{ margin: 1.5rem 0;	}
		.toc > li li		 { margin: 0.3rem 0;	}
		.toc > li li li	  { margin-left: 2rem;   }

		/* Section numbers in a column of their own */
		.toc .secno {
			float: left;
			width: 4rem;
			white-space: nowrap;
		}
		.toc > li li li li .secno { font-size: 85%; }
		.toc > li li li li li .secno { font-size: 100%; }

		.toc li {
			clear: both;
		}

		:not(li) > .toc			 { margin-left:  5rem; }
		.toc .secno				 { margin-left: -5rem; }
		.toc > li li li .secno	  { margin-left: -7rem; }
		.toc > li li li li .secno	{ margin-left: -9rem; }
		.toc > li li li li li .secno { margin-left: -11rem; }

		/* Tighten up indentation in narrow ToCs */
		@media (max-width: 30em) {
			:not(li) > .toc			 { margin-left:  4rem; }
			.toc .secno				 { margin-left: -4rem; }
			.toc > li li li			 { margin-left:  1rem; }
			.toc > li li li .secno	  { margin-left: -5rem; }
			.toc > li li li li .secno	{ margin-left: -6rem; }
			.toc > li li li li li .secno { margin-left: -7rem; }
		}
		/* Loosen it on wide screens */
		@media screen and (min-width: 78em) {
			body:not(.toc-inline) :not(li) > .toc			 { margin-left:  4rem; }
			body:not(.toc-inline) .toc .secno				 { margin-left: -4rem; }
			body:not(.toc-inline) .toc > li li li			 { margin-left:  1rem; }
			body:not(.toc-inline) .toc > li li li .secno	  { margin-left: -5rem; }
			body:not(.toc-inline) .toc > li li li li .secno	{ margin-left: -6rem; }
			body:not(.toc-inline) .toc > li li li li li .secno { margin-left: -7rem; }
	}
	/* } */

	@supports (display:grid) and (display:contents) {
		/* Use #toc over .toc to override non-@supports rules. */
		#toc {
			display: grid;
			align-content: start;
			grid-template-columns: auto 1fr;
			grid-column-gap: 1rem;
			column-gap: 1rem;
			grid-row-gap: .6rem;
			row-gap: .6rem;
		}
		#toc h2 {
			grid-column: 1 / -1;
			margin-bottom: 0;
		}
		#toc ol,
		#toc li,
		#toc a {
			display: contents;
			/* Switch <a> to subgrid when supported */
		}
		#toc span {
			margin: 0;
		}
		#toc > .toc > li > a > span {
			/* The spans of the top-level list,
			  comprising the first items of each top-level section. */
			margin-top: 1.1rem;
		}
		#toc#toc .secno { /* Ugh, need more specificity to override base.css */
			grid-column: 1;
			width: auto;
			margin-left: 0;
		}
		#toc .content {
			grid-column: 2;
			width: auto;
			margin-right: 1rem;
			border-bottom: 3px solid transparent;
			margin-bottom: -3px;
		}
		#toc .content:hover,
		#toc .content:focus {
			background: rgba(75%, 75%, 75%, .25);
			background: var(--a-hover-bg);
			border-bottom-color: #054572;
			border-bottom-color: var(--toclink-underline);
		}
		#toc li li li .content {
			margin-left: 1rem;
		}
		#toc li li li li .content {
			margin-left: 2rem;
		}
	}


/** Index *********************************************************************/

	/* Index Lists: Layout */
	ul.index	  { margin-left: 0; columns: 15em; text-indent: 1em hanging; }
	ul.index li	{ margin-left: 0; list-style: none; break-inside: avoid; }
	ul.index li li { margin-left: 1em; }
	ul.index dl	{ margin-top: 0; }
	ul.index dt	{ margin: .2em 0 .2em 20px;}
	ul.index dd	{ margin: .2em 0 .2em 40px;}
	/* Index Lists: Typography */
	ul.index ul,
	ul.index dl { font-size: smaller; }
	@media not print {
		ul.index li a + span {
			white-space: nowrap;
			color: transparent; }
		ul.index li a:hover + span,
		ul.index li a:focus + span {
			color: #707070;
			color: var(--indexinfo-text);
		}
	}

/** Index Tables *****************************************************/
	/* See also the data table styling section, which this effectively subclasses */

	table.index {
		font-size: small;
		border-collapse: collapse;
		border-spacing: 0;
		text-align: left;
		margin: 1em 0;
	}

	table.index td,
	table.index th {
		padding: 0.4em;
	}

	table.index tr:hover td:not([rowspan]),
	table.index tr:hover th:not([rowspan]) {
		color: black;
		color: var(--indextable-hover-text);
		background: #f7f8f9;
		background: var(--indextable-hover-bg);
	}

	/* The link in the first column in the property table (formerly a TD) */
	table.index th:first-child a {
		font-weight: bold;
	}

/** Outdated warning **********************************************************/

.outdated-spec {
	color: black;
	color: var(--outdatedspec-text);
	background-color: rgba(0,0,0,0.5);
	background-color: var(--outdatedspec-bg);
}

.outdated-warning {
	position: fixed;
	bottom: 50%;
	left: 0;
	right: 0;
	margin: 0 auto;
	width: 50%;
	background: maroon;
	background: var(--outdated-bg);
	color: white;
	color: var(--outdated-text);
	border-radius: 1em;
	box-shadow: 0 0 1em red;
	box-shadow: 0 0 1em var(--outdated-shadow);
	padding: 2em;
	text-align: center;
	z-index: 2;
}

.outdated-warning a {
	color: currentcolor;
	background: transparent;
}

.edited-rec-warning {
	background: darkorange;
	background: var(--editedrec-bg);
	box-shadow: 0 0 1em;
}

.outdated-warning button {
	color: var(--outdated-text);
	border-radius: 1em;
	box-shadow: 0 0 1em red;
	box-shadow: 0 0 1em var(--outdated-shadow);
	padding: 2em;
	text-align: center;
	z-index: 2;
}

.outdated-warning a {
	color: currentcolor;
	background: transparent;
}

.edited-rec-warning {
	background: darkorange;
	background: var(--editedrec-bg);
	box-shadow: 0 0 1em;
}

.outdated-warning button {
	position: absolute;
	top: 0;
	right:0;
	margin: 0;
	border: 0;
	padding: 0.25em 0.5em;
	background: transparent;
	color: white;
	color: var(--outdated-text);
	font:1em sans-serif;
	text-align:center;
}

.outdated-warning span {
	display: block;
}

.outdated-collapsed {
	bottom: 0;
	border-radius: 0;
	width: 100%;
	padding: 0;
}

/******************************************************************************/
/*                                    Print                                   */
/******************************************************************************/

	@media print {
		/* Pages have their own margins. */
		html {
			margin: 0;
		}
		/* Serif for print. */
		body {
			font-family: serif;
		}

		.outdated-warning {
			position: absolute;
			border-style: solid;
			border-color: red;
		}

		.outdated-warning input {
			display: none;
		}
	}
	@page {
		margin: 1.5cm 1.1cm;
	}



/******************************************************************************/
/*                             Overflow Control                               */
/******************************************************************************/

	.figure .caption, .sidefigure .caption, figcaption {
		/* in case figure is overlarge, limit caption to 50em */
		max-width: 50rem;
		margin-left: auto;
		margin-right: auto;
	}
	.overlarge {
		/* Magic to create good item positioning:
		  "content column" is 50ems wide at max; less on smaller screens.
		  Extra space (after ToC + content) is empty on the right.

		  1. When item < content column, centers item in column.
		  2. When content < item < available, left-aligns.
		  3. When item > available, fills available + scroll bar.
		*/
		display: grid;
		grid-template-columns: minmax(0, 50em);
	}
	.overlarge > table {
		/* limit preferred width of table */
		max-width: 50em;
		margin-left: auto;
		margin-right: auto;
	}

	@media (min-width: 55em) {
		.overlarge {
			margin-right: calc(13px + 26.5rem - 50vw);
			max-width: none;
		}
	}
	@media screen and (min-width: 78em) {
		body:not(.toc-inline) .overlarge {
			/* 30.5em body padding 50em content area */
			margin-right: calc(40em - 50vw) !important;
		}
	}
	@media screen and (min-width: 90em) {
		body:not(.toc-inline) .overlarge {
			/* 4em html margin 30.5em body padding 50em content area */
			margin-right: calc(84.5em - 100vw) !important;
		}
	}

	@media not print {
		.overlarge {
			overflow-x: auto;
			/* See Lea Verou's explanation background-attachment:
			* http://lea.verou.me/2012/04/background-attachment-local/
			*
			background: top left  / 4em 100% linear-gradient(to right,  #ffffff, rgba(255, 255, 255, 0)) local,
						top right / 4em 100% linear-gradient(to left, #ffffff, rgba(255, 255, 255, 0)) local,
						top left  / 1em 100% linear-gradient(to right,  #c3c3c5, rgba(195, 195, 197, 0)) scroll,
						top right / 1em 100% linear-gradient(to left, #c3c3c5, rgba(195, 195, 197, 0)) scroll,
						white;
			background-repeat: no-repeat;
			*/
		}
	}
</style>
<style>
    table, th, td {
      border: 1px solid black;
      border-collapse: collapse;
      vertical-align: top;
    }
    th, td {
      border-left: none;
      border-right: none;
      padding: 0px 10px;
    }
    th {
      text-align: center;
    }

    del { background: #fcc; color: #000; text-decoration: line-through; }
    ins { background: #cfc; color: #000; }
    blockquote .highlight:not(.idl) { background: initial; margin: initial; padding: 0.5em }
    blockquote ul { background: inherit; }
    blockquote code.highlight:not(.idl) { padding: initial; }
    blockquote c-[a] { color: inherit; } /* Keyword.Declaration */
    blockquote c-[b] { color: inherit; } /* Keyword.Type */
    blockquote c-[c] { color: inherit; } /* Comment */
    blockquote c-[d] { color: inherit; } /* Comment.Multiline */
    blockquote c-[e] { color: inherit; } /* Name.Attribute */
    blockquote c-[f] { color: inherit; } /* Name.Tag */
    blockquote c-[g] { color: inherit; } /* Name.Variable */
    blockquote c-[k] { color: inherit; } /* Keyword */
    blockquote c-[l] { color: inherit; } /* Literal */
    blockquote c-[m] { color: inherit; } /* Literal.Number */
    blockquote c-[n] { color: inherit; } /* Name */
    blockquote c-[o] { color: inherit; } /* Operator */
    blockquote c-[p] { color: inherit; } /* Punctuation */
    blockquote c-[s] { color: inherit; } /* Literal.String */
    blockquote c-[t] { color: inherit; } /* Literal.String.Single */
    blockquote c-[u] { color: inherit; } /* Literal.String.Double */
    blockquote c-[cp] { color: inherit; } /* Comment.Preproc */
    blockquote c-[c1] { color: inherit; } /* Comment.Single */
    blockquote c-[cs] { color: inherit; } /* Comment.Special */
    blockquote c-[kc] { color: inherit; } /* Keyword.Constant */
    blockquote c-[kn] { color: inherit; } /* Keyword.Namespace */
    blockquote c-[kp] { color: inherit; } /* Keyword.Pseudo */
    blockquote c-[kr] { color: inherit; } /* Keyword.Reserved */
    blockquote c-[ld] { color: inherit; } /* Literal.Date */
    blockquote c-[nc] { color: inherit; } /* Name.Class */
    blockquote c-[no] { color: inherit; } /* Name.Constant */
    blockquote c-[nd] { color: inherit; } /* Name.Decorator */
    blockquote c-[ni] { color: inherit; } /* Name.Entity */
    blockquote c-[ne] { color: inherit; } /* Name.Exception */
    blockquote c-[nf] { color: inherit; } /* Name.Function */
    blockquote c-[nl] { color: inherit; } /* Name.Label */
    blockquote c-[nn] { color: inherit; } /* Name.Namespace */
    blockquote c-[py] { color: inherit; } /* Name.Property */
    blockquote c-[ow] { color: inherit; } /* Operator.Word */
    blockquote c-[mb] { color: inherit; } /* Literal.Number.Bin */
    blockquote c-[mf] { color: inherit; } /* Literal.Number.Float */
    blockquote c-[mh] { color: inherit; } /* Literal.Number.Hex */
    blockquote c-[mi] { color: inherit; } /* Literal.Number.Integer */
    blockquote c-[mo] { color: inherit; } /* Literal.Number.Oct */
    blockquote c-[sb] { color: inherit; } /* Literal.String.Backtick */
    blockquote c-[sc] { color: inherit; } /* Literal.String.Char */
    blockquote c-[sd] { color: inherit; } /* Literal.String.Doc */
    blockquote c-[se] { color: inherit; } /* Literal.String.Escape */
    blockquote c-[sh] { color: inherit; } /* Literal.String.Heredoc */
    blockquote c-[si] { color: inherit; } /* Literal.String.Interpol */
    blockquote c-[sx] { color: inherit; } /* Literal.String.Other */
    blockquote c-[sr] { color: inherit; } /* Literal.String.Regex */
    blockquote c-[ss] { color: inherit; } /* Literal.String.Symbol */
    blockquote c-[vc] { color: inherit; } /* Name.Variable.Class */
    blockquote c-[vg] { color: inherit; } /* Name.Variable.Global */
    blockquote c-[vi] { color: inherit; } /* Name.Variable.Instance */
    blockquote c-[il] { color: inherit; } /* Literal.Number.Integer.Long */
  </style>
  <meta content="Bikeshed version d5d58a306, updated Fri Jan 26 16:12:28 2024 -0800" name="generator">
  <link href="https://wg21.link/P3300" rel="canonical">
  <link href="https://isocpp.org/favicon.ico" rel="icon">
  <meta content="e6b8d7110efbdc1562be04dd6b4d82aa7af19d71" name="revision">
<style>
table, th, tr, td {
  border: 2px solid black !important;
}
@media (prefers-color-scheme: dark) {
  table, th, tr, td {
    border: 2px solid white !important;
  }
}
</style>
<style>/* Boilerplate: style-autolinks */
.css.css, .property.property, .descriptor.descriptor {
    color: var(--a-normal-text);
    font-size: inherit;
    font-family: inherit;
}
.css::before, .property::before, .descriptor::before {
    content: "‘";
}
.css::after, .property::after, .descriptor::after {
    content: "’";
}
.property, .descriptor {
    /* Don't wrap property and descriptor names */
    white-space: nowrap;
}
.type { /* CSS value <type> */
    font-style: italic;
}
pre .property::before, pre .property::after {
    content: "";
}
[data-link-type="property"]::before,
[data-link-type="propdesc"]::before,
[data-link-type="descriptor"]::before,
[data-link-type="value"]::before,
[data-link-type="function"]::before,
[data-link-type="at-rule"]::before,
[data-link-type="selector"]::before,
[data-link-type="maybe"]::before {
    content: "‘";
}
[data-link-type="property"]::after,
[data-link-type="propdesc"]::after,
[data-link-type="descriptor"]::after,
[data-link-type="value"]::after,
[data-link-type="function"]::after,
[data-link-type="at-rule"]::after,
[data-link-type="selector"]::after,
[data-link-type="maybe"]::after {
    content: "’";
}

[data-link-type].production::before,
[data-link-type].production::after,
.prod [data-link-type]::before,
.prod [data-link-type]::after {
    content: "";
}

[data-link-type=element],
[data-link-type=element-attr] {
    font-family: Menlo, Consolas, "DejaVu Sans Mono", monospace;
    font-size: .9em;
}
[data-link-type=element]::before { content: "<" }
[data-link-type=element]::after  { content: ">" }

[data-link-type=biblio] {
    white-space: pre;
}

@media (prefers-color-scheme: dark) {
    :root {
        --selflink-text: black;
        --selflink-bg: silver;
        --selflink-hover-text: white;
    }
}
</style>
<style>/* Boilerplate: style-colors */
/* Any --*-text not paired with a --*-bg is assumed to have a transparent bg */
:root {
    color-scheme: light dark;

    --text: black;
    --bg: white;

    --unofficial-watermark: url(https://www.w3.org/StyleSheets/TR/2016/logos/UD-watermark);

    --logo-bg: #1a5e9a;
    --logo-active-bg: #c00;
    --logo-text: white;

    --tocnav-normal-text: #707070;
    --tocnav-normal-bg: var(--bg);
    --tocnav-hover-text: var(--tocnav-normal-text);
    --tocnav-hover-bg: #f8f8f8;
    --tocnav-active-text: #c00;
    --tocnav-active-bg: var(--tocnav-normal-bg);

    --tocsidebar-text: var(--text);
    --tocsidebar-bg: #f7f8f9;
    --tocsidebar-shadow: rgba(0,0,0,.1);
    --tocsidebar-heading-text: hsla(203,20%,40%,.7);

    --toclink-text: var(--text);
    --toclink-underline: #3980b5;
    --toclink-visited-text: var(--toclink-text);
    --toclink-visited-underline: #054572;

    --heading-text: #005a9c;

    --hr-text: var(--text);

    --algo-border: #def;

    --del-text: red;
    --del-bg: transparent;
    --ins-text: #080;
    --ins-bg: transparent;

    --a-normal-text: #034575;
    --a-normal-underline: #bbb;
    --a-visited-text: var(--a-normal-text);
    --a-visited-underline: #707070;
    --a-hover-bg: rgba(75%, 75%, 75%, .25);
    --a-active-text: #c00;
    --a-active-underline: #c00;

    --blockquote-border: silver;
    --blockquote-bg: transparent;
    --blockquote-text: currentcolor;

    --issue-border: #e05252;
    --issue-bg: #fbe9e9;
    --issue-text: var(--text);
    --issueheading-text: #831616;

    --example-border: #e0cb52;
    --example-bg: #fcfaee;
    --example-text: var(--text);
    --exampleheading-text: #574b0f;

    --note-border: #52e052;
    --note-bg: #e9fbe9;
    --note-text: var(--text);
    --noteheading-text: hsl(120, 70%, 30%);
    --notesummary-underline: silver;

    --assertion-border: #aaa;
    --assertion-bg: #eee;
    --assertion-text: black;

    --advisement-border: orange;
    --advisement-bg: #fec;
    --advisement-text: var(--text);
    --advisementheading-text: #b35f00;

    --warning-border: red;
    --warning-bg: hsla(40,100%,50%,0.95);
    --warning-text: var(--text);

    --amendment-border: #330099;
    --amendment-bg: #F5F0FF;
    --amendment-text: var(--text);
    --amendmentheading-text: #220066;

    --def-border: #8ccbf2;
    --def-bg: #def;
    --def-text: var(--text);
    --defrow-border: #bbd7e9;

    --datacell-border: silver;

    --indexinfo-text: #707070;

    --indextable-hover-text: black;
    --indextable-hover-bg: #f7f8f9;

    --outdatedspec-bg: rgba(0, 0, 0, .5);
    --outdatedspec-text: black;
    --outdated-bg: maroon;
    --outdated-text: white;
    --outdated-shadow: red;

    --editedrec-bg: darkorange;
}

@media (prefers-color-scheme: dark) {
    :root {
        --text: #ddd;
        --bg: black;

        --unofficial-watermark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cg fill='%23100808' transform='translate(200 200) rotate(-45) translate(-200 -200)' stroke='%23100808' stroke-width='3'%3E%3Ctext x='50%25' y='220' style='font: bold 70px sans-serif; text-anchor: middle; letter-spacing: 6px;'%3EUNOFFICIAL%3C/text%3E%3Ctext x='50%25' y='305' style='font: bold 70px sans-serif; text-anchor: middle; letter-spacing: 6px;'%3EDRAFT%3C/text%3E%3C/g%3E%3C/svg%3E");

        --logo-bg: #1a5e9a;
        --logo-active-bg: #c00;
        --logo-text: white;

        --tocnav-normal-text: #999;
        --tocnav-normal-bg: var(--bg);
        --tocnav-hover-text: var(--tocnav-normal-text);
        --tocnav-hover-bg: #080808;
        --tocnav-active-text: #f44;
        --tocnav-active-bg: var(--tocnav-normal-bg);

        --tocsidebar-text: var(--text);
        --tocsidebar-bg: #080808;
        --tocsidebar-shadow: rgba(255,255,255,.1);
        --tocsidebar-heading-text: hsla(203,20%,40%,.7);

        --toclink-text: var(--text);
        --toclink-underline: #6af;
        --toclink-visited-text: var(--toclink-text);
        --toclink-visited-underline: #054572;

        --heading-text: #8af;

        --hr-text: var(--text);

        --algo-border: #456;

        --del-text: #f44;
        --del-bg: transparent;
        --ins-text: #4a4;
        --ins-bg: transparent;

        --a-normal-text: #6af;
        --a-normal-underline: #555;
        --a-visited-text: var(--a-normal-text);
        --a-visited-underline: var(--a-normal-underline);
        --a-hover-bg: rgba(25%, 25%, 25%, .2);
        --a-active-text: #f44;
        --a-active-underline: var(--a-active-text);

        --borderedblock-bg: rgba(255, 255, 255, .05);

        --blockquote-border: silver;
        --blockquote-bg: var(--borderedblock-bg);
        --blockquote-text: currentcolor;

        --issue-border: #e05252;
        --issue-bg: var(--borderedblock-bg);
        --issue-text: var(--text);
        --issueheading-text: hsl(0deg, 70%, 70%);

        --example-border: hsl(50deg, 90%, 60%);
        --example-bg: var(--borderedblock-bg);
        --example-text: var(--text);
        --exampleheading-text: hsl(50deg, 70%, 70%);

        --note-border: hsl(120deg, 100%, 35%);
        --note-bg: var(--borderedblock-bg);
        --note-text: var(--text);
        --noteheading-text: hsl(120, 70%, 70%);
        --notesummary-underline: silver;

        --assertion-border: #444;
        --assertion-bg: var(--borderedblock-bg);
        --assertion-text: var(--text);

        --advisement-border: orange;
        --advisement-bg: #222218;
        --advisement-text: var(--text);
        --advisementheading-text: #f84;

        --warning-border: red;
        --warning-bg: hsla(40,100%,20%,0.95);
        --warning-text: var(--text);

        --amendment-border: #330099;
        --amendment-bg: #080010;
        --amendment-text: var(--text);
        --amendmentheading-text: #cc00ff;

        --def-border: #8ccbf2;
        --def-bg: #080818;
        --def-text: var(--text);
        --defrow-border: #136;

        --datacell-border: silver;

        --indexinfo-text: #aaa;

        --indextable-hover-text: var(--text);
        --indextable-hover-bg: #181818;

        --outdatedspec-bg: rgba(255, 255, 255, .5);
        --outdatedspec-text: black;
        --outdated-bg: maroon;
        --outdated-text: white;
        --outdated-shadow: red;

        --editedrec-bg: darkorange;
    }
    /* In case a transparent-bg image doesn't expect to be on a dark bg,
       which is quite common in practice... */
    img { background: white; }
}
</style>
<style>/* Boilerplate: style-counters */
body {
    counter-reset: example figure issue;
}
.issue {
    counter-increment: issue;
}
.issue:not(.no-marker)::before {
    content: "Issue " counter(issue);
}

.example {
    counter-increment: example;
}
.example:not(.no-marker)::before {
    content: "Example " counter(example);
}
.invalid.example:not(.no-marker)::before,
.illegal.example:not(.no-marker)::before {
    content: "Invalid Example" counter(example);
}

figcaption {
    counter-increment: figure;
}
figcaption:not(.no-marker)::before {
    content: "Figure " counter(figure) " ";
}
</style>
<style>/* Boilerplate: style-dfn-panel */
:root {
    --dfnpanel-bg: #ddd;
    --dfnpanel-text: var(--text);
    --dfnpanel-target-bg: #ffc;
    --dfnpanel-target-outline: orange;
}
@media (prefers-color-scheme: dark) {
    :root {
        --dfnpanel-bg: #222;
        --dfnpanel-text: var(--text);
        --dfnpanel-target-bg: #333;
        --dfnpanel-target-outline: silver;
    }
}
.dfn-panel {
    position: absolute;
    z-index: 35;
    width: 20em;
    width: 300px;
    height: auto;
    max-height: 500px;
    overflow: auto;
    padding: 0.5em 0.75em;
    font: small Helvetica Neue, sans-serif, Droid Sans Fallback;
    background: var(--dfnpanel-bg);
    color: var(--dfnpanel-text);
    border: outset 0.2em;
    white-space: normal; /* in case it's moved into a pre */
}
.dfn-panel:not(.on) { display: none; }
.dfn-panel * { margin: 0; padding: 0; text-indent: 0; }
.dfn-panel > b { display: block; }
.dfn-panel a { color: var(--dfnpanel-text); }
.dfn-panel a:not(:hover) { text-decoration: none !important; border-bottom: none !important; }
.dfn-panel a:focus {
    outline: 5px auto Highlight;
    outline: 5px auto -webkit-focus-ring-color;
}
.dfn-panel > b + b { margin-top: 0.25em; }
.dfn-panel ul { padding: 0 0 0 1em; list-style: none; }
.dfn-panel li a {
    max-width: calc(300px - 1.5em - 1em);
    overflow: hidden;
    text-overflow: ellipsis;
}

.dfn-panel.activated {
    display: inline-block;
    position: fixed;
    left: 8px;
    bottom: 2em;
    margin: 0 auto;
    max-width: calc(100vw - 1.5em - .4em - .5em);
    max-height: 30vh;
    transition: left 1s ease-out, bottom 1s ease-out;
}

.dfn-panel .link-item:hover {
    text-decoration: underline;
}
.dfn-panel .link-item .copy-icon {
    opacity: 0;
}
.dfn-panel .link-item:hover .copy-icon,
.dfn-panel .link-item .copy-icon:focus {
    opacity: 1;
}

.dfn-panel .copy-icon {
    display: inline-block;
    margin-right: 0.5em;
    width: 0.85em;
    height: 1em;
    border-radius: 3px;
    background-color: #ccc;
    cursor: pointer;
}

.dfn-panel .copy-icon .icon {
    width: 100%;
    height: 100%;
    background-color: #fff;
    display: flex;
    justify-content: center;
    align-items: center;
    position: relative;
}

.dfn-panel .copy-icon .icon::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    border: 1px solid black;
    background-color: #ccc;
    opacity: 0.25;
    transform: translate(3px, -3px);
}

.dfn-panel .copy-icon:active .icon::before {
    opacity: 1;
}

.dfn-paneled[role="button"] { cursor: help; }

.highlighted {
    animation: target-fade 3s;
}

@keyframes target-fade {
    from {
        background-color: var(--dfnpanel-target-bg);
        outline: 5px solid var(--dfnpanel-target-outline);
    }
    to {
        color: var(--a-normal-text);
        background-color: transparent;
        outline: transparent;
    }
}
</style>
<style>/* Boilerplate: style-issues */
a[href].issue-return {
    float: right;
    float: inline-end;
    color: var(--issueheading-text);
    font-weight: bold;
    text-decoration: none;
}
</style>
<style>/* Boilerplate: style-md-lists */
/* This is a weird hack for me not yet following the commonmark spec
   regarding paragraph and lists. */
[data-md] > :first-child {
    margin-top: 0;
}
[data-md] > :last-child {
    margin-bottom: 0;
}
</style>
<style>/* Boilerplate: style-selflinks */
:root {
    --selflink-text: white;
    --selflink-bg: gray;
    --selflink-hover-text: black;
}
.heading, .issue, .note, .example, li, dt {
    position: relative;
}
a.self-link {
    position: absolute;
    top: 0;
    left: calc(-1 * (3.5rem - 26px));
    width: calc(3.5rem - 26px);
    height: 2em;
    text-align: center;
    border: none;
    transition: opacity .2s;
    opacity: .5;
}
a.self-link:hover {
    opacity: 1;
}
.heading > a.self-link {
    font-size: 83%;
}
.example > a.self-link,
.note > a.self-link,
.issue > a.self-link {
    /* These blocks are overflow:auto, so positioning outside
       doesn't work. */
    left: auto;
    right: 0;
}
li > a.self-link {
    left: calc(-1 * (3.5rem - 26px) - 2em);
}
dfn > a.self-link {
    top: auto;
    left: auto;
    opacity: 0;
    width: 1.5em;
    height: 1.5em;
    background: var(--selflink-bg);
    color: var(--selflink-text);
    font-style: normal;
    transition: opacity .2s, background-color .2s, color .2s;
}
dfn:hover > a.self-link {
    opacity: 1;
}
dfn > a.self-link:hover {
    color: var(--selflink-hover-text);
}

a.self-link::before            { content: "¶"; }
.heading > a.self-link::before { content: "§"; }
dfn > a.self-link::before      { content: "#"; }
</style>
 <body class="h-entry">
  <div class="head">
   <p data-fill-with="logo"></p>
   <h1 class="p-name no-ref" id="title">P3300R0<br>C++ Asynchronous Parallel Algorithms</h1>
   <h2 class="no-num no-toc no-ref heading settled" id="profile-and-date"><span class="content">Published Proposal, <time class="dt-updated" datetime="2024-02-15">2024-02-15</time></span></h2>
   <div data-fill-with="spec-metadata">
    <dl>
     <dt class="editor">Author:
     <dd class="editor p-author h-card vcard"><a class="p-name fn u-email email" href="mailto:brycelelbach@gmail.com">Bryce Adelstein Lelbach (he/him/his)</a> (<span class="p-org org">NVIDIA</span>)
     <dt>Source:
     <dd><a href="https://github.com/brycelelbach/wg21_p3300_cpp_asynchronous_parallel_algorithms/blob/main/cpp_asynchronous_parallel_algorithms.bs">GitHub</a>
     <dt>Issue Tracking:
     <dd><a href="https://github.com/brycelelbach/wg21_p3300_cpp_asynchronous_parallel_algorithms/issues">GitHub</a>
     <dt>Project:
     <dd>ISO/IEC 14882 Programming Languages — C++, ISO/IEC JTC1/SC22/WG21
     <dt>Audience:
     <dd>WG21
    </dl>
   </div>
   <div data-fill-with="warning"></div>
   <hr title="Separator for header">
  </div>
  <nav data-fill-with="table-of-contents" id="toc">
   <h2 class="no-num no-toc no-ref" id="contents">Table of Contents</h2>
   <ol class="toc" role="directory">
    <li><a href="#introduction"><span class="secno">1</span> <span class="content">Introduction</span></a>
    <li><a href="#terminology"><span class="secno">2</span> <span class="content">Terminology</span></a>
    <li>
     <a href="#design"><span class="secno">3</span> <span class="content">Design</span></a>
     <ol class="toc">
      <li><a href="#design-compositional-utilities"><span class="secno">3.1</span> <span class="content">Compositional Utilities</span></a>
     </ol>
    <li>
     <a href="#examples"><span class="secno">4</span> <span class="content">Examples</span></a>
     <ol class="toc">
      <li><a href="#examples-basics"><span class="secno">4.1</span> <span class="content">Basics</span></a>
      <li><a href="#examples-normalize"><span class="secno">4.2</span> <span class="content">Normalize</span></a>
      <li><a href="#examples-minimum-difference"><span class="secno">4.3</span> <span class="content">Minimum Difference</span></a>
      <li><a href="#examples-rain-water"><span class="secno">4.4</span> <span class="content">Rain Water</span></a>
      <li><a href="#examples-upper-triangular-cholesky-factorization"><span class="secno">4.5</span> <span class="content">Upper Triangular Cholesky Factorization</span></a>
     </ol>
    <li>
     <a href="#alternatives"><span class="secno">5</span> <span class="content">Alternatives</span></a>
     <ol class="toc">
      <li><a href="#alternatives-asynchronous-parameters"><span class="secno">5.1</span> <span class="content">Asynchronous Parameters and Logical Return Types</span></a>
      <li><a href="#alternatives-attach-to-execution-policeis-or-schedulers"><span class="secno">5.2</span> <span class="content">Attach Execution Policies to Schedulers or Vice Versa</span></a>
      <li><a href="#alternatives-explicit-properties"><span class="secno">5.3</span> <span class="content">Require Explicit Execution Policy and/or Scheduler Parameters</span></a>
     </ol>
    <li>
     <a href="#issues"><span class="secno">6</span> <span class="content">Issues</span></a>
     <ol class="toc">
      <li><a href="#issues-dynamic-asynchrony"><span class="secno">6.1</span> <span class="content">Dynamic Asynchrony</span></a>
      <li><a href="#issues-lifetimes"><span class="secno">6.2</span> <span class="content">Lifetimes</span></a>
     </ol>
    <li>
     <a href="#index"><span class="secno"></span> <span class="content">Index</span></a>
     <ol class="toc">
      <li><a href="#index-defined-here"><span class="secno"></span> <span class="content">Terms defined by this specification</span></a>
     </ol>
    <li>
     <a href="#references"><span class="secno"></span> <span class="content">References</span></a>
     <ol class="toc">
      <li><a href="#informative"><span class="secno"></span> <span class="content">Informative References</span></a>
     </ol>
   </ol>
  </nav>
  <main>
   <h2 class="heading settled" data-level="1" id="introduction"><span class="secno">1. </span><span class="content">Introduction</span><a class="self-link" href="#introduction"></a></h2>
   <p>This paper outlines a unified design for asynchronous parallel algorithms and
  scheduler-aware synchronous parallel algorithms for Standard C++.</p>
   <p>In C++17, we introduced parallel versions of the standard algorithms, which
  take an execution policy that describes what form of parallelism is allowed, if
  any.
These execution policy parallel algorithms are powerful, but have two major
  limitations:</p>
   <ul>
    <li data-md>
     <p>They are synchronous; they launch the parallel work, and then block the
calling execution agent until the work completes.</p>
    <li data-md>
     <p>There is no way to specify where parallel work is executed.</p>
    <li data-md>
     <p>There is no way to pass tuning knobs and other parameters for execution.</p>
   </ul>
   <p>In C++26, we plan to introduce <a data-link-type="biblio" href="https://wg21.link/p2300r7" title="`std::execution`">senders and schedulers</a>, C++'s framework for
  asynchrony and controlling execution.
With senders and schedulers, we can address both of the shortcomings of the
  execution policy parallel algorithms.
We can add new asynchronous parallel algorithms that can be composed together
  and run on any scheduler.
We can also add new versions of the synchronous parallel algorithms whose
  execution can be controlled by schedulers.</p>
   <h2 class="heading settled" data-level="2" id="terminology"><span class="secno">2. </span><span class="content">Terminology</span><a class="self-link" href="#terminology"></a></h2>
   <p><dfn class="dfn-paneled" data-dfn-type="dfn" data-export id="serial-algorithm"><b>serial algorithm</b></dfn><br> A version of an algorithm that does not take or return an execution policy, sender, or scheduler. E.g. All of the standard algorithms prior to C++17.</p>
   <p><dfn class="dfn-paneled" data-dfn-type="dfn" data-export id="parallel-algorithm"><b>parallel algorithm</b></dfn><br> A version of an algorithm that either returns a sender or takes an execution policy, sender, or scheduler. It does not necessarily execute in parallel.</p>
   <p><dfn class="dfn-paneled" data-dfn-type="dfn" data-export id="synchronous-parallel-algorithm"><b>synchronous parallel algorithm</b></dfn><br> A parallel algorithm that blocks the calling execution agent until the parallel work that it has launched completes.</p>
   <p><dfn class="dfn-paneled" data-dfn-type="dfn" data-export id="synchronous-unscheduled-parallel-algorithm"><b>synchronous unscheduled parallel algorithm</b></dfn><br> A synchronous parallel algorithm that takes an execution policy and no sender or scheduler. Therefore, users cannot control where it executes. E.g. the C++17 parallel algorithms.</p>
   <p><dfn class="dfn-paneled" data-dfn-type="dfn" data-export id="synchronous-scheduled-parallel-algorithm"><b>synchronous scheduled parallel algorithm</b></dfn><br> A synchronous parallel algorithm that takes an execution policy and a sender or scheduler. Therefore, users can control where it executes.</p>
   <p><dfn class="dfn-paneled" data-dfn-type="dfn" data-export id="asynchronous-parallel-algorithm"><b>asynchronous parallel algorithm</b></dfn><br> A parallel algorithm that returns a sender and does not block pending the completion of the parallel work it launched.</p>
   <p><dfn class="dfn-paneled" data-dfn-type="dfn" data-export id="predecessor-sender"><b>predecessor sender</b></dfn><br> The single sender parameter of a synchronous scheduled parallel algorithm or an asynchronous parallel algorithm.</p>
   <h2 class="heading settled" data-level="3" id="design"><span class="secno">3. </span><span class="content">Design</span><a class="self-link" href="#design"></a></h2>
   <p>We will introduce two new types of parallel algorithms: synchronous scheduled
  and asynchronous.
Both will take a single sender parameter and no scheduler or execution policy
  parameter.
This sender represents prior work that must complete before the algorithm is
  evaluated.</p>
<pre class="highlight"><c- n>algorithm</c-><c- p>(</c-><c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>,</c-> <c- n>d</c-><c- p>);</c-> <c- c1>// Serial</c->
<c- n>algorithm</c-><c- p>(</c-><c- n>pol</c-><c- p>,</c-> <c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>,</c-> <c- n>d</c-><c- p>);</c-> <c- c1>// Synchronous unscheduled parallel (C++17)</c->
<c- n>T</c-> <c- n>t</c-> <c- o>=</c-> <c- n>algorithm</c-><c- p>(</c-><c- n>snd</c-><c- p>,</c-> <c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>,</c-> <c- n>d</c-><c- p>);</c-> <c- c1>// Synchronous scheduled parallel (New)</c->
<c- n>sender_of</c-><c- o>&lt;</c-><c- n>T</c-><c- o>></c-> <c- k>auto</c-> <c- n>s</c-> <c- o>=</c-> <c- n>async</c-><c- o>::</c-><c- n>algorithm</c-><c- p>(</c-><c- n>snd</c-><c- p>,</c-> <c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>,</c-> <c- n>d</c-><c- p>);</c-> <c- c1>// Asynchronous (New)</c->
</pre>
   <p>Senders have queryable attributes.
A parallel algorithm that takes a sender determines its scheduler and execution
  policy from the sender by querying the appropriate attribute.
A scheduler may be attached to a sender with <code class="highlight"><c- n>snd</c-> <c- o>=</c-> <c- n>schedule</c-><c- p>(</c-><c- n>sch</c-><c- p>)</c-></code> or <code class="highlight"><c- n>snd</c-> <c- o>|</c-> <c- n>transfer</c-><c- p>(</c-><c- n>sch</c-><c- p>)</c-></code>.
An execution policy may be attached to a sender with <code class="highlight"><c- n>snd</c-> <c- o>|</c-> <c- n>attach_execution_policy</c-><c- p>(</c-><c- n>pol</c-><c- p>)</c-></code>.
Both may be attached at the same time with <code class="highlight"><c- n>snd</c-> <c- o>=</c-> <c- n>schedule</c-><c- p>(</c-><c- n>sch</c-><c- p>,</c-> <c- n>pol</c-><c- p>)</c-></code>, <code class="highlight"><c- n>snd</c-> <c- o>|</c-> <c- n>transfer</c-><c- p>(</c-><c- n>sch</c-><c- p>,</c-> <c- n>pol</c-><c- p>)</c-></code>, or <code class="highlight"><c- n>on</c-><c- p>(</c-><c- n>sch</c-><c- p>,</c-> <c- n>pol</c-><c- p>,</c-> <c- n>snd</c-><c- p>)</c-></code>.</p>
   <p>If the execution policy attached cannot be satisfied by the scheduler, a
  compilation error occurs.
If no execution policy is attached and the scheduler has no default, a
  compilation error occurs.
If the sender passed to a synchronous scheduled parallel algorithm has no
  scheduler attached to it, a compilation error occurs.</p>
   <p>Synchronous scheduler parallel algorithms take a <code class="highlight"><c- n>sender_of</c-><c- o>&lt;</c-><c- b>void</c-><c- o>></c-></code>.
They are sender consumers - they will connect their predecessor sender, block
  pending its completion, and then return a non-sender value.
They must use <code class="highlight"><c- n>sync_wait</c-></code> to block pending the completion of both their
  predecessor sender and the work they create, to ensure that they block in a
  manner that can be customized by scheduler and sender authors.</p>
   <p>Asynchronous parallel algorithms are sender adaptors - they take a sender and
  return a sender.
The returned sender will send the result you would get by calling the serial
  algorithm (if it’s non-<code class="highlight"><c- b>void</c-></code>), followed by the values sent by the predecessor
  sender.</p>
<pre class="highlight"><c- n>sender_of</c-><c- o>&lt;</c-><c- n>T</c-><c- o>></c-> <c- k>auto</c-> <c- n>s0</c-> <c- o>=</c-> <c- n>async</c-><c- o>::</c-><c- n>algorithm</c-><c- p>(</c-><c- n>schedule</c-><c- p>(</c-><c- n>sch</c-><c- p>),</c-> <c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>,</c-> <c- n>d</c-><c- p>);</c->

<c- n>sender_of</c-><c- o>&lt;</c-><c- b>int</c-><c- p>,</c-> <c- b>bool</c-><c- o>></c-> <c- k>auto</c-> <c- n>s1</c-> <c- o>=</c-> <c- n>just</c-><c- p>(</c-><c- mi>17</c-><c- p>,</c-> true<c- p>);</c->
<c- n>sender_of</c-><c- o>&lt;</c-><c- n>T</c-><c- p>,</c-> <c- b>int</c-><c- p>,</c-> <c- b>bool</c-><c- o>></c-> <c- k>auto</c-> <c- n>s2</c-> <c- o>=</c-> <c- n>async</c-><c- o>::</c-><c- n>algorithm</c-><c- p>(</c-><c- n>s1</c-><c- p>,</c-> <c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>,</c-> <c- n>d</c-><c- p>);</c->
</pre>
   <p>Asynchronous parallel algorithms are pipeable.</p>
<pre class="highlight"><c- n>sender_of</c-><c- o>&lt;</c-><c- n>U</c-><c- o>></c-> <c- k>auto</c-> <c- n>s0</c-> <c- o>=</c-> <c- n>schedule</c-><c- p>(</c-><c- n>sch</c-><c- p>)</c->
                     <c- o>|</c-> <c- n>async</c-><c- o>::</c-><c- n>algorithm_x</c-><c- p>(</c-><c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>,</c-> <c- n>d</c-><c- p>)</c-> <c- c1>// Result is type `T`.</c->
                     <c- o>|</c-> <c- n>then</c-><c- p>([]</c-> <c- p>(</c-><c- n>T</c-> <c- n>t</c-><c- p>)</c-> <c- o>-></c-> <c- b>void</c-> <c- p>{</c-> <c- d>/* … */</c-> <c- p>})</c->
                     <c- o>|</c-> <c- n>async</c-><c- o>::</c-><c- n>algorithm_y</c-><c- p>(</c-><c- n>b</c-><c- p>,</c-> <c- n>e</c-><c- p>,</c-> <c- n>f</c-><c- p>);</c->   <c- c1>// Result is type `U`.</c->

<c- n>sender_of</c-><c- o>&lt;</c-><c- n>U</c-><c- p>,</c-> <c- n>V</c-><c- o>></c-> <c- k>auto</c-> <c- n>s1</c-> <c- o>=</c-> <c- n>just</c-><c- p>(</c-><c- mi>17</c-><c- p>,</c-> true<c- p>);</c->
                        <c- o>|</c-> <c- n>async</c-><c- o>::</c-><c- n>algorithm_x</c-><c- p>(</c-><c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>,</c-> <c- n>d</c-><c- p>)</c->
                        <c- o>|</c-> <c- n>then</c-><c- p>([]</c-> <c- p>(</c-><c- n>T</c-> <c- n>t</c-><c- p>,</c-> <c- b>int</c-> <c- n>i</c-><c- p>,</c-> <c- b>bool</c-> <c- n>b</c-><c- p>)</c-> <c- o>-></c-> <c- n>V</c-> <c- p>{</c-> <c- d>/* … */</c-> <c- p>})</c->
                        <c- o>|</c-> <c- n>async</c-><c- o>::</c-><c- n>algorithm_y</c-><c- p>(</c-><c- n>b</c-><c- p>,</c-> <c- n>e</c-><c- p>,</c-> <c- n>f</c-><c- p>);</c->
</pre>
   <p>The senders returned by asynchronous parallel algorithms are awaitable, so we
  can easily use them in coroutines.</p>
<pre class="highlight"><c- n>R</c-> <c- n>serial</c-> <c- p>{</c->
  <c- k>return</c-> <c- n>f</c-><c- p>(</c-><c- n>algorithm_x</c-><c- p>(</c-><c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>,</c-> <c- n>d</c-><c- p>));</c->
<c- p>}</c->

<c- n>sender_of</c-><c- o>&lt;</c-><c- n>R</c-><c- o>></c-> <c- k>auto</c-> <c- n>pipes</c-><c- p>()</c-> <c- p>{</c->
  <c- k>return</c-> <c- n>just</c-><c- p>()</c-> <c- o>|</c-> <c- n>async</c-><c- o>::</c-><c- n>algorithm_x</c-><c- p>(</c-><c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>,</c-> <c- n>d</c-><c- p>)</c-> <c- o>|</c-> <c- n>then</c-><c- p>(</c-><c- n>f</c-><c- p>);</c->
<c- p>}</c->

<c- n>sender_of</c-><c- o>&lt;</c-><c- n>R</c-><c- o>></c-> <c- k>auto</c-> <c- n>coroutines</c-><c- p>()</c-> <c- p>{</c->
  <c- k>co_return</c-> <c- n>f</c-><c- p>(</c-><c- k>co_await</c-> <c- n>async</c-><c- o>::</c-><c- n>algorithm_x</c-><c- p>(</c-><c- n>just</c-><c- p>(),</c-> <c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>,</c-> <c- n>d</c-><c- p>));</c->
<c- p>}</c->
</pre>
   <p>The asynchronous parallel algorithms will be based on the more modern <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>ranges</c-></code> algorithms.
Because they differ in return type and in semantics, all asynchronous parallel
  algorithm overloads will live in a new <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>async</c-></code> namespace.
The synchronous scheduled parallel algorithms will provide both legacy
  iterator-based overloads in <code class="highlight"><c- n>std</c-></code> (next to their existing counterparts that
  take execution policies) and new <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>ranges</c-></code> overloads.
For completeness, execution policy overloads of <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>ranges</c-></code> algorithms
  should be added as well.
A separate paper on the related but orthogonal topic of parallelizing <code class="highlight"><c- n>std</c-><c- o>::</c-><c- n>ranges</c-></code> algorithms and range adaptors is forthcoming.</p>
   <p>There is precedent for this overall design in <code class="highlight"><c- n>bulk</c-></code>. <code class="highlight"><c- n>bulk</c-></code> is a sender adaptor, does not take an explicit scheduler or execution
  policy, and passes through the values sent by its predecessor.</p>
   <h3 class="heading settled" data-level="3.1" id="design-compositional-utilities"><span class="secno">3.1. </span><span class="content">Compositional Utilities</span><a class="self-link" href="#design-compositional-utilities"></a></h3>
   <p><code class="highlight"><c- n>select</c-></code> and <code class="highlight"><c- n>with</c-></code> are sender adaptors that aid composition of asynchronous
  parallel algorithms. <code class="highlight"><c- n>select</c-><c- o>&lt;</c-><c- n>N</c-><c- p>,</c-> <c- n>M</c-><c- p>,</c-> <c- p>...</c-><c- o>></c-><c- p>(</c-><c- n>snd</c-><c- p>)</c-></code> chooses which values from a sender should be sent and
  in what order. <code class="highlight"><c- n>with</c-><c- p>(</c-><c- n>snd</c-><c- p>,</c-> <c- n>f</c-><c- p>)</c-></code> invokes <code class="highlight"><c- n>f</c-></code> with the values sent by <code class="highlight"><c- n>snd</c-></code>, and then sends those
  same values, along with the result of the invocation.</p>
<pre class="highlight"><c- k>template</c-> <c- o>&lt;</c-><c- n>std</c-><c- o>::</c-><c- b>size_t</c-><c- p>...</c-> <c- n>S</c-><c- o>></c->
<c- n>sender</c-> <c- k>auto</c->
<c- n>select</c-><c- p>(</c-><c- n>sender_of</c-><c- o>&lt;</c-><c- n>Args</c-><c- o>></c-> <c- k>auto</c-> <c- n>args</c-><c- p>,</c-> <c- n>F</c-> <c- n>f</c-><c- p>);</c->
<c- c1>// (args), (S[0], S[1], …), f -> f(args[S[0]], args[S[1]], args[…], …)</c->

<c- n>sender_of</c-><c- o>&lt;</c-><c- n>A</c-><c- p>,</c-> <c- n>B</c-><c- p>,</c-> …<c- p>,</c-> <c- n>invoke_result_t</c-><c- o>&lt;</c-><c- n>F</c-><c- p>,</c-> <c- n>A</c-><c- p>,</c-> <c- n>B</c-><c- p>,</c-> …<c- o>>></c-> <c- k>auto</c->
<c- n>with</c-><c- p>(</c-><c- n>sender_of</c-><c- o>&lt;</c-><c- n>A</c-><c- p>,</c-> <c- n>B</c-><c- p>,</c-> …<c- o>></c-> <c- k>auto</c-><c- p>,</c-> <c- n>F</c-> <c- n>f</c-><c- p>);</c->
<c- c1>// (a, b, …), f -> (a, b, …, F(a, b, …))</c->
</pre>
   <p><code class="highlight"><c- n>send_values</c-><c- p>(</c-><c- n>t</c-><c- p>...)</c-></code> is a function that returns an unspecified tuple-like type
  that indicates to <code class="highlight"><c- n>then</c-></code> and similar adaptors that <code class="highlight"><c- n>t</c-><c- p>...</c-></code> should be sent as
  values by the sender returned from the sender adaptor.
Likewise, <code class="highlight"><c- n>send_error</c-><c- p>(</c-><c- n>e</c-><c- p>)</c-></code> indicates to <code class="highlight"><c- n>then</c-></code> and similar adaptors that <code class="highlight"><c- n>e</c-></code> should be sent as an error by the returned sender.</p>
   <h2 class="heading settled" data-level="4" id="examples"><span class="secno">4. </span><span class="content">Examples</span><a class="self-link" href="#examples"></a></h2>
   <h3 class="heading settled" data-level="4.1" id="examples-basics"><span class="secno">4.1. </span><span class="content">Basics</span><a class="self-link" href="#examples-basics"></a></h3>
<pre class="highlight"><c- k>auto</c-> <c- n>fgh</c-> <c- o>=</c-> <c- n>just</c-><c- p>()</c-> <c- o>|</c-> <c- n>for_each</c-><c- p>(</c-><c- n>v</c-><c- p>,</c-> <c- n>f</c-><c- p>)</c-> <c- o>|</c-> <c- n>for_each</c-><c- p>(</c-><c- n>v</c-><c- p>,</c-> <c- n>g</c-><c- p>)</c-> <c- o>|</c-> <c- n>for_each</c-><c- p>(</c-><c- n>v</c-><c- p>,</c-> <c- n>h</c-><c- p>);</c->
</pre>
<pre class="highlight"><c- c1>// Range view chaining.</c->
<c- k>auto</c-> <c- n>rolling_max</c-> <c- o>=</c-> <c- n>rng</c-> <c- o>|</c-> <c- n>slide</c-><c- p>(</c-><c- n>N</c-><c- p>)</c-> <c- o>|</c-> <c- n>transform</c-><c- p>(</c-><c- n>max_element</c-><c- p>);</c->

<c- c1>// Asynchronous parallel algorithm chaining.</c->
<c- k>auto</c-> <c- n>unique_sort</c-> <c- o>=</c-> <c- n>just</c-><c- p>()</c-> <c- o>|</c-> <c- n>async</c-><c- o>::</c-><c- n>sort</c-><c- p>(</c-><c- n>v</c-><c- p>)</c-> <c- o>|</c-> <c- n>async</c-><c- o>::</c-><c- n>unique</c-><c- p>(</c-><c- n>v</c-><c- p>);</c->
</pre>
   <h3 class="heading settled" data-level="4.2" id="examples-normalize"><span class="secno">4.2. </span><span class="content">Normalize</span><a class="self-link" href="#examples-normalize"></a></h3>
<pre class="highlight"><c- k>auto</c-> <c- n>normalize_serial</c-><c- p>(</c-><c- n>range</c-> <c- k>auto</c-><c- o>&amp;&amp;</c-> <c- n>v</c-><c- p>)</c-> <c- p>{</c->
  <c- k>auto</c-> <c- n>mx</c-> <c- o>=</c-> <c- n>fold_left</c-><c- p>(</c-><c- n>v</c-><c- p>,</c-> <c- n>ranges</c-><c- o>::</c-><c- n>max</c-><c- p>{});</c->
  <c- k>return</c-> <c- n>transform</c-><c- p>(</c-><c- n>v</c-><c- p>,</c-> <c- n>views</c-><c- o>::</c-><c- n>repeat</c-><c- p>(</c-><c- n>mx</c-><c- p>),</c-> <c- n>begin</c-><c- p>(</c-><c- n>v</c-><c- p>),</c-> <c- n>divides</c-><c- p>);</c->
<c- p>}</c->
</pre>
<pre class="highlight"><c- k>auto</c-> <c- n>normalize_parallel</c-><c- p>(</c-><c- n>range</c-> <c- k>auto</c-><c- o>&amp;&amp;</c-> <c- n>v</c-><c- p>)</c-> <c- p>{</c->
  <c- k>auto</c-> <c- n>mx</c-> <c- o>=</c-> <c- n>fold_left</c-><c- p>(</c-><c- n>par</c-><c- p>,</c-> <c- n>v</c-><c- p>,</c-> <c- n>ranges</c-><c- o>::</c-><c- n>max</c-><c- p>{});</c->
  <c- k>return</c-> <c- n>transform</c-><c- p>(</c-><c- n>par</c-><c- p>,</c-> <c- n>v</c-><c- p>,</c-> <c- n>views</c-><c- o>::</c-><c- n>repeat</c-><c- p>(</c-><c- n>mx</c-><c- p>),</c-> <c- n>begin</c-><c- p>(</c-><c- n>v</c-><c- p>),</c-> <c- n>divides</c-><c- p>);</c->
<c- p>}</c->
</pre>
<pre class="highlight"><c- k>auto</c-> <c- n>normalize_async</c-><c- p>(</c-><c- n>range</c-> <c- k>auto</c-><c- o>&amp;&amp;</c-> <c- n>v</c-><c- p>)</c-> <c- p>{</c->
  <c- k>return</c-> <c- n>async</c-><c- o>::</c-><c- n>fold_left</c-><c- p>(</c-><c- n>v</c-><c- p>,</c-> <c- n>ranges</c-><c- o>::</c-><c- n>max</c-><c- p>{})</c->
       <c- o>|</c-> <c- n>let_value</c-><c- p>([]</c-> <c- p>(</c-><c- k>auto</c-> <c- n>mx</c-><c- p>,</c-> <c- n>range</c-> <c- k>auto</c-><c- o>&amp;&amp;</c-> <c- n>v</c-><c- p>)</c-> <c- p>{</c->
           <c- k>return</c-> <c- n>async</c-><c- o>::</c-><c- n>transform</c-><c- p>(</c->
             <c- n>just</c-><c- p>(),</c-> <c- n>v</c-><c- p>,</c-> <c- n>views</c-><c- o>::</c-><c- n>repeat</c-><c- p>(</c-><c- n>mx</c-><c- p>),</c-> <c- n>begin</c-><c- p>(</c-><c- n>v</c-><c- p>),</c-> <c- n>divides</c-><c- p>);</c->
         <c- p>});</c->
<c- p>}</c->
</pre>
<pre class="highlight"><c- k>auto</c-> <c- n>normalize_coroutine</c-><c- p>(</c-><c- n>range</c-> <c- k>auto</c-><c- o>&amp;&amp;</c-> <c- n>v</c-><c- p>)</c-> <c- p>{</c->
  <c- k>auto</c-> <c- n>mx</c-> <c- o>=</c-> <c- n>async</c-><c- o>::</c-><c- n>fold_left</c-><c- p>(</c-><c- n>just</c-><c- p>(),</c-> <c- n>v</c-><c- p>,</c-> <c- n>ranges</c-><c- o>::</c-><c- n>max</c-><c- p>{})</c-> <c- o>|</c-> <c- n>split</c-><c- p>;</c->
  <c- k>co_return</c-> <c- n>async</c-><c- o>::</c-><c- n>transform</c-><c- p>(</c-><c- n>mx</c-><c- p>,</c-> <c- n>v</c-><c- p>,</c-> <c- n>views</c-><c- o>::</c-><c- n>repeat</c-><c- p>(</c-><c- k>co_await</c-> <c- n>mx</c-><c- p>),</c-> <c- n>begin</c-><c- p>(</c-><c- n>v</c-><c- p>),</c-> <c- n>divides</c-><c- p>);</c->
<c- p>}</c->
</pre>
   <h3 class="heading settled" data-level="4.3" id="examples-minimum-difference"><span class="secno">4.3. </span><span class="content">Minimum Difference</span><a class="self-link" href="#examples-minimum-difference"></a></h3>
<pre class="highlight"><c- k>auto</c-> <c- n>minimum_difference_serial</c-><c- p>(</c-><c- n>range</c-> <c- k>auto</c-><c- o>&amp;&amp;</c-> <c- n>v</c-><c- p>)</c-> <c- p>{</c->
  <c- n>vector</c-><c- o>&lt;</c-><c- n>T</c-><c- o>></c-> <c- n>tmp</c-><c- p>(</c-><c- n>size</c-><c- p>(</c-><c- n>v</c-><c- p>));</c->
  <c- n>sort</c-><c- p>(</c-><c- n>v</c-><c- p>);</c->
  <c- n>adjacent_difference</c-><c- p>(</c-><c- n>v</c-><c- p>,</c-> <c- n>begin</c-><c- p>(</c-><c- n>tmp</c-><c- p>));</c->
  <c- k>return</c-> <c- o>*</c-><c- n>min_element</c-><c- p>(</c-><c- n>tmp</c-> <c- o>|</c-> <c- n>drop</c-><c- p>(</c-><c- mi>1</c-><c- p>));</c->
<c- p>}</c->
</pre>
<pre class="highlight"><c- n>sender</c-> <c- k>auto</c-> <c- n>minimum_difference_async</c-><c- p>(</c-><c- n>range</c-> <c- k>auto</c-><c- o>&amp;&amp;</c-> <c- n>v</c-><c- p>)</c-> <c- p>{</c->
  <c- k>return</c-> <c- n>just</c-><c- p>(</c-><c- n>vector</c-><c- o>&lt;</c-><c- n>T</c-><c- o>></c-><c- p>(</c-><c- n>size</c-><c- p>(</c-><c- n>v</c-><c- p>))))</c->
       <c- o>|</c-> <c- n>let_value</c-><c- p>([</c-><c- o>&amp;</c-><c- n>v</c-><c- p>]</c-> <c- p>(</c-><c- n>range</c-> <c- k>auto</c-><c- o>&amp;&amp;</c-> <c- n>tmp</c-><c- p>)</c-> <c- p>{</c->
           <c- k>return</c-> <c- n>async</c-><c- o>::</c-><c- n>sort</c-><c- p>(</c-><c- n>just</c-><c- p>(),</c-> <c- n>v</c-><c- p>)</c->
                <c- o>|</c-> <c- n>async</c-><c- o>::</c-><c- n>adjacent_difference</c-><c- p>(</c-><c- n>v</c-><c- p>,</c-> <c- n>begin</c-><c- p>(</c-><c- n>tmp</c-><c- p>))</c->
                <c- o>|</c-> <c- n>async</c-><c- o>::</c-><c- n>min_element</c-><c- p>(</c-><c- n>tmp</c-> <c- o>|</c-> <c- n>drop</c-><c- p>(</c-><c- mi>1</c-><c- p>));</c->
         <c- p>});</c->
<c- p>}</c->
</pre>
<pre class="highlight"><c- n>sender</c-> <c- k>auto</c-> <c- n>minimum_difference_coroutine</c-><c- p>(</c-><c- n>range</c-> <c- k>auto</c-><c- o>&amp;&amp;</c-> <c- n>v</c-><c- p>)</c-> <c- p>{</c->
  <c- n>vector</c-><c- o>&lt;</c-><c- n>T</c-><c- o>></c-> <c- n>tmp</c-><c- p>(</c-><c- n>size</c-><c- p>(</c-><c- n>v</c-><c- p>));</c->
  <c- k>auto</c-> <c- n>s0</c-> <c- o>=</c-> <c- n>async</c-><c- o>::</c-><c- n>sort</c-><c- p>(</c-><c- n>just</c-><c- p>(),</c-> <c- n>v</c-><c- p>);</c->
  <c- k>auto</c-> <c- n>s1</c-> <c- o>=</c-> <c- n>async</c-><c- o>::</c-><c- n>adjacent_difference</c-><c- p>(</c-><c- n>s0</c-><c- p>,</c-> <c- n>v</c-><c- p>,</c-> <c- n>begin</c-><c- p>(</c-><c- n>tmp</c-><c- p>));</c->
  <c- k>co_return</c-> <c- o>*</c-><c- k>co_await</c-> <c- n>async</c-><c- o>::</c-><c- n>min_element</c-><c- p>(</c-><c- n>s1</c-><c- p>,</c-> <c- n>tmp</c-> <c- o>|</c-> <c- n>drop</c-><c- p>(</c-><c- mi>1</c-><c- p>));</c->
<c- p>}</c->
</pre>
   <h3 class="heading settled" data-level="4.4" id="examples-rain-water"><span class="secno">4.4. </span><span class="content">Rain Water</span><a class="self-link" href="#examples-rain-water"></a></h3>
<pre class="highlight"><c- b>int</c-> <c- nf>rain_water_serial</c-><c- p>(</c-><c- n>range</c-> <c- k>auto</c-><c- o>&amp;&amp;</c-> <c- n>v</c-><c- p>)</c-> <c- p>{</c->
  <c- n>vector</c-><c- o>&lt;</c-><c- n>T</c-><c- o>></c-> <c- n>tmp</c-><c- p>(</c-><c- n>size</c-><c- p>(</c-><c- n>v</c-><c- p>),</c-> <c- mi>0</c-><c- p>);</c->
  <c- k>auto</c-> <c- n>it</c-> <c- o>=</c-> <c- n>max_element</c-><c- p>(</c-><c- n>v</c-><c- p>);</c->
  <c- n>inclusive_scan</c-><c- p>(</c-><c- n>begin</c-><c- p>(</c-><c- n>v</c-><c- p>),</c->  <c- n>next</c-><c- p>(</c-><c- n>it</c-><c- p>),</c-> <c- n>begin</c-><c- p>(</c-><c- n>tmp</c-><c- p>),</c-> <c- n>ranges</c-><c- o>::</c-><c- n>max</c-><c- p>{});</c->
  <c- n>inclusive_scan</c-><c- p>(</c-><c- n>rbegin</c-><c- p>(</c-><c- n>v</c-><c- p>),</c-> <c- n>reverse_iterator</c-><c- p>(</c-><c- n>it</c-><c- p>),</c->  <c- n>rbegin</c-><c- p>(</c-><c- n>tmp</c-><c- p>),</c-> <c- n>ranges</c-><c- o>::</c-><c- n>max</c-><c- p>{});</c->
  <c- k>return</c-> <c- n>transform_reduce</c-><c- p>(</c-><c- n>tmp</c-><c- p>,</c-> <c- n>cbegin</c-><c- p>(</c-><c- n>v</c-><c- p>),</c-> <c- mi>0</c-><c- p>,</c-> <c- n>plus</c-><c- o>&lt;></c-><c- p>{},</c-> <c- n>minus</c-><c- o>&lt;></c-><c- p>{});</c->
<c- p>}</c->
</pre>
<pre class="highlight"><c- n>sender_of</c-><c- o>&lt;</c-><c- b>int</c-><c- o>></c-> <c- k>auto</c-> <c- n>rain_water_async</c-><c- p>(</c-><c- n>range</c-> <c- k>auto</c-><c- o>&amp;&amp;</c-> <c- n>v</c-><c- p>)</c-> <c- p>{</c->
  <c- k>return</c-> <c- n>just</c-><c- p>(</c-><c- n>vector</c-><c- o>&lt;</c-><c- n>T</c-><c- o>></c-><c- p>(</c-><c- n>size</c-><c- p>(</c-><c- n>v</c-><c- p>))))</c->
       <c- o>|</c-> <c- n>let_value</c-><c- p>([</c-><c- o>&amp;</c-><c- n>v</c-><c- p>]</c-> <c- p>(</c-><c- n>ranges</c-> <c- k>auto</c-><c- o>&amp;&amp;</c-> <c- n>tmp</c-><c- p>)</c-> <c- p>{</c->
           <c- k>auto</c-> <c- n>sit</c-> <c- o>=</c-> <c- n>async</c-><c- o>::</c-><c- n>max_element</c-><c- p>(</c-><c- n>just</c-><c- p>(),</c-> <c- n>v</c-><c- p>)</c-> <c- o>|</c-> <c- n>split</c-><c- p>;</c->

           <c- k>auto</c-> <c- n>sleft</c->  <c- o>=</c-> <c- n>sit</c->
                      <c- o>|</c-> <c- n>let_value</c-><c- p>([</c-><c- o>&amp;</c-><c- n>v</c-><c- p>]</c-> <c- p>(</c-><c- k>auto</c-> <c- n>it</c-><c- p>)</c-> <c- p>{</c->
                          <c- k>return</c-> <c- n>async</c-><c- o>::</c-><c- n>inclusive_scan</c-><c- p>(</c->
                            <c- n>just</c-><c- p>(),</c-> <c- n>begin</c-><c- p>(</c-><c- n>v</c-><c- p>),</c-> <c- n>next</c-><c- p>(</c-><c- n>it</c-><c- p>),</c-> <c- n>begin</c-><c- p>(</c-><c- n>tmp</c-><c- p>),</c->
                            <c- n>ranges</c-><c- o>::</c-><c- n>max</c-><c- p>{});</c->
                        <c- p>});</c->

           <c- k>auto</c-> <c- n>sright</c-> <c- o>=</c-> <c- n>sit</c->
                      <c- o>|</c-> <c- n>let_value</c-><c- p>([</c-><c- o>&amp;</c-><c- n>v</c-><c- p>]</c-> <c- p>(</c-><c- k>auto</c-> <c- n>it</c-><c- p>)</c-> <c- p>{</c->
                          <c- k>return</c-> <c- n>async</c-><c- o>::</c-><c- n>inclusive_scan</c-><c- p>(</c->
                            <c- n>just</c-><c- p>(),</c-> <c- n>rbegin</c-><c- p>(</c-><c- n>v</c-><c- p>),</c-> <c- n>reverse_iterator</c-><c- p>(</c-><c- n>it</c-><c- p>),</c-> <c- n>rbegin</c-><c- p>(</c-><c- n>tmp</c-><c- p>),</c->
                            <c- n>ranges</c-><c- o>::</c-><c- n>max</c-><c- p>{});</c->
                        <c- p>});</c->

           <c- k>return</c-> <c- n>when_all</c-><c- p>(</c-><c- n>sleft</c-><c- p>,</c-> <c- n>sright</c-><c- p>)</c->
                <c- o>|</c-> <c- n>async</c-><c- o>::</c-><c- n>transform_reduce</c-><c- p>(</c-><c- n>tmp</c-><c- p>,</c-> <c- n>cbegin</c-><c- p>(</c-><c- n>v</c-><c- p>),</c-> <c- mi>0</c-><c- p>,</c-> <c- n>plus</c-><c- o>&lt;></c-><c- p>(),</c-> <c- n>minus</c-><c- o>&lt;></c-><c- p>());</c->
         <c- p>});</c->
<c- p>}</c->
</pre>
<pre class="highlight"><c- n>sender_of</c-><c- o>&lt;</c-><c- b>int</c-><c- o>></c-> <c- k>auto</c-> <c- n>rain_water_coroutine</c-><c- p>(</c-><c- n>range</c-> <c- k>auto</c-><c- o>&amp;&amp;</c-> <c- n>v</c-><c- p>)</c-> <c- p>{</c->
  <c- n>vector</c-><c- o>&lt;</c-><c- n>T</c-><c- o>></c-> <c- n>tmp</c-><c- p>(</c-><c- n>size</c-><c- p>(</c-><c- n>v</c-><c- p>),</c-> <c- mi>0</c-><c- p>);</c->
  <c- k>auto</c-> <c- n>sit</c-> <c- o>=</c-> <c- n>async</c-><c- o>::</c-><c- n>max_element</c-><c- p>(</c-><c- n>just</c-><c- p>(),</c-> <c- n>v</c-><c- p>)</c-> <c- o>|</c-> <c- n>split</c-><c- p>;</c->
  <c- k>auto</c-> <c- n>sleft</c->  <c- o>=</c-> <c- n>async</c-><c- o>::</c-><c- n>inclusive_scan</c-><c- p>(</c->
    <c- n>sit</c-><c- p>,</c-> <c- n>begin</c-><c- p>(</c-><c- n>v</c-><c- p>),</c->  <c- n>next</c-><c- p>(</c-><c- k>co_await</c-> <c- n>sit</c-><c- p>),</c-> <c- n>begin</c-><c- p>(</c-><c- n>tmp</c-><c- p>),</c-> <c- n>ranges</c-><c- o>::</c-><c- n>max</c-><c- p>{});</c->
  <c- k>auto</c-> <c- n>sright</c-> <c- o>=</c-> <c- n>async</c-><c- o>::</c-><c- n>inclusive_scan</c-><c- p>(</c->
    <c- n>sit</c-><c- p>,</c-> <c- n>rbegin</c-><c- p>(</c-><c- n>v</c-><c- p>),</c-> <c- n>reverse_iterator</c-><c- p>(</c-><c- k>co_await</c-> <c- n>sit</c-><c- p>),</c-> <c- n>rbegin</c-><c- p>(</c-><c- n>tmp</c-><c- p>),</c-> <c- n>ranges</c-><c- o>::</c-><c- n>max</c-><c- p>{});</c->
  <c- k>co_return</c-> <c- n>transform_reduce</c-><c- p>(</c-><c- n>tmp</c-><c- p>,</c-> <c- n>cbegin</c-><c- p>(</c-><c- n>v</c-><c- p>),</c-> <c- mi>0</c-><c- p>,</c-> <c- n>plus</c-><c- o>&lt;></c-><c- p>{},</c-> <c- n>minus</c-><c- o>&lt;></c-><c- p>{});</c->
<c- p>}</c->
</pre>
   <h3 class="heading settled" data-level="4.5" id="examples-upper-triangular-cholesky-factorization"><span class="secno">4.5. </span><span class="content">Upper Triangular Cholesky Factorization</span><a class="self-link" href="#examples-upper-triangular-cholesky-factorization"></a></h3>
<pre class="highlight"><c- b>void</c-> <c- nf>upper_triangular_cholesky_factorization_parallel</c-><c- p>(</c->
  <c- n>la_matrix</c-> <c- n>A</c-><c- p>,</c-> <c- n>la_vector</c-> <c- n>b</c-><c- p>,</c-> <c- n>la_vector</c-> <c- n>x</c-><c- p>)</c->
<c- p>{</c->
  <c- c1>// Solve U^T c = b, using x to store c</c->
  <c- n>triangular_matrix_vector_solve</c-><c- p>(</c->
    <c- n>par</c-><c- p>,</c->
    <c- n>transposed</c-><c- p>(</c-><c- n>A</c-><c- p>),</c-> <c- n>upper_triangle</c-><c- p>,</c-> <c- n>explicit_diagonal</c-><c- p>,</c->
    <c- n>b</c-><c- p>,</c->
    <c- n>x</c-><c- p>,</c->
    <c- n>divides</c-><c- p>);</c->

  <c- c1>// Solve U x = c, overwriting x with result</c->
  <c- n>triangular_matrix_vector_solve</c-><c- p>(</c->
    <c- n>par</c-><c- p>,</c->
    <c- n>A</c-><c- p>,</c-> <c- n>upper_triangle</c-><c- p>,</c-> <c- n>explicit_diagonal</c-><c- p>,</c->
    <c- n>x</c-><c- p>,</c->
    <c- n>divides</c-><c- p>);</c->
<c- p>}</c->
</pre>
<pre class="highlight"><c- n>sender_of</c-><c- o>&lt;</c-><c- n>la_vector</c-><c- o>></c-> <c- n>upper_triangular_cholesky_factorization_async</c-><c- p>(</c->
  <c- n>la_matrix</c-> <c- n>A</c-><c- p>,</c-> <c- n>la_vector</c-> <c- n>b</c-><c- p>,</c-> <c- n>la_vector</c-> <c- n>x</c-><c- p>)</c->
<c- p>{</c->
  <c- k>return</c-> <c- n>just</c-><c- p>(</c-><c- n>x</c-><c- p>)</c->
         <c- c1>// Solve U^T c = b, using x to store c</c->
       <c- o>|</c-> <c- n>async</c-><c- o>::</c-><c- n>triangular_matrix_vector_solve</c-><c- p>(</c->
           <c- n>transposed</c-><c- p>(</c-><c- n>A</c-><c- p>),</c-> <c- n>upper_triangle</c-><c- p>,</c-> <c- n>explicit_diagonal</c-><c- p>,</c->
           <c- n>b</c-><c- p>,</c->
           <c- n>x</c-><c- p>,</c->
           <c- n>divides</c-><c- p>);</c->
         <c- c1>// Solve U x = c, overwriting x with result</c->
       <c- o>|</c-> <c- n>async</c-><c- o>::</c-><c- n>triangular_matrix_vector_solve</c-><c- p>(</c->
           <c- n>A</c-><c- p>,</c-> <c- n>upper_triangle</c-><c- p>,</c-> <c- n>explicit_diagonal</c-><c- p>,</c->
           <c- n>x</c-><c- p>,</c->
           <c- n>divides</c-><c- p>);</c->
<c- p>}</c->
</pre>
   <h2 class="heading settled" data-level="5" id="alternatives"><span class="secno">5. </span><span class="content">Alternatives</span><a class="self-link" href="#alternatives"></a></h2>
   <h3 class="heading settled" data-level="5.1" id="alternatives-asynchronous-parameters"><span class="secno">5.1. </span><span class="content">Asynchronous Parameters and Logical Return Types</span><a class="self-link" href="#alternatives-asynchronous-parameters"></a></h3>
   <p>We explored a design where the parameters to the algorithms (ranges, iterators,
  invocables) could be provided asynchronously by the predecessor sender instead
  of being supplied at the time the asynchronous algorithm is invoked.
If the predecessor sender sends N values, they correspond to the first N
  parameters of the parallel algorithm.
None of the parameters may be skipped; they bind from the front, like <code class="highlight"><c- n>bind_front</c-></code>.
Any parameters that are not sent by the sender must be passed after the
  predecessor parameter.
The sender may send no parameters, in which case they all must be provided
  after the predecessor sender parameter.</p>
<pre class="highlight"><c- c1>// Synchronous scheduled parallel (New)</c->
<c- n>value</c-> <c- o>=</c-> <c- n>algorithm</c-><c- p>(</c-><c- n>schedule</c-><c- p>(</c-><c- n>sch</c-><c- p>),</c-> <c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>,</c-> <c- n>d</c-><c- p>);</c->
<c- n>value</c-> <c- o>=</c-> <c- n>algorithm</c-><c- p>(</c-><c- n>transfer_just</c-><c- p>(</c-><c- n>sch</c-><c- p>,</c-> <c- n>a</c-><c- p>),</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>,</c-> <c- n>d</c-><c- p>);</c->
<c- n>value</c-> <c- o>=</c-> <c- n>algorithm</c-><c- p>(</c-><c- n>transfer_just</c-><c- p>(</c-><c- n>sch</c-><c- p>,</c-> <c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>),</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>,</c-> <c- n>d</c-><c- p>);</c->
<c- n>value</c-> <c- o>=</c-> <c- n>algorithm</c-><c- p>(</c-><c- n>transfer_just</c-><c- p>(</c-><c- n>sch</c-><c- p>,</c-> <c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>),</c-> <c- n>d</c-><c- p>);</c->
<c- n>value</c-> <c- o>=</c-> <c- n>algorithm</c-><c- p>(</c-><c- n>transfer_just</c-><c- p>(</c-><c- n>sch</c-><c- p>,</c-> <c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>));</c->

<c- c1>// Asynchronous parallel (New)</c->
<c- n>snd</c-> <c- o>=</c-> <c- n>async</c-><c- o>::</c-><c- n>algorithm</c-><c- p>(</c-><c- n>just</c-><c- p>(),</c-> <c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>,</c-> <c- n>d</c-><c- p>);</c->
<c- n>snd</c-> <c- o>=</c-> <c- n>async</c-><c- o>::</c-><c- n>algorithm</c-><c- p>(</c-><c- n>just</c-><c- p>(</c-><c- n>a</c-><c- p>),</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>,</c-> <c- n>d</c-><c- p>);</c->
<c- n>snd</c-> <c- o>=</c-> <c- n>async</c-><c- o>::</c-><c- n>algorithm</c-><c- p>(</c-><c- n>just</c-><c- p>(</c-><c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>),</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>,</c-> <c- n>d</c-><c- p>);</c->
<c- n>snd</c-> <c- o>=</c-> <c- n>async</c-><c- o>::</c-><c- n>algorithm</c-><c- p>(</c-><c- n>just</c-><c- p>(</c-><c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>),</c-> <c- n>d</c-><c- p>);</c->
<c- n>snd</c-> <c- o>=</c-> <c- n>async</c-><c- o>::</c-><c- n>algorithm</c-><c- p>(</c-><c- n>just</c-><c- p>(</c-><c- n>a</c-><c- p>,</c-> <c- n>b</c-><c- p>,</c-> <c- n>c</c-><c- p>,</c-> <c- n>d</c-><c- p>));</c->
</pre>
   <p>The goal of this model was composability.
We wanted to enable the same point-free style programming used by range adaptor
  pipelines, where the inputs and outputs of each operation are not explicitly
  named and flow into each other.</p>
   <p>We found that this model worked best when you are working with a single range
  and performing operations in-place:</p>
<pre class="highlight"><c- k>auto</c-> <c- n>fgh</c-> <c- o>=</c-> <c- n>just</c-><c- p>(</c-><c- n>v</c-><c- p>)</c-> <c- o>|</c-> <c- n>for_each</c-><c- p>(</c-><c- n>f</c-><c- p>)</c-> <c- o>|</c-> <c- n>for_each</c-><c- p>(</c-><c- n>g</c-><c- p>)</c-> <c- o>|</c-> <c- n>for_each</c-><c- p>(</c-><c- n>h</c-><c- p>);</c->

<c- k>auto</c-> <c- n>unique_sort</c-> <c- o>=</c-> <c- n>just</c-><c- p>(</c-><c- n>v</c-><c- p>)</c-> <c- o>|</c-> <c- n>async</c-><c- o>::</c-><c- n>sort</c-> <c- o>|</c-> <c- n>async</c-><c- o>::</c-><c- n>unique</c-><c- p>;</c->
</pre>
   <p>However, we found that the model broke down when writing more complex code.
We often had to use the <code class="highlight"><c- n>with</c-></code> and <code class="highlight"><c- n>select</c-></code> utilities and additionally insert <code class="highlight"><c- n>then</c-></code> adaptors that would adjust parameters in between algorithm
  invocations - increment an iterator by one, dereference an iterator, turn a
  scalar into a range view, get an iterator from a range, etc.</p>
<pre class="highlight"><c- k>auto</c-> <c- n>normalize_async</c-><c- p>(</c-><c- n>range</c-> <c- k>auto</c-><c- o>&amp;&amp;</c-> <c- n>rng</c-><c- p>)</c-> <c- p>{</c->
  <c- k>return</c-> <c- n>async</c-><c- o>::</c-><c- n>fold_left</c-><c- p>(</c-><c- n>rng</c-><c- p>,</c-> <c- n>max</c-><c- p>)</c->
       <c- o>|</c-> <c- n>then</c-><c- p>([]</c-> <c- p>(</c-><c- k>auto</c-> <c- n>rng</c-><c- p>,</c-> <c- k>auto</c-> <c- n>mx</c-><c- p>)</c-> <c- p>{</c->
           <c- k>return</c-> <c- n>send_values</c-><c- p>(</c-><c- n>rng</c-><c- p>,</c-> <c- n>repeat</c-><c- p>(</c-><c- n>mx</c-><c- p>));</c->
         <c- p>})</c->
       <c- o>|</c-> <c- n>inplace</c-><c- p>(</c-><c- n>async</c-><c- o>::</c-><c- n>transform</c-><c- p>(</c-><c- n>divides</c-><c- p>));</c->
<c- p>}</c->

<c- n>sender_of</c-><c- o>&lt;</c-><c- n>la_vector</c-><c- o>></c-> <c- n>upper_triangular_cholesky_factorization_async</c-><c- p>(</c->
  <c- n>la_matrix</c-> <c- n>A</c-><c- p>,</c-> <c- n>la_vector</c-> <c- n>b</c-><c- p>,</c-> <c- n>la_vector</c-> <c- n>x</c-><c- p>)</c->
<c- p>{</c->
  <c- k>return</c-> <c- n>just</c-><c- p>(</c-><c- n>transposed</c-><c- p>(</c-><c- n>A</c-><c- p>),</c-> <c- n>upper_triangle</c-><c- p>,</c-> <c- n>explicit_diagonal</c-><c- p>,</c-> <c- n>b</c-><c- p>,</c-> <c- n>x</c-><c- p>)</c->
       <c- o>|</c-> <c- n>with</c-><c- p>(</c-><c- n>async</c-><c- o>::</c-><c- n>triangular_matrix_vector_solve</c-><c- p>(</c-><c- n>divides</c-><c- p>))</c->
         <c- c1>// Receives (transposed(A), upper_triangle, explicit_diagonal, b, x)</c->
         <c- c1>// Sends (transposed(A), upper_triangle, explicit_diagonal, b, x)</c->
       <c- o>|</c-> <c- n>select</c-><c- o>&lt;</c-><c- mi>0</c-><c- p>,</c-> <c- mi>1</c-><c- p>,</c-> <c- mi>2</c-><c- p>,</c-> <c- mi>4</c-><c- o>></c-><c- p>(</c-><c- n>then</c-><c- p>([]</c-> <c- p>(</c-><c- k>auto</c-> <c- n>A</c-><c- p>,</c-> <c- k>auto</c-> <c- n>tri</c-><c- p>,</c-> <c- k>auto</c-> <c- n>dia</c-><c- p>,</c-> <c- k>auto</c-> <c- n>x</c-><c- p>)</c-> <c- p>{</c->
           <c- k>return</c-> <c- n>send_values</c-><c- p>(</c-><c- n>transposed</c-><c- p>(</c-><c- n>A</c-><c- p>),</c-> <c- n>tri</c-><c- p>,</c-> <c- n>dia</c-><c- p>,</c-> <c- n>x</c-><c- p>);</c->
         <c- p>}))</c-> <c- c1>// Drop b and untranspose A.</c->
         <c- c1>// Sends (A, upper_triangle, explicit_diagonal, x)</c->
       <c- o>|</c-> <c- n>async</c-><c- o>::</c-><c- n>triangular_matrix_vector_solve</c-><c- p>(</c-><c- n>divides</c-><c- p>);</c->
<c- p>}</c->
</pre>
   <p>We were particularly plagued by mismatches between range parameters (which are
  only used for the primary input to algorithms), iterator parameters (which are
  used for auxiliary inputs and outputs to algorithms), and non-scalar return
  types (most algorithms return iterators-past-the-end, which can’t be fed in as
  the next primary input, which needs to be a range).</p>
   <p>Many algorithms are not in-place, such as <code class="highlight"><c- n>transform</c-></code>.
An <code class="highlight"><c- n>inplace</c-><c- p>(</c-><c- n>snd</c-><c- p>,</c-> <c- n>adaptor</c-><c- p>)</c-></code> that transforms an out-of-place asynchronous
  algorithm into an in-place one helps with that:</p>
<pre class="highlight"><c- n>sender_of</c-><c- o>&lt;</c-><c- k>decltype</c-><c- p>(</c-><c- n>Adaptor</c-><c- p>{}(</c-><c- n>R</c-><c- p>{},</c-> <c- n>iterator_t</c-><c- o>&lt;</c-><c- n>R</c-><c- p>{}</c-><c- o>></c-><c- p>))</c-><c- o>></c-> <c- k>auto</c->
<c- n>inplace</c-><c- p>(</c-><c- n>sender_of</c-><c- o>&lt;</c-><c- n>R</c-><c- o>></c-> <c- n>r</c-><c- p>,</c-> <c- n>Adaptor</c-> <c- n>a</c-><c- p>);</c->
<c- c1>// r, a -> a(r, begin(r))</c->

<c- k>auto</c-> <c- n>fgh</c-> <c- o>=</c-> <c- n>just</c-><c- p>(</c-><c- n>v</c-><c- p>)</c->
         <c- o>|</c-> <c- n>inplace</c-><c- p>(</c-><c- n>transform</c-><c- p>(</c-><c- n>f</c-><c- p>))</c->
         <c- o>|</c-> <c- n>inplace</c-><c- p>(</c-><c- n>transform</c-><c- p>(</c-><c- n>g</c-><c- p>))</c->
         <c- o>|</c-> <c- n>inplace</c-><c- p>(</c-><c- n>transform</c-><c- p>(</c-><c- n>h</c-><c- p>));</c->
</pre>
   <p>As part of this design, we planned to have the asynchronous parallel algorithms
  return a sender that sends the logical output of the algorithm, suitable for
  piping into another parallel algorithm.
This asynchronous value will often differ from the return value of the
  corresponding non-parallel algorithm.
For example, <code class="highlight"><c- n>transform</c-></code> returns a single iterator to the end of the
  transformed range, but <code class="highlight"><c- n>async</c-><c- o>::</c-><c- n>transform</c-></code> should send the transformed range.
This is implementable because parallel algorithms require forward iterators.</p>
   <table>
    <tbody>
     <tr>
      <th> Algorithm 
      <th> Non-Parallel Returns... 
      <th> Asynchronous Sends... 
     <tr>
      <td>
<pre class="highlight"><c- n>transform</c-><c- p>(</c-><c- n>Range0</c-> <c- n>rng0</c-><c- p>,</c->
          <c- p>[</c-><c- n>Range1</c-> <c- n>rng1</c-><c- p>,]</c->
          <c- n>Iterator</c-> <c- n>out</c-><c- p>,</c->
          <c- n>F</c-> <c- n>f</c-><c- p>)</c->
</pre>
      <td> Iterators to the last transformed element of <code class="highlight"><c- n>rng0</c-></code> and <code class="highlight"><c- n>rng1</c-></code> and an iterator
  to the element past the last transformed element of <code class="highlight"><c- n>out</c-></code>. 
      <td> The range of transformed elements of <code class="highlight"><c- n>out</c-></code>, e.g. <code class="highlight"><c- n>subrange</c-><c- p>(</c-><c- n>out</c-><c- p>,</c-> <c- n>advance</c-><c- p>(</c-><c- n>out</c-><c- p>,</c-> <c- n>N</c-><c- p>))</c-></code>,
  where <code class="highlight"><c- n>N</c-></code> is the number of elements transformed. 
     <tr>
      <td>
<pre class="highlight"><c- n>for_each</c-><c- p>(</c-><c- n>Range</c-> <c- n>rng</c-><c- p>,</c->
         <c- n>F</c-> <c- n>f</c-><c- p>)</c->
</pre>
      <td> An iterator to the element past the last transformed element of <code class="highlight"><c- n>rng</c-></code> and the
  object <code class="highlight"><c- n>f</c-></code>. 
      <td> <code class="highlight"><c- n>rng</c-></code>. 
     <tr>
      <td>
<pre class="highlight"><c- n>reduce</c-><c- p>(</c-><c- n>Range</c-> <c- n>rng</c-><c- p>,</c->
        <c- n>T</c-> <c- n>init</c-><c- p>,</c->
        <c- n>F</c-> <c- n>f</c-><c- p>)</c->
</pre>
      <td> The sum as a T. 
      <td> The sum as a T. 
     <tr>
      <td>
<pre class="highlight"><c- n>fold_</c-><c- o>*</c-><c- p>(</c-><c- n>Range</c-> <c- n>rng</c-><c- p>,</c->
       <c- p>[</c-><c- n>T</c-> <c- n>init</c-><c- p>,]</c->
       <c- n>F</c-> <c- n>f</c-><c- p>)</c->
</pre>
      <td> The sum as a T. 
      <td> The sum as a T. 
     <tr>
      <td>
<pre class="highlight"><c- n>find_if</c-><c- p>(</c-><c- n>Range</c-> <c- n>rng</c-><c- p>,</c->
        <c- n>Pred</c-> <c- n>p</c-><c- p>)</c->
</pre>
      <td> An iterator to the found element. 
      <td> An iterator to the found element. 
     <tr>
      <td>
<pre class="highlight"><c- p>(</c-><c- n>min</c-><c- o>|</c-><c- n>max</c-><c- p>)</c-><c- n>_element</c-><c- p>(</c-><c- n>Range</c-> <c- n>rng</c-><c- p>,</c->
                  <c- n>Comp</c-> <c- n>c</c-><c- p>)</c->
</pre>
      <td> An iterator to the found element. 
      <td> An iterator to the found element. 
     <tr>
      <td>
<pre class="highlight"><c- n>copy</c-><c- p>(</c-><c- n>Range</c-> <c- n>from</c-><c- p>,</c->
     <c- n>Iterator</c-> <c- n>to</c-><c- p>)</c->
</pre>
      <td> An iterator to the last copied element of <code class="highlight"><c- n>from</c-></code> and an iterator to the element
  past the last copied element of <code class="highlight"><c- n>to</c-></code>. 
      <td> The range of copied elements of <code class="highlight"><c- n>to</c-></code>, e.g. <code class="highlight"><c- n>subrange</c-><c- p>(</c-><c- n>to</c-><c- p>,</c-> <c- n>advance</c-><c- p>(</c-><c- n>to</c-><c- p>,</c-> <c- n>N</c-><c- p>))</c-></code>, where <code class="highlight"><c- n>N</c-></code> is the number of elements copied. 
     <tr>
      <td>
<pre class="highlight"><c- n>copy_if</c-><c- p>(</c-><c- n>Range</c-> <c- n>from</c-><c- p>,</c->
        <c- n>Iterator</c-> <c- n>to</c-><c- p>)</c->
</pre>
      <td> An iterator to the last copied element of <code class="highlight"><c- n>from</c-></code> and an iterator to the element
  past the last copied element of <code class="highlight"><c- n>to</c-></code>. 
      <td> The range of copied elements of <code class="highlight"><c- n>to</c-></code>, e.g. <code class="highlight"><c- n>subrange</c-><c- p>(</c-><c- n>to</c-><c- p>,</c-> <c- n>advance</c-><c- p>(</c-><c- n>to</c-><c- p>,</c-> <c- n>M</c-><c- p>))</c-></code>,
  where <code class="highlight"><c- n>M</c-></code> is the number of elements copied. 
     <tr>
      <td>
<pre class="highlight"><c- o>*</c-><c- n>_scan</c-><c- p>(</c-><c- n>Range</c-> <c- n>rng</c-><c- p>,</c->
       <c- n>Iterator</c-> <c- n>out</c-><c- p>,</c->
       <c- n>T</c-> <c- n>init</c-><c- p>,</c->
       <c- n>F</c-> <c- n>f</c-><c- p>)</c->
</pre>
      <td> An iterator to the element past the last scanned element of <code class="highlight"><c- n>out</c-></code> (this is what
  the non-parallel overload returns, as there’s no parallel overload yet) 
      <td> The range of scanned elements of <code class="highlight"><c- n>out</c-></code>, e.g. <code class="highlight"><c- n>subrange</c-><c- p>(</c-><c- n>out</c-><c- p>,</c-> <c- n>advance</c-><c- p>(</c-><c- n>out</c-><c- p>,</c-> <c- n>N</c-><c- p>))</c-></code>,
  where <code class="highlight"><c- n>N</c-></code> is the number of elements scanned. 
     <tr>
      <td>
<pre class="highlight"><c- n>sort</c-><c- p>(</c-><c- n>Range</c-> <c- n>rng</c-><c- p>,</c->
     <c- n>Comp</c-> <c- n>c</c-><c- p>)</c->
</pre>
      <td> An iterator to the last sorted element. 
      <td> <code class="highlight"><c- n>rng</c-></code>. 
     <tr>
      <td>
<pre class="highlight"><c- n>unique</c-><c- p>(</c-><c- n>Range</c-> <c- n>rng</c-><c- p>,</c->
       <c- n>Comp</c-> <c- n>c</c-><c- p>)</c->
</pre>
      <td> The range of unique elements of <code class="highlight"><c- n>rng</c-></code>, e.g. <code class="highlight"><c- n>subrange</c-><c- p>(</c-><c- n>begin</c-><c- p>(</c-><c- n>rng</c-><c- p>),</c-> <c- n>advance</c-><c- p>(</c-><c- n>begin</c-><c- p>(</c-><c- n>rng</c-><c- p>),</c-> <c- n>N</c-><c- p>))</c-></code>, where <code class="highlight"><c- n>N</c-></code> is the number of
  unique elements. 
      <td> The range of unique elements of <code class="highlight"><c- n>rng</c-></code>, e.g. <code class="highlight"><c- n>subrange</c-><c- p>(</c-><c- n>begin</c-><c- p>(</c-><c- n>rng</c-><c- p>),</c-> <c- n>advance</c-><c- p>(</c-><c- n>begin</c-><c- p>(</c-><c- n>rng</c-><c- p>),</c-> <c- n>N</c-><c- p>))</c-></code>, where <code class="highlight"><c- n>N</c-></code> is the number of
  unique elements. 
     <tr>
      <td>
<pre class="highlight"><c- n>partition</c-><c- p>(</c-><c- n>Range</c-> <c- n>rng</c-><c- p>,</c->
          <c- n>Comp</c-> <c- n>c</c-><c- p>)</c->
</pre>
      <td> The range of the group of <code class="highlight"><c- n>rng</c-></code> for which <code class="highlight"><c- n>c</c-></code> is false (the second group), e.g. <code class="highlight"><c- n>subrange</c-><c- p>(</c-><c- n>advance</c-><c- p>(</c-><c- n>begin</c-><c- p>(</c-><c- n>rng</c-><c- p>),</c-> <c- n>M</c-><c- p>),</c-> <c- n>end</c-><c- p>(</c-><c- n>rng</c-><c- p>))</c-></code>, where <code class="highlight"><c- n>M</c-></code> is the number of
  elements for which <code class="highlight"><c- n>c</c-></code> is true. 
      <td> The range of the group of <code class="highlight"><c- n>rng</c-></code> for which <code class="highlight"><c- n>c</c-></code> is false (the second group), e.g. <code class="highlight"><c- n>subrange</c-><c- p>(</c-><c- n>advance</c-><c- p>(</c-><c- n>begin</c-><c- p>(</c-><c- n>rng</c-><c- p>),</c-> <c- n>M</c-><c- p>),</c-> <c- n>end</c-><c- p>(</c-><c- n>rng</c-><c- p>))</c-></code>, where <code class="highlight"><c- n>M</c-></code> is the number of
  elements for which <code class="highlight"><c- n>c</c-></code> is true. 
   </table>
   <p>We eventually reached the conclusion that this whole approach was too complex,
  and that it would be better to use <code class="highlight"><c- n>let_value</c-></code> and named variables to handle
  asynchronous parameters.
The major downside to this approach is that <code class="highlight"><c- n>let_value</c-></code> requires dynamic
  parallelism, which is undesirable on some platforms.
This is discussed more in <a href="#issues-dynamic-asynchrony">§ 6.1 Dynamic Asynchrony</a>.</p>
   <h3 class="heading settled" data-level="5.2" id="alternatives-attach-to-execution-policeis-or-schedulers"><span class="secno">5.2. </span><span class="content">Attach Execution Policies to Schedulers or Vice Versa</span><a class="self-link" href="#alternatives-attach-to-execution-policeis-or-schedulers"></a></h3>
   <p><a data-link-type="biblio" href="https://wg21.link/p2500r2" title="C++ parallel algorithms and P2300">[P2500R2]</a> suggests a different approach for synchronous scheduled parallel
  algorithms.
It proposes that we attach execution policies to schedulers, and then pass
  these policy-aware schedulers as a parameter to the synchronous scheduled
  parallel algorithms, instead of passing a sender parameter as we suggest.</p>
   <p>We believe that attaching execution policies to schedulers or vice versa is not
  a clean design.
We already have a property-like mechanism for attaching things to
  senders/receivers (<code class="highlight"><c- n>get_env</c-></code>, sender attributes, and receiver environments).
If we also allow execution policies to have things attached to them, we’ll end
  up introducing an additional system which may interact poorly with the
  sender/receiver property mechanism.</p>
   <p>Many of the sender/receiver properties are applicable to the synchronous
  scheduled parallel algorithms too.
How do we pass an allocator to a synchronous parallel algorithm?
How do we pass a stop token to a synchronous scheduled parallel algorithm?
Do we attach those to schedulers as well?
We’ll end up needing every sender/receiver property on schedulers as well.</p>
   <p>If both senders/receivers and schedulers can have the same kinds of things
  attached to them, and schedulers can be attached to senders/receivers, we can
  end up with collisions and confusion.
Should an asynchronous parallel algorithm use the execution policy attached to
  the sender/receiver?
Or should it use the execution policy attached to the scheduler that’s attached
  to the sender/receiver?
What if both exist and disagree?</p>
   <p>We have ample experience with property systems on execution policies in the
  Thrust library, which was the inspiration for C++'s parallel algorithms and
  has over 15 years of field experience and hundreds of thousands of users.
Initially, we introduced one property for execution policies, but over time,
  user demand for tuning knobs and controls led to the addition of others.
Today, Thrust’s execution policies can have the moral equivalent of schedulers
  (streams), allocators, and asynchronous dependencies attached to them.
The interactions between these different properties and their lifetime model is
  brittle and difficult to understand.
Our field experience with this model has largely been negative.</p>
   <p>It is much simpler for us to have one property system (the existing one for
  senders/receivers), attach both schedulers and execution policies via that
  property system, and then parameterize both the synchronous and asynchronous
  parallel algorithms on senders, not execution policies.</p>
   <h3 class="heading settled" data-level="5.3" id="alternatives-explicit-properties"><span class="secno">5.3. </span><span class="content">Require Explicit Execution Policy and/or Scheduler Parameters</span><a class="self-link" href="#alternatives-explicit-properties"></a></h3>
   <p>Another alternative design would be to require explicit execution policy and/or
  scheduler parameters on every parallel algorithm invocation.
This might be reasonable, albeit verbose, for the synchronous scheduled
  parallel algorithms.
For the asynchronous parallel algorithms, this would significantly constrain
  their functionality and make it harder to write generic code.</p>
   <p>You may not know which execution policy or scheduler should be used at the time
  that you are calling an asynchronous parallel algorithm; that’s fine, because
  they can be attached later
If you do know that a particular execution policy or scheduler is needed,
  that’s also fine, because you can attach them early.</p>
<pre class="highlight"><c- c1>// Early attaching: We know the types and operations and can decide what to use.</c->
<c- k>auto</c-> <c- n>unique_sort</c-><c- p>(</c-><c- n>vector</c-><c- o>&lt;</c-><c- b>int</c-><c- o>>&amp;</c-> <c- n>v</c-><c- p>)</c-> <c- p>{</c->
  <c- k>return</c-> <c- n>schedule</c-><c- p>(</c-><c- n>system_scheduler</c-><c- p>,</c-> <c- n>par</c-><c- p>)</c-> <c- o>|</c-> <c- n>sort</c-><c- p>(</c-><c- n>v</c-><c- p>)</c-> <c- o>|</c-> <c- n>unique</c-><c- p>(</c-><c- n>v</c-><c- p>);</c->
<c- p>}</c->

<c- c1>// Late attaching: We don't know all the types and operations.</c->
<c- k>auto</c-> <c- n>pattern</c-><c- p>(</c-><c- n>range</c-> <c- k>auto</c-><c- o>&amp;&amp;</c-> <c- n>v</c-><c- p>,</c-> <c- k>auto</c-> <c- n>f</c-><c- p>,</c-> <c- k>auto</c-> <c- n>g</c-><c- p>,</c-> <c- k>auto</c-> <c- n>h</c-><c- p>)</c-> <c- p>{</c->
  <c- k>return</c-> <c- n>just</c-><c- p>()</c-> <c- o>|</c-> <c- n>async</c-><c- o>::</c-><c- n>transform</c-><c- p>(</c-><c- n>v</c-><c- p>,</c-> <c- n>f</c-><c- p>)</c-> <c- o>|</c-> <c- n>async</c-><c- o>::</c-><c- n>then</c-><c- p>(</c-><c- n>g</c-><c- p>)</c-> <c- o>|</c-> <c- n>async</c-><c- o>::</c-><c- n>transform</c-><c- p>(</c-><c- n>v</c-><c- p>,</c-> <c- n>h</c-><c- p>);</c->
<c- p>}</c->

<c- c1>// The decision about execution policy and scheduler to use should be local to</c->
<c- c1>// the knowledge of the types and operations.</c->
<c- k>auto</c-> <c- n>s</c-> <c- o>=</c-> <c- n>on</c-><c- p>(</c-><c- n>sch</c-><c- p>,</c-> <c- n>pol</c-><c- p>,</c-> <c- n>pattern</c-><c- p>(</c-><c- n>d</c-><c- p>,</c-> <c- n>x</c-><c- p>,</c-> <c- n>y</c-><c- p>,</c-> <c- n>z</c-><c- p>));</c->
</pre>
   <p>We do not require that users specify a scheduler for every sender adaptor
  today; we should not require this of asynchronous parallel algorithms either.</p>
   <h2 class="heading settled" data-level="6" id="issues"><span class="secno">6. </span><span class="content">Issues</span><a class="self-link" href="#issues"></a></h2>
   <h3 class="heading settled" data-level="6.1" id="issues-dynamic-asynchrony"><span class="secno">6.1. </span><span class="content">Dynamic Asynchrony</span><a class="self-link" href="#issues-dynamic-asynchrony"></a></h3>
   <p>Asynchrony is dynamic when asynchronous work is enqueued by other asynchronous
  work.
In some parallel programming environments, dynamic asynchrony is either
  expensive or impossible to implement.</p>
   <p>This is a common issue for GPU programming models like CUDA - launching new
  asynchronous work from the GPU is either expensive or impossible.
For CUDA in particular, we have a feature called CUDA Dynamic Parallelism (CDP)
  that allows us to launch new GPU work from the GPU.
The original implementation of CDP had such significant overheads that it was
  almost never used.
CUDA recently released a new CDP model that offers much better overheads - but
  the launch latency is still approximately 2x worse than CPU launch latency.</p>
   <p>Most of the senders framework can be implemented without dynamic asynchrony.
However, that is not the case for <code class="highlight"><c- n>let_value</c-></code>, which is inherently dynamic, as
  we cannot know what the next sender will be until we connect and invoke the
  user-provided invocable.
In our current implementation of CUDA schedulers, <code class="highlight"><c- n>let_value</c-></code> will block until
  the entire predecessor chain completes on the CPU side, introducing a
  substantial latency bubble that destroys performance.
We believe a better implementation is possible using the new CUDA CDP model,
  however this will still have substantial overheads that are best avoided.</p>
   <p>For this reason, the NVIDIA mindset on <code class="highlight"><c- n>let_value</c-></code> has typically been that it
  is to be avoided or used sparingly, as it will have performance issues on our
  platform.
This is one of the reasons we pursued a point-free-style design, as it was
  hoped this would limit the need for <code class="highlight"><c- n>let_value</c-></code>.
However, we do not see a path for an elegant point-free-style design, so we are
  now resigned to using <code class="highlight"><c- n>let_value</c-></code>, despite its performance pitfalls.</p>
   <h3 class="heading settled" data-level="6.2" id="issues-lifetimes"><span class="secno">6.2. </span><span class="content">Lifetimes</span><a class="self-link" href="#issues-lifetimes"></a></h3>
   <p>While most of the serial algorithms take arguments by value, the range-based
  ones tend to take ranges by universal reference.
That’s fine because they complete immediately.
But it may be a bit of a footgun for asynchronous parallel algorithms, just as
  it is for range views.</p>
  </main>
<script>
(function() {
  "use strict";
  var collapseSidebarText = '<span aria-hidden="true">←</span> '
                          + '<span>Collapse Sidebar</span>';
  var expandSidebarText   = '<span aria-hidden="true">→</span> '
                          + '<span>Pop Out Sidebar</span>';
  var tocJumpText         = '<span aria-hidden="true">↑</span> '
                          + '<span>Jump to Table of Contents</span>';

  var sidebarMedia = window.matchMedia('screen and (min-width: 78em)');
  var autoToggle   = function(e){ toggleSidebar(e.matches) };
  if(sidebarMedia.addListener) {
    sidebarMedia.addListener(autoToggle);
  }

  function toggleSidebar(on) {
    if (on == undefined) {
      on = !document.body.classList.contains('toc-sidebar');
    }

    /* Don't scroll to compensate for the ToC if we're above it already. */
    var headY = 0;
    var head = document.querySelector('.head');
    if (head) {
      // terrible approx of "top of ToC"
      headY += head.offsetTop + head.offsetHeight;
    }
    var skipScroll = window.scrollY < headY;

    var toggle = document.getElementById('toc-toggle');
    var tocNav = document.getElementById('toc');
    if (on) {
      var tocHeight = tocNav.offsetHeight;
      document.body.classList.add('toc-sidebar');
      document.body.classList.remove('toc-inline');
      toggle.innerHTML = collapseSidebarText;
      if (!skipScroll) {
        window.scrollBy(0, 0 - tocHeight);
      }
      tocNav.focus();
      sidebarMedia.addListener(autoToggle); // auto-collapse when out of room
    }
    else {
      document.body.classList.add('toc-inline');
      document.body.classList.remove('toc-sidebar');
      toggle.innerHTML = expandSidebarText;
      if (!skipScroll) {
        window.scrollBy(0, tocNav.offsetHeight);
      }
      if (toggle.matches(':hover')) {
        /* Unfocus button when not using keyboard navigation,
           because I don't know where else to send the focus. */
        toggle.blur();
      }
    }
  }

  function createSidebarToggle() {
    /* Create the sidebar toggle in JS; it shouldn't exist when JS is off. */
    var toggle = document.createElement('a');
      /* This should probably be a button, but appearance isn't standards-track.*/
    toggle.id = 'toc-toggle';
    toggle.class = 'toc-toggle';
    toggle.href = '#toc';
    toggle.innerHTML = collapseSidebarText;

    sidebarMedia.addListener(autoToggle);
    var toggler = function(e) {
      e.preventDefault();
      sidebarMedia.removeListener(autoToggle); // persist explicit off states
      toggleSidebar();
      return false;
    }
    toggle.addEventListener('click', toggler, false);


    /* Get <nav id=toc-nav>, or make it if we don't have one. */
    var tocNav = document.getElementById('toc-nav');
    if (!tocNav) {
      tocNav = document.createElement('p');
      tocNav.id = 'toc-nav';
      /* Prepend for better keyboard navigation */
      document.body.insertBefore(tocNav, document.body.firstChild);
    }
    /* While we're at it, make sure we have a Jump to Toc link. */
    var tocJump = document.getElementById('toc-jump');
    if (!tocJump) {
      tocJump = document.createElement('a');
      tocJump.id = 'toc-jump';
      tocJump.href = '#toc';
      tocJump.innerHTML = tocJumpText;
      tocNav.appendChild(tocJump);
    }

    tocNav.appendChild(toggle);
  }

  var toc = document.getElementById('toc');
  if (toc) {
    createSidebarToggle();
    toggleSidebar(sidebarMedia.matches);

    /* If the sidebar has been manually opened and is currently overlaying the text
       (window too small for the MQ to add the margin to body),
       then auto-close the sidebar once you click on something in there. */
    toc.addEventListener('click', function(e) {
      if(e.target.tagName.toLowerCase() == "a" && document.body.classList.contains('toc-sidebar') && !sidebarMedia.matches) {
        toggleSidebar(false);
      }
    }, false);
  }
  else {
    console.warn("Can't find Table of Contents. Please use <nav id='toc'> around the ToC.");
  }

  /* Wrap tables in case they overflow */
  var tables = document.querySelectorAll(':not(.overlarge) > table.data, :not(.overlarge) > table.index');
  var numTables = tables.length;
  for (var i = 0; i < numTables; i++) {
    var table = tables[i];
    var wrapper = document.createElement('div');
    wrapper.className = 'overlarge';
    table.parentNode.insertBefore(wrapper, table);
    wrapper.appendChild(table);
  }

})();
</script>
  <h2 class="no-num no-ref heading settled" id="index"><span class="content">Index</span><a class="self-link" href="#index"></a></h2>
  <h3 class="no-num no-ref heading settled" id="index-defined-here"><span class="content">Terms defined by this specification</span><a class="self-link" href="#index-defined-here"></a></h3>
  <ul class="index">
   <li><a href="#asynchronous-parallel-algorithm">asynchronous parallel algorithm</a><span>, in § 2</span>
   <li><a href="#parallel-algorithm">parallel algorithm</a><span>, in § 2</span>
   <li><a href="#predecessor-sender">predecessor sender</a><span>, in § 2</span>
   <li><a href="#serial-algorithm">serial algorithm</a><span>, in § 2</span>
   <li><a href="#synchronous-parallel-algorithm">synchronous parallel algorithm</a><span>, in § 2</span>
   <li><a href="#synchronous-scheduled-parallel-algorithm">synchronous scheduled parallel algorithm</a><span>, in § 2</span>
   <li><a href="#synchronous-unscheduled-parallel-algorithm">synchronous unscheduled parallel algorithm</a><span>, in § 2</span>
  </ul>
  <h2 class="no-num no-ref heading settled" id="references"><span class="content">References</span><a class="self-link" href="#references"></a></h2>
  <h3 class="no-num no-ref heading settled" id="informative"><span class="content">Informative References</span><a class="self-link" href="#informative"></a></h3>
  <dl>
   <dt id="biblio-p2300r7">[P2300R7]
   <dd>Eric Niebler, Michał Dominiak, Georgy Evtushenko, Lewis Baker, Lucian Radu Teodorescu, Lee Howes, Kirk Shoop, Michael Garland, Bryce Adelstein Lelbach. <a href="https://wg21.link/p2300r7"><cite>`std::execution`</cite></a>. 21 April 2023. URL: <a href="https://wg21.link/p2300r7">https://wg21.link/p2300r7</a>
   <dt id="biblio-p2500r2">[P2500R2]
   <dd>Ruslan Arutyunyan, Alexey Kukanov. <a href="https://wg21.link/p2500r2"><cite>C++ parallel algorithms and P2300</cite></a>. 15 October 2023. URL: <a href="https://wg21.link/p2500r2">https://wg21.link/p2500r2</a>
  </dl>
<script>/* Boilerplate: script-dom-helper */
"use strict";
function query(sel) { return document.querySelector(sel); }

function queryAll(sel) { return [...document.querySelectorAll(sel)]; }

function iter(obj) {
	if(!obj) return [];
	var it = obj[Symbol.iterator];
	if(it) return it;
	return Object.entries(obj);
}

function mk(tagname, attrs, ...children) {
	const el = document.createElement(tagname);
	for(const [k,v] of iter(attrs)) {
		if(k.slice(0,3) == "_on") {
			const eventName = k.slice(3);
			el.addEventListener(eventName, v);
		} else if(k[0] == "_") {
			// property, not attribute
			el[k.slice(1)] = v;
		} else {
			if(v === false || v == null) {
        continue;
      } else if(v === true) {
        el.setAttribute(k, "");
        continue;
      } else {
  			el.setAttribute(k, v);
      }
		}
	}
	append(el, children);
	return el;
}

/* Create shortcuts for every known HTML element */
[
  "a",
  "abbr",
  "acronym",
  "address",
  "applet",
  "area",
  "article",
  "aside",
  "audio",
  "b",
  "base",
  "basefont",
  "bdo",
  "big",
  "blockquote",
  "body",
  "br",
  "button",
  "canvas",
  "caption",
  "center",
  "cite",
  "code",
  "col",
  "colgroup",
  "datalist",
  "dd",
  "del",
  "details",
  "dfn",
  "dialog",
  "div",
  "dl",
  "dt",
  "em",
  "embed",
  "fieldset",
  "figcaption",
  "figure",
  "font",
  "footer",
  "form",
  "frame",
  "frameset",
  "head",
  "header",
  "h1",
  "h2",
  "h3",
  "h4",
  "h5",
  "h6",
  "hr",
  "html",
  "i",
  "iframe",
  "img",
  "input",
  "ins",
  "kbd",
  "label",
  "legend",
  "li",
  "link",
  "main",
  "map",
  "mark",
  "meta",
  "meter",
  "nav",
  "nobr",
  "noscript",
  "object",
  "ol",
  "optgroup",
  "option",
  "output",
  "p",
  "param",
  "pre",
  "progress",
  "q",
  "s",
  "samp",
  "script",
  "section",
  "select",
  "small",
  "source",
  "span",
  "strike",
  "strong",
  "style",
  "sub",
  "summary",
  "sup",
  "table",
  "tbody",
  "td",
  "template",
  "textarea",
  "tfoot",
  "th",
  "thead",
  "time",
  "title",
  "tr",
  "u",
  "ul",
  "var",
  "video",
  "wbr",
  "xmp",
].forEach(tagname=>{
	mk[tagname] = (...args) => mk(tagname, ...args);
});

function* nodesFromChildList(children) {
	for(const child of children.flat(Infinity)) {
		if(child instanceof Node) {
			yield child;
		} else {
			yield new Text(child);
		}
	}
}
function append(el, ...children) {
	for(const child of nodesFromChildList(children)) {
		if(el instanceof Node) el.appendChild(child);
		else el.push(child);
	}
	return el;
}

function insertAfter(el, ...children) {
	for(const child of nodesFromChildList(children)) {
		el.parentNode.insertBefore(child, el.nextSibling);
	}
	return el;
}

function clearContents(el) {
	el.innerHTML = "";
	return el;
}

function parseHTML(markup) {
	if(markup.toLowerCase().trim().indexOf('<!doctype') === 0) {
		const doc = document.implementation.createHTMLDocument("");
		doc.documentElement.innerHTML = markup;
		return doc;
	} else {
		const el = mk.template({});
		el.innerHTML = markup;
		return el.content;
	}
}</script>
<script>/* Boilerplate: script-dfn-panel */
"use strict";
{
let dfnPanelData = {
"asynchronous-parallel-algorithm": {"dfnID":"asynchronous-parallel-algorithm","dfnText":"asynchronous parallel algorithm","external":false,"refSections":[],"url":"#asynchronous-parallel-algorithm"},
"parallel-algorithm": {"dfnID":"parallel-algorithm","dfnText":"parallel algorithm","external":false,"refSections":[],"url":"#parallel-algorithm"},
"predecessor-sender": {"dfnID":"predecessor-sender","dfnText":"predecessor sender","external":false,"refSections":[],"url":"#predecessor-sender"},
"serial-algorithm": {"dfnID":"serial-algorithm","dfnText":"serial algorithm","external":false,"refSections":[],"url":"#serial-algorithm"},
"synchronous-parallel-algorithm": {"dfnID":"synchronous-parallel-algorithm","dfnText":"synchronous parallel algorithm","external":false,"refSections":[],"url":"#synchronous-parallel-algorithm"},
"synchronous-scheduled-parallel-algorithm": {"dfnID":"synchronous-scheduled-parallel-algorithm","dfnText":"synchronous scheduled parallel algorithm","external":false,"refSections":[],"url":"#synchronous-scheduled-parallel-algorithm"},
"synchronous-unscheduled-parallel-algorithm": {"dfnID":"synchronous-unscheduled-parallel-algorithm","dfnText":"synchronous unscheduled parallel algorithm","external":false,"refSections":[],"url":"#synchronous-unscheduled-parallel-algorithm"},
};

document.addEventListener("DOMContentLoaded", ()=>{
    genAllDfnPanels();

    document.body.addEventListener("click", (e) => {
        // If not handled already, just hide all dfn panels.
        hideAllDfnPanels();
    });
});

window.addEventListener("resize", () => {
    // Pin any visible dfn panel
    queryAll(".dfn-panel.on, .dfn-panel.activated").forEach(el=>positionDfnPanel(el));
});

function genAllDfnPanels() {
    for(const panelData of Object.values(dfnPanelData)) {
        const dfnID = panelData.dfnID;
        const dfn = document.getElementById(dfnID);
        if(!dfn) {
            console.log(`Can't find dfn#${dfnID}.`, panelData);
            continue;
        }
        dfn.panelData = panelData;
        insertDfnPopupAction(dfn);
    }
}

function genDfnPanel(dfn, { dfnID, url, dfnText, refSections, external }) {
    const dfnPanel = mk.aside({
        class: "dfn-panel on",
        id: `infopanel-for-${dfnID}`,
        "data-for": dfnID,
        "aria-labelled-by":`infopaneltitle-for-${dfnID}`,
        },
        mk.span({id:`infopaneltitle-for-${dfnID}`, style:"display:none"},
            `Info about the '${dfnText}' ${external?"external":""} reference.`),
        mk.a({href:url, class:"dfn-link"}, url),
        refSections.length == 0 ? [] :
            mk.b({}, "Referenced in:"),
            mk.ul({},
                ...refSections.map(section=>
                    mk.li({},
                        ...section.refs.map((ref, refI)=>
                            [
                                mk.a({ href: `#${ref.id}` },
                                    (refI == 0) ? section.title : `(${refI + 1})`
                                ),
                                " ",
                            ]
                        ),
                    ),
                ),
            ),
        genLinkingSyntaxes(dfn),
    );

    dfnPanel.addEventListener('click', (event) => {
        if (event.target.nodeName == 'A') {
            scrollToTargetAndHighlight(event);
            pinDfnPanel(dfnPanel);
        }
        event.stopPropagation();
        refocusOnTarget(event);
    });
    dfnPanel.addEventListener('keydown', (event) => {
        if(event.keyCode == 27) { // Escape key
            hideDfnPanel({dfnPanel});
            event.stopPropagation();
            event.preventDefault();
        }
    });

    dfnPanel.dfn = dfn;
    dfn.dfnPanel = dfnPanel;
    return dfnPanel;
}



function hideAllDfnPanels() {
    // Delete the currently-active dfn panel.
    queryAll(".dfn-panel").forEach(dfnPanel=>hideDfnPanel({dfnPanel}));
}

function showDfnPanel(dfn) {
    hideAllDfnPanels(); // Only display one at a time.

    dfn.setAttribute("aria-expanded", "true");

    const dfnPanel = genDfnPanel(dfn, dfn.panelData);

    // Give the dfn a unique tabindex, and then
    // give all the tabbable panel bits successive indexes.
    let tabIndex = 100;
    dfn.tabIndex = tabIndex++;
    const tabbable = dfnPanel.querySelectorAll(":is(a, button)");
    for (const el of tabbable) {
        el.tabIndex = tabIndex++;
    }

    append(document.body, dfnPanel);
    positionDfnPanel(dfnPanel);
}

function positionDfnPanel(dfnPanel) {
    const dfn = dfnPanel.dfn;
    const dfnPos = getBounds(dfn);
    dfnPanel.style.top = dfnPos.bottom + "px";
    dfnPanel.style.left = dfnPos.left + "px";

    const panelPos = dfnPanel.getBoundingClientRect();
    const panelMargin = 8;
    const maxRight = document.body.parentNode.clientWidth - panelMargin;
    if (panelPos.right > maxRight) {
        const overflowAmount = panelPos.right - maxRight;
        const newLeft = Math.max(panelMargin, dfnPos.left - overflowAmount);
        dfnPanel.style.left = newLeft + "px";
    }
}

function pinDfnPanel(dfnPanel) {
    // Switch it to "activated" state, which pins it.
    dfnPanel.classList.add("activated");
    dfnPanel.style.position = "fixed";
    dfnPanel.style.left = null;
    dfnPanel.style.top = null;
}

function hideDfnPanel({dfn, dfnPanel}) {
    if(!dfnPanel) dfnPanel = dfn.dfnPanel;
    if(!dfn) dfn = dfnPanel.dfn;
    dfn.dfnPanel = undefined;
    dfnPanel.dfn = undefined;
    dfn.setAttribute("aria-expanded", "false");
    dfn.tabIndex = undefined;
    dfnPanel.remove()
}

function toggleDfnPanel(dfn) {
    if(dfn.dfnPanel) {
        hideDfnPanel(dfn);
    } else {
        showDfnPanel(dfn);
    }
}

function insertDfnPopupAction(dfn) {
    dfn.setAttribute('role', 'button');
    dfn.setAttribute('aria-expanded', 'false')
    dfn.tabIndex = 0;
    dfn.classList.add('has-dfn-panel');
    dfn.addEventListener('click', (event) => {
        toggleDfnPanel(dfn);
        event.stopPropagation();
    });
    dfn.addEventListener('keypress', (event) => {
        const kc = event.keyCode;
        // 32->Space, 13->Enter
        if(kc == 32 || kc == 13) {
            toggleDfnPanel(dfn);
            event.stopPropagation();
            event.preventDefault();
        }
    });
}

function refocusOnTarget(event) {
    const target = event.target;
    setTimeout(() => {
        // Refocus on the event.target element.
        // This is needed after browser scrolls to the destination.
        target.focus();
    });
}

// TODO: shared util
// Returns the root-level absolute position {left and top} of element.
function getBounds(el, relativeTo=document.body) {
    const relativeRect = relativeTo.getBoundingClientRect();
    const elRect = el.getBoundingClientRect();
    const top = elRect.top - relativeRect.top;
    const left = elRect.left - relativeRect.left;
    return {
        top,
        left,
        bottom: top + elRect.height,
        right: left + elRect.width,
    }
}

function scrollToTargetAndHighlight(event) {
    let hash = event.target.hash;
    if (hash) {
        hash = decodeURIComponent(hash.substring(1));
        const dest = document.getElementById(hash);
        if (dest) {
            dest.classList.add('highlighted');
            setTimeout(() => dest.classList.remove('highlighted'), 1000);
        }
    }
}

// Functions, divided by link type, that wrap an autolink's
// contents with the appropriate outer syntax.
// Alternately, a string naming another type they format
// the same as.
function needsFor(type) {
    switch(type) {
        case "descriptor":
        case "value":
        case "element-attr":
        case "attr-value":
        case "element-state":
        case "method":
        case "constructor":
        case "argument":
        case "attribute":
        case "const":
        case "dict-member":
        case "event":
        case "enum-value":
        case "stringifier":
        case "serializer":
        case "iterator":
        case "maplike":
        case "setlike":
        case "state":
        case "mode":
        case "context":
        case "facet": return true;

        default: return false;
    }
}
function refusesFor(type) {
    switch(type) {
        case "property":
        case "element":
        case "interface":
        case "namespace":
        case "callback":
        case "dictionary":
        case "enum":
        case "exception":
        case "typedef":
        case "http-header":
        case "permission": return true;

        default: return false;
    }
}
function linkFormatterFromType(type) {
    switch(type) {
        case 'scheme':
        case 'permission':
        case 'dfn': return (text) => `[=${text}=]`;

        case 'abstract-op': return (text) => `[\$${text}\$]`;

        case 'function':
        case 'at-rule':
        case 'selector':
        case 'value': return (text) => `''${text}''`;

        case 'http-header': return (text) => `[:${text}:]`;

        case 'interface':
        case 'constructor':
        case 'method':
        case 'argument':
        case 'attribute':
        case 'callback':
        case 'dictionary':
        case 'dict-member':
        case 'enum':
        case 'enum-value':
        case 'exception':
        case 'const':
        case 'typedef':
        case 'stringifier':
        case 'serializer':
        case 'iterator':
        case 'maplike':
        case 'setlike':
        case 'extended-attribute':
        case 'event':
        case 'idl': return (text) => `{{${text}}}`;

        case 'element-state':
        case 'element-attr':
        case 'attr-value':
        case 'element': return (element) => `<{${element}}>`;

        case 'grammar': return (text) => `${text} (within a <pre class=prod>)`;

        case 'type': return (text)=> `<<${text}>>`;

        case 'descriptor':
        case 'property': return (text) => `'${text}'`;

        default: return;
    };
};

function genLinkingSyntaxes(dfn) {
    if(dfn.tagName != "DFN") return;

    const type = dfn.getAttribute('data-dfn-type');
    if(!type) {
        console.log(`<dfn> doesn't have a data-dfn-type:`, dfn);
        return [];
    }

    // Return a function that wraps link text based on the type
    const linkFormatter = linkFormatterFromType(type);
    if(!linkFormatter) {
        console.log(`<dfn> has an unknown data-dfn-type:`, dfn);
        return [];
    }

    let ltAlts;
    if(dfn.hasAttribute('data-lt')) {
        ltAlts = dfn.getAttribute('data-lt')
            .split("|")
            .map(x=>x.trim());
    } else {
        ltAlts = [dfn.textContent.trim()];
    }
    if(type == "type") {
        // lt of "<foo>", but "foo" is the interior;
        // <<foo/bar>> is how you write it with a for,
        // not <foo/<bar>> or whatever.
        for(var i = 0; i < ltAlts.length; i++) {
            const lt = ltAlts[i];
            const match = /<(.*)>/.exec(lt);
            if(match) { ltAlts[i] = match[1]; }
        }
    }

    let forAlts;
    if(dfn.hasAttribute('data-dfn-for')) {
        forAlts = dfn.getAttribute('data-dfn-for')
            .split(",")
            .map(x=>x.trim());
    } else {
        forAlts = [''];
    }

    let linkingSyntaxes = [];
    if(!needsFor(type)) {
        for(const lt of ltAlts) {
            linkingSyntaxes.push(linkFormatter(lt));
        }
    }
    if(!refusesFor(type)) {
        for(const f of forAlts) {
            linkingSyntaxes.push(linkFormatter(`${f}/${ltAlts[0]}`))
        }
    }
    return [
        mk.b({}, 'Possible linking syntaxes:'),
        mk.ul({},
            ...linkingSyntaxes.map(link => {
                const copyLink = async () =>
                    await navigator.clipboard.writeText(link);
                return mk.li({},
                    mk.div({ class: 'link-item' },
                        mk.button({
                            class: 'copy-icon', title: 'Copy',
                            type: 'button',
                            _onclick: copyLink,
                            tabindex: 0,
                        }, mk.span({ class: 'icon' }) ),
                        mk.span({}, link)
                    )
                );
            })
        )
    ];
}
}
</script>