<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang xml:lang>
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="mpark/wg21" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <meta name="dcterms.date" content="2024-11-14" />
  <title>Justification for ranges as the output of parallel range
algorithms</title>
  <style>
      code{white-space: pre-wrap;}
      span.smallcaps{font-variant: small-caps;}
      span.underline{text-decoration: underline;}
      div.column{display: inline-block; vertical-align: top; width: 50%;}
      div.csl-block{margin-left: 1.5em;}
      ul.task-list{list-style: none;}
      pre > code.sourceCode { white-space: pre; position: relative; }
      pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
      pre > code.sourceCode > span:empty { height: 1.2em; }
      .sourceCode { overflow: visible; }
      code.sourceCode > span { color: inherit; text-decoration: inherit; }
      div.sourceCode { margin: 1em 0; }
      pre.sourceCode { margin: 0; }
      @media screen {
      div.sourceCode { overflow: auto; }
      }
      @media print {
      pre > code.sourceCode { white-space: pre-wrap; }
      pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
      }
      pre.numberSource code
        { counter-reset: source-line 0; }
      pre.numberSource code > span
        { position: relative; left: -4em; counter-increment: source-line; }
      pre.numberSource code > span > a:first-child::before
        { content: counter(source-line);
          position: relative; left: -1em; text-align: right; vertical-align: baseline;
          border: none; display: inline-block;
          -webkit-touch-callout: none; -webkit-user-select: none;
          -khtml-user-select: none; -moz-user-select: none;
          -ms-user-select: none; user-select: none;
          padding: 0 4px; width: 4em;
          color: #aaaaaa;
        }
      pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa;  padding-left: 4px; }
      div.sourceCode
        {  background-color: #f6f8fa; }
      @media screen {
      pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
      }
      code span { } /* Normal */
      code span.al { color: #ff0000; } /* Alert */
      code span.an { } /* Annotation */
      code span.at { } /* Attribute */
      code span.bn { color: #9f6807; } /* BaseN */
      code span.bu { color: #9f6807; } /* BuiltIn */
      code span.cf { color: #00607c; } /* ControlFlow */
      code span.ch { color: #9f6807; } /* Char */
      code span.cn { } /* Constant */
      code span.co { color: #008000; font-style: italic; } /* Comment */
      code span.cv { color: #008000; font-style: italic; } /* CommentVar */
      code span.do { color: #008000; } /* Documentation */
      code span.dt { color: #00607c; } /* DataType */
      code span.dv { color: #9f6807; } /* DecVal */
      code span.er { color: #ff0000; font-weight: bold; } /* Error */
      code span.ex { } /* Extension */
      code span.fl { color: #9f6807; } /* Float */
      code span.fu { } /* Function */
      code span.im { } /* Import */
      code span.in { color: #008000; } /* Information */
      code span.kw { color: #00607c; } /* Keyword */
      code span.op { color: #af1915; } /* Operator */
      code span.ot { } /* Other */
      code span.pp { color: #6f4e37; } /* Preprocessor */
      code span.re { } /* RegionMarker */
      code span.sc { color: #9f6807; } /* SpecialChar */
      code span.ss { color: #9f6807; } /* SpecialString */
      code span.st { color: #9f6807; } /* String */
      code span.va { } /* Variable */
      code span.vs { color: #9f6807; } /* VerbatimString */
      code span.wa { color: #008000; font-weight: bold; } /* Warning */
      code.diff {color: #898887}
      code.diff span.va {color: #006e28}
      code.diff span.st {color: #bf0303}
  </style>
  <style type="text/css">
body {
margin: 5em;
font-family: serif;

hyphens: auto;
line-height: 1.35;
text-align: justify;
}
@media screen and (max-width: 30em) {
body {
margin: 1.5em;
}
}
div.wrapper {
max-width: 60em;
margin: auto;
}
ul {
list-style-type: none;
padding-left: 2em;
margin-top: -0.2em;
margin-bottom: -0.2em;
}
a {
text-decoration: none;
color: #4183C4;
}
a.hidden_link {
text-decoration: none;
color: inherit;
}
li {
margin-top: 0.6em;
margin-bottom: 0.6em;
}
h1, h2, h3, h4 {
position: relative;
line-height: 1;
}
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;
font-family: sans-serif;
font-weight: normal;
font-size: 83%;
}
a.self-link:hover { opacity: 1; }
a.self-link::before { content: "§"; }
ul > li:before {
content: "\2014";
position: absolute;
margin-left: -1.5em;
}
:target { background-color: #C9FBC9; }
:target .codeblock { background-color: #C9FBC9; }
:target ul { background-color: #C9FBC9; }
.abbr_ref { float: right; }
.folded_abbr_ref { float: right; }
:target .folded_abbr_ref { display: none; }
:target .unfolded_abbr_ref { float: right; display: inherit; }
.unfolded_abbr_ref { display: none; }
.secnum { display: inline-block; min-width: 35pt; }
.header-section-number { display: inline-block; min-width: 35pt; }
.annexnum { display: block; }
div.sourceLinkParent {
float: right;
}
a.sourceLink {
position: absolute;
opacity: 0;
margin-left: 10pt;
}
a.sourceLink:hover {
opacity: 1;
}
a.itemDeclLink {
position: absolute;
font-size: 75%;
text-align: right;
width: 5em;
opacity: 0;
}
a.itemDeclLink:hover { opacity: 1; }
span.marginalizedparent {
position: relative;
left: -5em;
}
li span.marginalizedparent { left: -7em; }
li ul > li span.marginalizedparent { left: -9em; }
li ul > li ul > li span.marginalizedparent { left: -11em; }
li ul > li ul > li ul > li span.marginalizedparent { left: -13em; }
div.footnoteNumberParent {
position: relative;
left: -4.7em;
}
a.marginalized {
position: absolute;
font-size: 75%;
text-align: right;
width: 5em;
}
a.enumerated_item_num {
position: relative;
left: -3.5em;
display: inline-block;
margin-right: -3em;
text-align: right;
width: 3em;
}
div.para { margin-bottom: 0.6em; margin-top: 0.6em; text-align: justify; }
div.section { text-align: justify; }
div.sentence { display: inline; }
span.indexparent {
display: inline;
position: relative;
float: right;
right: -1em;
}
a.index {
position: absolute;
display: none;
}
a.index:before { content: "⟵"; }

a.index:target {
display: inline;
}
.indexitems {
margin-left: 2em;
text-indent: -2em;
}
div.itemdescr {
margin-left: 3em;
}
.bnf {
font-family: serif;
margin-left: 40pt;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
.ncbnf {
font-family: serif;
margin-top: 0.5em;
margin-bottom: 0.5em;
margin-left: 40pt;
}
.ncsimplebnf {
font-family: serif;
font-style: italic;
margin-top: 0.5em;
margin-bottom: 0.5em;
margin-left: 40pt;
background: inherit; 
}
span.textnormal {
font-style: normal;
font-family: serif;
white-space: normal;
display: inline-block;
}
span.rlap {
display: inline-block;
width: 0px;
}
span.descr { font-style: normal; font-family: serif; }
span.grammarterm { font-style: italic; }
span.term { font-style: italic; }
span.terminal { font-family: monospace; font-style: normal; }
span.nonterminal { font-style: italic; }
span.tcode { font-family: monospace; font-style: normal; }
span.textbf { font-weight: bold; }
span.textsc { font-variant: small-caps; }
a.nontermdef { font-style: italic; font-family: serif; }
span.emph { font-style: italic; }
span.techterm { font-style: italic; }
span.mathit { font-style: italic; }
span.mathsf { font-family: sans-serif; }
span.mathrm { font-family: serif; font-style: normal; }
span.textrm { font-family: serif; }
span.textsl { font-style: italic; }
span.mathtt { font-family: monospace; font-style: normal; }
span.mbox { font-family: serif; font-style: normal; }
span.ungap { display: inline-block; width: 2pt; }
span.textit { font-style: italic; }
span.texttt { font-family: monospace; }
span.tcode_in_codeblock { font-family: monospace; font-style: normal; }
span.phantom { color: white; }

span.math { font-style: normal; }
span.mathblock {
display: block;
margin-left: auto;
margin-right: auto;
margin-top: 1.2em;
margin-bottom: 1.2em;
text-align: center;
}
span.mathalpha {
font-style: italic;
}
span.synopsis {
font-weight: bold;
margin-top: 0.5em;
display: block;
}
span.definition {
font-weight: bold;
display: block;
}
.codeblock {
margin-left: 1.2em;
line-height: 127%;
}
.outputblock {
margin-left: 1.2em;
line-height: 127%;
}
div.itemdecl {
margin-top: 2ex;
}
code.itemdeclcode {
white-space: pre;
display: block;
}
span.textsuperscript {
vertical-align: super;
font-size: smaller;
line-height: 0;
}
.footnotenum { vertical-align: super; font-size: smaller; line-height: 0; }
.footnote {
font-size: small;
margin-left: 2em;
margin-right: 2em;
margin-top: 0.6em;
margin-bottom: 0.6em;
}
div.minipage {
display: inline-block;
margin-right: 3em;
}
div.numberedTable {
text-align: center;
margin: 2em;
}
div.figure {
text-align: center;
margin: 2em;
}
table {
border: 1px solid black;
border-collapse: collapse;
margin-left: auto;
margin-right: auto;
margin-top: 0.8em;
text-align: left;
hyphens: none; 
}
td, th {
padding-left: 1em;
padding-right: 1em;
vertical-align: top;
}
td.empty {
padding: 0px;
padding-left: 1px;
}
td.left {
text-align: left;
}
td.right {
text-align: right;
}
td.center {
text-align: center;
}
td.justify {
text-align: justify;
}
td.border {
border-left: 1px solid black;
}
tr.rowsep, td.cline {
border-top: 1px solid black;
}
tr.even, tr.odd {
border-bottom: 1px solid black;
}
tr.capsep {
border-top: 3px solid black;
border-top-style: double;
}
tr.header {
border-bottom: 3px solid black;
border-bottom-style: double;
}
th {
border-bottom: 1px solid black;
}
span.centry {
font-weight: bold;
}
div.table {
display: block;
margin-left: auto;
margin-right: auto;
text-align: center;
width: 90%;
}
span.indented {
display: block;
margin-left: 2em;
margin-bottom: 1em;
margin-top: 1em;
}
ol.enumeratea { list-style-type: none; background: inherit; }
ol.enumerate { list-style-type: none; background: inherit; }

code.sourceCode > span { display: inline; }
</style>
  <link href="data:image/x-icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAVoJEAN6CRADegkQAWIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wCCRAAAgkQAAIJEAACCRAAsgkQAvoJEAP+CRAD/gkQA/4JEAP+CRADAgkQALoJEAACCRAAAgkQAAP///wD///8AgkQAAIJEABSCRACSgkQA/IJEAP99PQD/dzMA/3czAP99PQD/gkQA/4JEAPyCRACUgkQAFIJEAAD///8A////AHw+AFiBQwDqgkQA/4BBAP9/PxP/uZd6/9rJtf/bybX/upd7/39AFP+AQQD/gkQA/4FDAOqAQgBc////AP///wDKklv4jlEa/3o7AP+PWC//8+3o///////////////////////z7un/kFox/35AAP+GRwD/mVYA+v///wD///8A0Zpk+NmibP+0d0T/8evj///////+/fv/1sKz/9bCs//9/fr//////+/m2/+NRwL/nloA/5xYAPj///8A////ANKaZPjRmGH/5cKh////////////k149/3UwAP91MQD/lmQ//86rhv+USg3/m1YA/5hSAP+bVgD4////AP///wDSmmT4zpJY/+/bx///////8+TV/8mLT/+TVx//gkIA/5lVAP+VTAD/x6B//7aEVv/JpH7/s39J+P///wD///8A0ppk+M6SWP/u2sf///////Pj1f/Nj1T/2KFs/8mOUv+eWhD/lEsA/8aee/+0glT/x6F7/7J8Rvj///8A////ANKaZPjRmGH/48Cf///////+/v7/2qt//82PVP/OkFX/37KJ/86siv+USg7/mVQA/5hRAP+bVgD4////AP///wDSmmT40ppk/9CVXP/69O////////7+/v/x4M//8d/P//7+/f//////9u7n/6tnJf+XUgD/nFgA+P///wD///8A0ppk+NKaZP/RmWL/1qNy//r07///////////////////////+vXw/9akdP/Wnmn/y5FY/6JfFvj///8A////ANKaZFTSmmTo0ppk/9GYYv/Ql1//5cWm//Hg0P/x4ND/5cWm/9GXYP/RmGH/0ppk/9KaZOjVnmpY////AP///wDSmmQA0ppkEtKaZI7SmmT60ppk/9CWX//OkVb/zpFW/9CWX//SmmT/0ppk/NKaZJDSmmQS0ppkAP///wD///8A0ppkANKaZADSmmQA0ppkKtKaZLrSmmT/0ppk/9KaZP/SmmT/0ppkvNKaZCrSmmQA0ppkANKaZAD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkUtKaZNzSmmTc0ppkVNKaZADSmmQA0ppkANKaZADSmmQA////AP5/AAD4HwAA4AcAAMADAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAgAEAAMADAADgBwAA+B8AAP5/AAAoAAAAIAAAAEAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wCCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAAyCRACMgkQA6oJEAOqCRACQgkQAEIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wD///8A////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRABigkQA5oJEAP+CRAD/gkQA/4JEAP+CRADqgkQAZoJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAAD///8A////AP///wD///8AgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAA4gkQAwoJEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQAxIJEADyCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAP///wD///8A////AP///wCCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAWgkQAmIJEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAJyCRAAYgkQAAIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wD///8A////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAdIJEAPCCRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAPSCRAB4gkQAAIJEAACCRAAAgkQAAIJEAAD///8A////AP///wD///8AgkQAAIJEAACCRAAAgkQASoJEANKCRAD/gkQA/4JEAP+CRAD/g0YA/39AAP9zLgD/bSQA/2shAP9rIQD/bSQA/3MuAP9/PwD/g0YA/4JEAP+CRAD/gkQA/4JEAP+CRADUgkQAToJEAACCRAAAgkQAAP///wD///8A////AP///wB+PwAAgkUAIoJEAKiCRAD/gkQA/4JEAP+CRAD/hEcA/4BBAP9sIwD/dTAA/5RfKv+viF7/vp56/76ee/+wiF7/lWAr/3YxAP9sIwD/f0AA/4RHAP+CRAD/gkQA/4JEAP+CRAD/gkQArIJEACaBQwAA////AP///wD///8A////AIBCAEBzNAD6f0EA/4NFAP+CRAD/gkQA/4VIAP92MwD/bSUA/6N1Tv/ezsL/////////////////////////////////38/D/6V3Uv9uJgD/dTEA/4VJAP+CRAD/gkQA/4JEAP+BQwD/fUAA/4FDAEj///8A////AP///wD///8AzJRd5qBlKf91NgD/dDUA/4JEAP+FSQD/cy4A/3YyAP/PuKP//////////////////////////////////////////////////////9K7qP94NQD/ciwA/4VJAP+CRAD/fkEA/35BAP+LSwD/mlYA6v///wD///8A////AP///wDdpnL/4qx3/8KJUv+PUhf/cTMA/3AsAP90LgD/4dK+/////////////////////////////////////////////////////////////////+TYxf91MAD/dTIA/31CAP+GRwD/llQA/6FcAP+gWwD8////AP///wD///8A////ANGZY/LSm2X/4ap3/92mcP+wdT3/byQA/8mwj////////////////////////////////////////////////////////////////////////////+LYxv9zLgP/jUoA/59bAP+hXAD/nFgA/5xYAPL///8A////AP///wD///8A0ppk8tKaZP/RmWL/1p9q/9ubXv/XqXj////////////////////////////7+fD/vZyG/6BxS/+gcUr/vJuE//r37f//////////////////////3MOr/5dQBf+dVQD/nVkA/5xYAP+cWAD/nFgA8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/SmWP/yohJ//jo2P//////////////////////4NTG/4JDFf9lGAD/bSQA/20kAP9kGAD/fz8S/+Xb0f//////5NG9/6txN/+LOgD/m1QA/51aAP+cWAD/m1cA/5xYAP+cWADy////AP///wD///8A////ANKaZPLSmmT/0ppk/8+TWf/Unmv//v37//////////////////////+TWRr/VwsA/35AAP+ERgD/g0UA/4JGAP9lHgD/kFga/8KXX/+TRwD/jT4A/49CAP+VTQD/n10A/5xYAP+OQQD/lk4A/55cAPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/y4tO/92yiP//////////////////////8NnE/8eCQP+rcTT/ez0A/3IyAP98PgD/gEMA/5FSAP+USwD/jj8A/5lUAP+JNwD/yqV2/694Mf+HNQD/jkAA/82rf/+laBj/jT4A8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/LiUr/4byY///////////////////////gupX/0I5P/+Wuev/Lklz/l1sj/308AP+QSwD/ol0A/59aAP+aVQD/k0oA/8yoh///////+fXv/6pwO//Lp3v///////Pr4f+oay7y////AP///wD///8A////ANKaZPLSmmT/0ppk/8uJSv/hvJj//////////////////////+G7l//Jhkb/0ppk/96nc//fqXX/x4xO/6dkFP+QSQD/llEA/5xXAP+USgD/yaOA///////38uv/qG05/8ijdv//////8efb/6ZpLPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/zIxO/9yxh///////////////////////7dbA/8iEQf/Sm2X/0Zlj/9ScZv/eqHf/2KJv/7yAQf+XTgD/iToA/5lSAP+JNgD/yKFv/611LP+HNQD/jT8A/8qmeP+kZRT/jT4A8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/Pk1n/1J5q//78+//////////////////+/fv/1aFv/8iEQv/Tm2b/0ppl/9GZY//Wn2z/1pZc/9eldf/Bl2b/kUcA/4w9AP+OQAD/lUwA/59eAP+cWQD/jT8A/5ZOAP+eXADy////AP///wD///8A////ANKaZPLSmmT/0ppk/9KZY//KiEn/8d/P///////////////////////47+f/05tm/8iCP//KiEj/yohJ/8eCP//RmGH//vfy///////n1sP/rXQ7/4k4AP+TTAD/nVoA/5xYAP+cVwD/nFgA/5xYAPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/0ptl/8uLTf/aq37////////////////////////////+/fz/6c2y/961jv/etY7/6Myx//78+v//////////////////////3MWv/5xXD/+ORAD/mFQA/51ZAP+cWAD/nFgA8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/SmmT/0ppk/8mFRP/s1b//////////////////////////////////////////////////////////////////////////////+PD/0JFU/7NzMv+WUQD/kUsA/5tXAP+dWQDy////AP///wD///8A////ANKaZP/SmmT/0ppk/9KaZP/Sm2X/z5NZ/8yMT//z5NX/////////////////////////////////////////////////////////////////9Ofa/8yNUP/UmGH/36p5/8yTWv+qaSD/kksA/5ROAPz///8A////AP///wD///8A0ppk5NKaZP/SmmT/0ppk/9KaZP/TnGf/zY9T/82OUv/t1sD//////////////////////////////////////////////////////+7Yw//OkFX/zI5R/9OcZ//SmmP/26V0/9ymdf/BhUf/ol8R6P///wD///8A////AP///wDSmmQ80ppk9tKaZP/SmmT/0ppk/9KaZP/TnGj/zpFW/8qJSv/dson/8uHS//////////////////////////////////Lj0//etIv/y4lL/86QVf/TnGj/0ppk/9KaZP/RmWP/05xn/9ymdfjUnWdC////AP///wD///8A////ANKaZADSmmQc0ppkotKaZP/SmmT/0ppk/9KaZP/Tm2b/0Zli/8qJSf/NjlH/16Z3/+G8mP/myKr/5siq/+G8mP/Xp3f/zY5S/8qISf/RmGH/05tm/9KaZP/SmmT/0ppk/9KaZP/SmmSm0pljINWdaQD///8A////AP///wD///8A0ppkANKaZADSmmQA0ppkQtKaZMrSmmT/0ppk/9KaZP/SmmT/0ptl/9GYYf/Nj1P/y4lL/8qISP/KiEj/y4lK/82PU//RmGH/0ptl/9KaZP/SmmT/0ppk/9KaZP/SmmTO0ppkRtKaZADSmmQA0ppkAP///wD///8A////AP///wDSmmQA0ppkANKaZADSmmQA0ppkANKaZGzSmmTu0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmTw0ppkcNKaZADSmmQA0ppkANKaZADSmmQA////AP///wD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZBLSmmSQ0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppklNKaZBTSmmQA0ppkANKaZADSmmQA0ppkANKaZAD///8A////AP///wD///8A0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQy0ppkutKaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppkvtKaZDbSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkAP///wD///8A////AP///wDSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkXNKaZODSmmT/0ppk/9KaZP/SmmT/0ppk5NKaZGDSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA////AP///wD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkBtKaZIbSmmTo0ppk6tKaZIrSmmQK0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZAD///8A////AP/8P///+B///+AH//+AAf//AAD//AAAP/AAAA/gAAAHwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA+AAAAfwAAAP/AAAP/8AAP//gAH//+AH///4H////D//" rel="icon" />
  
  <!--[if lt IE 9]>
    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
  <![endif]-->
</head>
<body>
<div class="wrapper">
<header id="title-block-header">
<h1 class="title" style="text-align:center">Justification for ranges as
the output of parallel range algorithms</h1>
<table style="border:none;float:right">
  <tr>
    <td>Document #:</td>
    <td>P3490R0</td>
  </tr>
  <tr>
    <td>Date:</td>
    <td>2024-11-14</td>
  </tr>
  <tr>
    <td style="vertical-align:top">Project:</td>
    <td>Programming Language C++</td>
  </tr>
  <tr>
    <td style="vertical-align:top">Audience:</td>
    <td>
      SG1, SG9<br>
    </td>
  </tr>
  <tr>
    <td style="vertical-align:top">Reply-to:</td>
    <td>
      Alexey Kukanov<br>&lt;<a href="mailto:alexey.kukanov@intel.com" class="email">alexey.kukanov@intel.com</a>&gt;<br>
      Ruslan Arutyunyan<br>&lt;<a href="mailto:ruslan.arutyunyan@intel.com" class="email">ruslan.arutyunyan@intel.com</a>&gt;<br>
    </td>
  </tr>
</table>
</header>
<div style="clear:both">
<div id="TOC" role="doc-toc">
<h1 id="toctitle">Contents</h1>
<ul>
<li><a href="#introduction" id="toc-introduction"><span class="toc-section-number">1</span> Introduction<span></span></a></li>
<li><a href="#recap_motivation" id="toc-recap_motivation"><span class="toc-section-number">2</span> Recap and
motivation<span></span></a></li>
<li><a href="#addressing_concerns" id="toc-addressing_concerns"><span class="toc-section-number">3</span> Addressing the
concerns<span></span></a>
<ul>
<li><a href="#parallel_serial_mismatch" id="toc-parallel_serial_mismatch"><span class="toc-section-number">3.1</span> Mismatch of parallel and serial
range algorithms<span></span></a></li>
<li><a href="#semantic_ambiguity" id="toc-semantic_ambiguity"><span class="toc-section-number">3.2</span> Semantic ambiguity for certain
algorithms<span></span></a></li>
<li><a href="#impact_on_serial" id="toc-impact_on_serial"><span class="toc-section-number">3.3</span> Impact on potential future
improvements<span></span></a></li>
<li><a href="#what_if_not" id="toc-what_if_not"><span class="toc-section-number">3.4</span> What if not accepted for
C++26<span></span></a></li>
</ul></li>
<li><a href="#proposed_api" id="toc-proposed_api"><span class="toc-section-number">4</span> Proposed API<span></span></a></li>
<li><a href="#impl_experience" id="toc-impl_experience"><span class="toc-section-number">5</span> Implementation
experience<span></span></a></li>
<li><a href="#polls" id="toc-polls"><span class="toc-section-number">6</span> Polls<span></span></a>
<ul>
<li><a href="#sg1_sg9_st_louis_2024" id="toc-sg1_sg9_st_louis_2024"><span class="toc-section-number">6.1</span> Joint SG1 + SG9, St. Louis,
2024<span></span></a></li>
</ul></li>
<li><a href="#bibliography" id="toc-bibliography"><span class="toc-section-number">7</span> References<span></span></a></li>
</ul>
</div>
<h1 class="unnumbered unlisted" id="abstract">Abstract<a href="#abstract" class="self-link"></a></h1>
<p>This paper elaborates on the question of using ranges as the output
for parallel range algorithms that we proposed in <span class="citation" data-cites="P3179R2">[<a href="https://wg21.link/p3179r2" role="doc-biblioref">P3179R2</a>]</span>, addressing the raised
concerns. The paper neither proposes nor discusses in detail using
ranges as the output for serial range algorithms.</p>
<h1 data-number="1" id="introduction"><span class="header-section-number">1</span> Introduction<a href="#introduction" class="self-link"></a></h1>
<p>In <span class="citation" data-cites="P3179R2">[<a href="https://wg21.link/p3179r2" role="doc-biblioref">P3179R2</a>]</span> we proposed to add overloads
taking an execution policy to the functions from <span><a href="https://wg21.link/algorithms">[algorithms]</a></span> defined in
the
<code class="sourceCode cpp">std<span class="op">::</span>ranges</code>
namespace. We refer to these overloads as <em>parallel range
algorithms</em>. The proposed design in particular suggested that:</p>
<ul>
<li>Parallel range algorithms take a range, not an iterator, as the
output for the overloads with ranges, and additionally take an output
sentinel for the overloads with iterators and sentinels.</li>
<li>Parallel range algorithms require <code class="sourceCode cpp">random_access_<span class="op">{</span>iterator,range<span class="op">}</span></code>.</li>
<li>At least one of the input sequences as well as the output sequence
must be bounded.</li>
</ul>
<p>The last two of these design decisions were <a href="#sg1_sg9_st_louis_2024">approved</a> at the joint SG1 and SG9
meeting in St. Louis. However, the idea of using ranges for the output
sequences of parallel range algorithms did not gain sufficient support.
The following major concerns were expressed during the meeting as well
as in personal communication:</p>
<ul>
<li>That would introduce a mismatch between serial and parallel range
algorithms so that switching between those will always require more code
changes than just adding an execution policy.</li>
<li>There might be a perception of semantic ambiguity when a whole
container is used as the output for certain algorithms, such as in <code class="sourceCode cpp">std<span class="op">::</span>ranges<span class="op">::</span>copy<span class="op">(</span>policy, vec1, vec2<span class="op">)</span></code>.</li>
<li>That could complicate improvements on the output of serial range
algorithms, which are considered for the future but will not be ready
for C++26.</li>
</ul>
<p>Consequently, in the <span class="citation" data-cites="P3179R3">[<a href="https://wg21.link/p3179r3" role="doc-biblioref">P3179R3</a>]</span> revision we switched back to a
single iterator for representing an output sequence. However, with this
paper we would like to provide new information and discuss the raised
concerns one more time, seeking an opportunity to go forward with the
original idea, which we believe will be a notable improvement.</p>
<h1 data-number="2" id="recap_motivation"><span class="header-section-number">2</span> Recap and motivation<a href="#recap_motivation" class="self-link"></a></h1>
<p>As a recap, we would like to propose parallel algorithms taking a
range as the output for the overloads that take ranges for input.
Similarly, we propose adding a sentinel for the output where the input
is passed as “iterator and sentinel”. See the <a href="#proposed_api">Proposed API</a> section for the examples.</p>
<p>We also propose output ranges to have boundaries set independently of
the input ranges. An algorithm should stop as soon as the end is reached
for the shortest range. The main motivation is to follow established
practices of secure coding, which recommend or even require to always
specify the size of the output in order to prevent out-of-range data
modifications. We think this will not impose any practical limitation on
which ranges can be used for the output of a parallel algorithm, as we
could not find or invent an example of a random-access writeable range
which would also be unbounded.</p>
<p>This <em>range-as-the-output</em> approach should not be confused
with the <code class="sourceCode cpp">output_range</code> concept
already used with a few serial range algorithms.
<code class="sourceCode cpp">output_range</code> would not be enough for
parallel range algorithms, which require random access ranges for the
output parameter, same as for the input one.</p>
<p>The benefits of this approach, comparing to taking a single iterator
for the output, are:</p>
<ul>
<li>It creates a safer API where the modified data sequences have known
bounds. Specifically, the
<code class="sourceCode cpp">sized_range</code> and
<code class="sourceCode cpp">sized_sentinel_for</code> concepts will be
applied to the output sequences in the same way as <span class="citation" data-cites="P3179R3">[<a href="https://wg21.link/p3179r3" role="doc-biblioref">P3179R3</a>]</span> applies those to the input
sequences.</li>
<li>Not for all algorithms the output size is defined by the input size.
An example is <code class="sourceCode cpp">copy_if</code> (and similar
algorithms with <em>filtering</em> semantics), where the output sequence
might be shorter than the input one. Knowing the expected size of the
output may open opportunities for more efficient implementations.</li>
<li>Passing a range for the output makes code a bit simpler in the cases
typical for parallel execution.</li>
</ul>
<p>It is worth noting that to various degrees these reasons are also
applicable to serial range algorithms.</p>
<p>We think that in practice parallel algorithms mainly write the output
data into a container or storage with preallocated space, for efficiency
reasons. So, typically parallel algorithms receive <code class="sourceCode cpp">std<span class="op">::</span>begin<span class="op">(</span>v<span class="op">)</span></code>
or <code class="sourceCode cpp">v<span class="op">.</span>begin<span class="op">()</span></code>
or <code class="sourceCode cpp">v<span class="op">.</span>data<span class="op">()</span></code>
for output, where <code class="sourceCode cpp">v</code> is an instance
of
<code class="sourceCode cpp">std<span class="op">::</span>vector</code>
or
<code class="sourceCode cpp">std<span class="op">::</span>array</code>.
Allowing <code class="sourceCode cpp">v</code> to be passed directly for
output in the same way as for input results in a slightly simpler code.
Here is an example compared to the approach in <span class="citation" data-cites="P3179R3">[<a href="https://wg21.link/p3179r3" role="doc-biblioref">P3179R3</a>]</span>:</p>
<table>
<thead>
<tr class="header">
<th><div style="text-align:center">
<strong>P3179R3</strong>
</div></th>
<th><div style="text-align:center">
<strong>This paper</strong>
</div></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><div>

<div class="sourceCode" id="cb1"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> normalize_parallel<span class="op">(</span>random_access_range <span class="kw">auto</span><span class="op">&amp;&amp;</span> v<span class="op">)</span> <span class="op">{</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> mx <span class="op">=</span> max<span class="op">(</span>execution<span class="op">::</span>par, v<span class="op">)</span>;</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>  transform<span class="op">(</span>execution<span class="op">::</span>par, v, views<span class="op">::</span>repeat<span class="op">(</span>mx<span class="op">)</span>, ranges<span class="op">::</span>begin<span class="op">(</span>v<span class="op">)</span>, divides<span class="op">)</span>;</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>

</div></td>
<td><div>

<div class="sourceCode" id="cb2"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> normalize_parallel<span class="op">(</span>random_access_range <span class="kw">auto</span><span class="op">&amp;&amp;</span> v<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> mx <span class="op">=</span> max<span class="op">(</span>execution<span class="op">::</span>par, v<span class="op">)</span>;</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>  transform<span class="op">(</span>execution<span class="op">::</span>par, v, views<span class="op">::</span>repeat<span class="op">(</span>mx<span class="op">)</span>, v, divides<span class="op">)</span>;</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>

</div></td>
</tr>
</tbody>
</table>
<p>In addition, classes such as <code class="sourceCode cpp">std<span class="op">::</span>back_insert_iterator</code>
or <code class="sourceCode cpp">std<span class="op">::</span>ostream_iterator</code>,
which have no meaningful range interpretation, already cannot be used
with C++17 parallel algorithms that require at least forward iterators.
Code using these types will, in any case, require modifications to
migrate to parallel algorithms.</p>
<p>All in all, we think for parallel algorithms taking ranges and
sentinels for output makes more sense than only taking an iterator.</p>
<p>Of the 89 parallel range algorithms proposed in <span class="citation" data-cites="P3179R3">[<a href="https://wg21.link/p3179r3" role="doc-biblioref">P3179R3</a>]</span> (as counted by names, not
overloads), the changes we discuss here will affect only the following
17 algorithms: <code class="sourceCode cpp">copy</code>,
<code class="sourceCode cpp">copy_if</code>,
<code class="sourceCode cpp">move</code>,
<code class="sourceCode cpp">transform</code>,
<code class="sourceCode cpp">replace_copy</code>,
<code class="sourceCode cpp">replace_copy_if</code>,
<code class="sourceCode cpp">remove_copy</code>,
<code class="sourceCode cpp">remove_copy_if</code>,
<code class="sourceCode cpp">unique_copy</code>,
<code class="sourceCode cpp">reverse_copy</code>,
<code class="sourceCode cpp">rotate_copy</code>,
<code class="sourceCode cpp">partition_copy</code>,
<code class="sourceCode cpp">merge</code>,
<code class="sourceCode cpp">set_union</code>,
<code class="sourceCode cpp">set_intersection</code>,
<code class="sourceCode cpp">set_difference</code>,
<code class="sourceCode cpp">set_symmetric_difference</code>.</p>
<h1 data-number="3" id="addressing_concerns"><span class="header-section-number">3</span> Addressing the concerns<a href="#addressing_concerns" class="self-link"></a></h1>
<h2 data-number="3.1" id="parallel_serial_mismatch"><span class="header-section-number">3.1</span> Mismatch of parallel and serial
range algorithms<a href="#parallel_serial_mismatch" class="self-link"></a></h2>
<p>The first concern we have heard about this approach is the mismatch
between serial and parallel variations. That is, if serial range
algorithms only take iterators for output and parallel range algorithms
only take ranges, switching between those will always require code
changes. That can be resolved by:</p>
<ul>
<li><ol type="A">
<li>adding <em>range-as-the-output</em> to serial range algorithms,</li>
</ol></li>
<li><ol start="2" type="A">
<li>adding <em>iterator-as-the-output</em> to parallel range
algorithms</li>
</ol></li>
</ul>
<p>or both.</p>
<p>The option A would give some of the described benefits to serial
range algorithms as well; one could argue that it would be a useful
addition on its own. However it is obviously too late to pursue this
option for C++26.</p>
<p>The option B does not seem to have benefits besides the aligned
semantics, while it has the downside of some algorithm variations not
being strengthened with the requirement for the output sequence to be
bounded. Nevertheless, given that the option A is not considered for
C++26, we prefer the option B, i.e. supporting both ranges and iterators
as the output of parallel range algorithms, to the status quo of <span class="citation" data-cites="P3179R3">[<a href="https://wg21.link/p3179r3" role="doc-biblioref">P3179R3</a>]</span> that supports only
iterators.</p>
<p>For the “iterator and sentinel” overloads we prefer to always require
a sentinel for output, despite the mismatch with the corresponding
serial overloads. We expect those overloads to be rarely used in
general, and in many cases the C++17 parallel algorithms can be used
instead. Adding a sentinel for output, on the other hand, preserves the
existing approach of expressing a range as the “iterator and sentinel”
pair in algorithm descriptions, as well as allows to specify how the end
of the output sequence is computed for the option B.</p>
<h2 data-number="3.2" id="semantic_ambiguity"><span class="header-section-number">3.2</span> Semantic ambiguity for certain
algorithms<a href="#semantic_ambiguity" class="self-link"></a></h2>
<p>The potential semantic ambiguity can be illustrated by the following
code:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>std<span class="op">::</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> vec1;</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>std<span class="op">::</span>vector<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span> vec2;</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="co">// Some initialization</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>std<span class="op">::</span>ranges<span class="op">::</span>copy<span class="op">(</span>policy, vec1, vec2<span class="op">)</span>;</span></code></pre></div>
<p>for which there might be uncertainty what the behavior is when <code class="sourceCode cpp">vec2<span class="op">.</span>size<span class="op">()</span> <span class="op">!=</span> vec1<span class="op">.</span>size<span class="op">()</span></code>.
Some might expect that <code class="sourceCode cpp">std<span class="op">::</span>ranges<span class="op">::</span>copy</code>
resizes <code class="sourceCode cpp">vec2</code> and makes it an exact
copy of <code class="sourceCode cpp">vec1</code>.</p>
<p>However, the standard already has a precedent in serial variations of
<code class="sourceCode cpp">std<span class="op">::</span>ranges<span class="op">::</span>uninitialized_copy</code>
and <code class="sourceCode cpp">std<span class="op">::</span>ranges<span class="op">::</span>uninitialized_move</code>,
which have the range-as-the-output semantics exactly as we propose:</p>
<ul>
<li>They use ranges (or sentinels) for both input and output
sequences.</li>
<li>They don’t resize the output sequence.</li>
<li>They stop as soon as any of the sequences reaches its end,
copying/moving elements to the minimum of two sizes.</li>
</ul>
<p>Giving another semantics to <code class="sourceCode cpp">std<span class="op">::</span>ranges<span class="op">::</span>copy</code>
etc. would be inconsistent with these already existing algorithms.
Moreover, it would be generally inconsistent with the current
<em>element-wise</em> semantics of both iterator-based and range
algorithms. For example, when <code class="sourceCode cpp">vec2<span class="op">.</span>size<span class="op">()</span></code>
is greater than <code class="sourceCode cpp">vec1<span class="op">.</span>size<span class="op">()</span></code>,
<code class="sourceCode cpp">std<span class="op">::</span>ranges<span class="op">::</span>copy<span class="op">(</span>vec1, vec2<span class="op">.</span>begin<span class="op">())</span></code>
does not modify the elements of <code class="sourceCode cpp">vec2</code>
beyond the size of <code class="sourceCode cpp">vec1</code>, and so
should not <code class="sourceCode cpp">std<span class="op">::</span>ranges<span class="op">::</span>copy<span class="op">(</span>policy, vec1, vec2<span class="op">)</span></code>.</p>
<p>Alternatively, potentially ambiguous names can be modified with some
prefix, such as <code class="sourceCode cpp">partial_</code>. It would
follow <code class="sourceCode cpp">std<span class="op">::</span>ranges<span class="op">::</span>partial_sort_copy</code>,
another range-as-the-output algorithm that already uses the semantics we
propose. That is, we could keep <code class="sourceCode cpp">copy</code>
with iterator-as-the-output and introduce
<code class="sourceCode cpp">partial_copy</code> with
range-as-the-output semantics, and similarly for other algorithms. We do
not recommend this however, as there is no serial algorithms with such
names, and also because it would create very sophisticated names:
<code class="sourceCode cpp">partial_remove_copy_if</code>, really?</p>
<p>Speaking of precedents, it is worth noting that there are more
existing range-as-the-output algorithms -
<code class="sourceCode cpp">fill</code>,
<code class="sourceCode cpp">generate</code>, and
<code class="sourceCode cpp">iota</code>. Their specifics is absence of
input sequences, so the output sequence needs a boundary. However,
extending this principle from algorithms with zero input sequences to
those with one or more seems appropriate.</p>
<h2 data-number="3.3" id="impact_on_serial"><span class="header-section-number">3.3</span> Impact on potential future
improvements<a href="#impact_on_serial" class="self-link"></a></h2>
<p>Finally, let’s discuss if the proposal could interfere somehow with
the anticipated improvements in how the serial range algorithms operate
with output data.</p>
<p>First and foremost, the range-as-the-output approach will for now
only apply to the new function overloads which first parameter is an
execution policy. No modifications to the existing range algorithms are
proposed, so there is no direct impact that would limit possible design
decisions in the future.</p>
<p>But even though the functions for serial and parallel execution have
independent constraints, there is more to consider. If/when the support
for ranges as the output of serial algorithms is added later, will it be
“backward compatible” with what this paper proposes? In other words,
will the transition from a parallel algorithm back to the serial
counterpart be as simple as removal of the execution policy?</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> normalize_serial<span class="op">(</span>range <span class="kw">auto</span><span class="op">&amp;&amp;</span> v<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> mx <span class="op">=</span> max<span class="op">(</span>v<span class="op">)</span>;</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>  transform<span class="op">(</span>v, views<span class="op">::</span>repeat<span class="op">(</span>mx<span class="op">)</span>, v, divides<span class="op">)</span>;</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The answer, we believe, is “Yes”. The reasoning is that parallel
range algorithms impose stricter requirements on their parameters,
narrowing down a possible set of arguments. If a range is accepted by a
parallel range algorithm, it is certainly accepted by a serial range
algorithm, whether for input or for output. Putting it differently, it
will be very strange if serial algorithms somehow exclude random access
sized ranges, a subset of the much broader set of writeable ranges that
could be used as the output.</p>
<p>Another question is whether other anticipated improvements on the
output of serial range algorithms might be applicable for the parallel
ones, and so should be considered in our design. We have found two WG21
papers that speak about modifications of the output for range
algorithms. <span class="citation" data-cites="P2550R0">[<a href="https://wg21.link/p2550r0" role="doc-biblioref">P2550R0</a>]</span> proposes that, citing,</p>
<blockquote>
<ul>
<li>All output algorithms now require
<code class="sourceCode cpp">weak_output_iterator</code>
<ul>
<li>In most cases, that’s re-specifying
<code class="sourceCode cpp">weakly_incrementable</code> and
<code class="sourceCode cpp">indirectly_writable</code> (no requirements
change, just better name)</li>
<li>In some cases, that’s weakening the requirement on those algorithms
that require <code class="sourceCode cpp">output_iterator</code> (all
currently valid code is still valid)</li>
</ul></li>
</ul>
</blockquote>
<p>If that paper is accepted, similar requirement modifications might be
made for parallel range algorithms, both taking iterators and ranges as
the output, and likewise it will not introduce breaking changes for what
we propose in <span class="citation" data-cites="P3179R3">[<a href="https://wg21.link/p3179r3" role="doc-biblioref">P3179R3</a>]</span> and in this paper.</p>
<p><span class="citation" data-cites="P2760R1">[<a href="https://wg21.link/p2760r1" role="doc-biblioref">P2760R1</a>]</span> discusses shortcomings of
<em>output-only</em> iterators, such as <code class="sourceCode cpp">std<span class="op">::</span>back_insert_iterator<span class="op">&lt;</span>C<span class="op">&gt;</span></code>.
While there is no formal proposal yet, the design outlined there uses
two new customization points, <code class="sourceCode cpp">std<span class="op">::</span>ranges<span class="op">::</span>put</code>
and <code class="sourceCode cpp">std<span class="op">::</span>ranges<span class="op">::</span>put_range</code>,
which would make implementations of classes like <code class="sourceCode cpp">std<span class="op">::</span>back_inserter</code>
both simpler and more efficient. Types that customize these CPOs would
not represent a range, not even an iterator; however, the CPOs
themselves would work with output iterators and output ranges. We think
that:</p>
<ul>
<li>Output-only iterators cannot be used with parallel algorithms, so
the use cases that <span class="citation" data-cites="P2760R1">[<a href="https://wg21.link/p2760r1" role="doc-biblioref">P2760R1</a>]</span> targets, like <code class="sourceCode cpp">std<span class="op">::</span>back_inserter</code>,
are already not applicable and filtered out.</li>
<li>Serial range algorithms implemented with these new CPOs should not
introduce breaking changes, and therefore could be used as serial blocks
in the implementation of parallel range algorithms.</li>
<li>Additional types that the new CPOs would enable for the output of
range algorithms can in principle be accepted by parallel range
algorithms if the semantic requirements imposed by execution policies
(such as thread safety) are met. It might be discussed and formalized as
necessary when these CPOs and possible new concepts based on them will
be designed.</li>
</ul>
<p>To summarize, we do not see how our proposal could create any issues
for future improvements related to the output of serial range
algorithms, and vice versa.</p>
<h2 data-number="3.4" id="what_if_not"><span class="header-section-number">3.4</span> What if not accepted for
C++26<a href="#what_if_not" class="self-link"></a></h2>
<p>Since we propose to have both an overload with iterator-as-the-output
and an overload with range-as-the-output for each relevant algorithm, it
appears that overloads with range-as-the-output could be added later.
This is true, though there is a caveat.</p>
<p>If we don’t add the range-as-the-output overload in C++26 that likely
means that we don’t add the output sentinel to the “iterator and
sentinel” counterpart as well. Given the C++ committee’s concerns about
the growing number of algorithm overloads, we are afraid that adding
another one with the output sentinel later will be opposed.</p>
<p>That will have the following consequences:</p>
<ul>
<li>For the “iterator and sentinel” overloads we will not have a bounded
output variation. Having only unbounded one is bad for parallel code, in
our opinion.</li>
<li>There will be no “iterator and sentinel” output pair to fully
express the range overload with.</li>
</ul>
<p>Please look at the “iterator and sentinel” overload in the <a href="#proposed_api">Proposed API</a> to see what API we likely lose in
the standard if the proposal is not accepted.</p>
<h1 data-number="4" id="proposed_api"><span class="header-section-number">4</span> Proposed API<a href="#proposed_api" class="self-link"></a></h1>
<p>Below is an example of modifications to be made to the wording
proposed in <span class="citation" data-cites="P3179R3">[<a href="https://wg21.link/p3179r3" role="doc-biblioref">P3179R3</a>]</span>. The example adjusts the binary
<code class="sourceCode cpp">transform</code> algorithm to use an output
sentinel and adds a new overload taking an output range. Underlines are
used to highlight the key differences introduced by this paper. Similar
modifications will be made to all the mentioned 17 algorithms if the
paper is supported.</p>
<p>Alternatively, we can use an exposition-only
<code class="sourceCode cpp"><em>range-or-iterator</em></code> concept
that combines the requirements for both a range and an iterator by
logical disjunction, as its name suggests. We did not explore which way
makes more sense; at glance, there seems to be little practical
difference for library implementers.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> ExecutionPolicy,</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>         random_access_iterator I1, sentinel_for<span class="op">&lt;</span>I1<span class="op">&gt;</span> S1,</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>         random_access_iterator I2, sentinel_for<span class="op">&lt;</span>I2<span class="op">&gt;</span> S2,</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>         random_access_iterator O, <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">sized_sentinel_for&lt;O&gt; SO,</code></span></ins></span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>         copy_constructible F,</span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>         <span class="kw">class</span> Proj1 <span class="op">=</span> identity, <span class="kw">class</span> Proj2 <span class="op">=</span> identity<span class="op">&gt;</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="kw">requires</span> indirectly_writable<span class="op">&lt;</span>O,</span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>             indirect_result_t<span class="op">&lt;</span>F<span class="op">&amp;</span>, projected<span class="op">&lt;</span>I1, Proj1<span class="op">&gt;</span>, projected<span class="op">&lt;</span>I2, Proj2<span class="op">&gt;&gt;&gt;</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>         <span class="op">&amp;&amp;</span> <span class="op">(</span>sized_sentinel_for<span class="op">&lt;</span>S1, I1<span class="op">&gt;</span> <span class="op">||</span> sized_sentinel_for<span class="op">&lt;</span>S2, I2<span class="op">&gt;)</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a><span class="kw">constexpr</span> binary_transform_result<span class="op">&lt;</span>I1, I2, O<span class="op">&gt;</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>    transform<span class="op">(</span>ExecutionPolicy<span class="op">&amp;&amp;</span> policy, I1 first1, S1 last1, I2 first2, S2 last2, O result,</span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>              <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">SO result_last,</code></span></ins></span> F binary_op, Proj1 proj1 <span class="op">=</span> <span class="op">{}</span>, Proj2 proj2 <span class="op">=</span> <span class="op">{})</span>;</span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> ExecutionPolicy,</span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a>         ranges<span class="op">::</span>random_access_range R1,</span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a>         ranges<span class="op">::</span>random_access_range R2,</span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a>         <u><code class="sourceCode cpp">random_access_iterator O</code></u>,</span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a>         copy_constructible F,</span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a>         <span class="kw">class</span> Proj1 <span class="op">=</span> identity, <span class="kw">class</span> Proj2 <span class="op">=</span> identity<span class="op">&gt;</span></span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a><span class="kw">requires</span> indirectly_writable<span class="op">&lt;</span>O,</span>
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true" tabindex="-1"></a>             indirect_result_t<span class="op">&lt;</span>F<span class="op">&amp;</span>,</span>
<span id="cb5-22"><a href="#cb5-22" aria-hidden="true" tabindex="-1"></a>                 projected<span class="op">&lt;</span>ranges<span class="op">::</span>iterator_t<span class="op">&lt;</span>R1<span class="op">&gt;</span>, Proj1<span class="op">&gt;</span>,</span>
<span id="cb5-23"><a href="#cb5-23" aria-hidden="true" tabindex="-1"></a>                 projected<span class="op">&lt;</span>ranges<span class="op">::</span>iterator_t<span class="op">&lt;</span>R2<span class="op">&gt;</span>, Proj2<span class="op">&gt;&gt;&gt;</span></span>
<span id="cb5-24"><a href="#cb5-24" aria-hidden="true" tabindex="-1"></a>         <span class="op">&amp;&amp;</span> <span class="op">(</span>sized_range<span class="op">&lt;</span>R1<span class="op">&gt;</span> <span class="op">||</span> sized_range<span class="op">&lt;</span>R2<span class="op">&gt;)</span></span>
<span id="cb5-25"><a href="#cb5-25" aria-hidden="true" tabindex="-1"></a><span class="kw">constexpr</span> binary_transform_result<span class="op">&lt;</span>ranges<span class="op">::</span>borrowed_iterator_t<span class="op">&lt;</span>R1<span class="op">&gt;</span>,</span>
<span id="cb5-26"><a href="#cb5-26" aria-hidden="true" tabindex="-1"></a>                                  ranges<span class="op">::</span>borrowed_iterator_t<span class="op">&lt;</span>R2<span class="op">&gt;</span>,</span>
<span id="cb5-27"><a href="#cb5-27" aria-hidden="true" tabindex="-1"></a>                                  O<span class="op">&gt;</span></span>
<span id="cb5-28"><a href="#cb5-28" aria-hidden="true" tabindex="-1"></a>    transform<span class="op">(</span>ExecutionPolicy<span class="op">&amp;&amp;</span> policy, R1<span class="op">&amp;&amp;</span> r1, R2<span class="op">&amp;&amp;</span> r2, O result, F binary_op,</span>
<span id="cb5-29"><a href="#cb5-29" aria-hidden="true" tabindex="-1"></a>              Proj1 proj1 <span class="op">=</span> <span class="op">{}</span>, Proj2 proj2 <span class="op">=</span> <span class="op">{})</span>;</span></code></pre></div>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb6"><pre class="sourceCode default cpp"><code class="sourceCode default"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>template&lt;typename ExecutionPolicy,</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>         ranges::random_access_range R1,</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>         ranges::random_access_range R2,</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>         <u><code class="sourceCode cpp">ranges<span class="op">::</span>random_access_range OutR</code></u>,</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>         copy_constructible F,</span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>         class Proj1 = identity, class Proj2 = identity&gt;</span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>requires indirectly_writable&lt;<u><code class="sourceCode cpp">ranges<span class="op">::</span>iterator_t<span class="op">&lt;</span>OutR<span class="op">&gt;</span></code></u>,</span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>             indirect_result_t&lt;F&amp;,</span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>                 projected&lt;ranges::iterator_t&lt;R1&gt;, Proj1&gt;,</span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>                 projected&lt;ranges::iterator_t&lt;R2&gt;, Proj2&gt;&gt;&gt;</span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>         &amp;&amp; (sized_range&lt;R1&gt; || sized_range&lt;R2&gt;) <u><code class="sourceCode cpp"><span class="op">&amp;&amp;</span> sized_range<span class="op">&lt;</span>OutR<span class="op">&gt;</span></code></u></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a>constexpr binary_transform_result&lt;ranges::borrowed_iterator_t&lt;R1&gt;,</span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a>                                  ranges::borrowed_iterator_t&lt;R2&gt;,</span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a>                                  <u><code class="sourceCode cpp">ranges<span class="op">::</span>borrowed_iterator_t<span class="op">&lt;</span>OutR<span class="op">&gt;</span></code></u>&gt;</span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a>    transform(ExecutionPolicy&amp;&amp; policy, R1&amp;&amp; r1, R2&amp;&amp; r2, <u><code class="sourceCode cpp">OutR<span class="op">&amp;&amp;</span></code></u> result, F binary_op,</span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a>              Proj1 proj1 = {}, Proj2 proj2 = {});</span></code></pre></div>

</div>
<h1 data-number="5" id="impl_experience"><span class="header-section-number">5</span> Implementation experience<a href="#impl_experience" class="self-link"></a></h1>
<p>The oneAPI DPC++ Library (oneDPL) <a href="https://oneapi-src.github.io/oneDPL/parallel_api/parallel_range_algorithms.html">developer
guide</a> covers parallel range algorithms we’ve implemented so far. The
oneAPI specification provides <a href="https://github.com/uxlfoundation/oneAPI-spec/blob/main/source/elements/oneDPL/source/parallel_api/parallel_range_api.rst">formal
signatures</a> of these algorithms. The implementation supports
execution policies for CPUs (semantically aligned with the C++ standard)
and for SYCL devices, and it works with a subset of the standard C++
views.</p>
<p>We use the range-as-the-output approach where applicable: in
<code class="sourceCode cpp">transform</code>,
<code class="sourceCode cpp">copy</code>,
<code class="sourceCode cpp">copy_if</code>, and
<code class="sourceCode cpp">merge</code>. We don’t have
iterator-as-the-output overloads for these algorithms, as <a href="#parallel_serial_mismatch">the motivation for it</a> applies much
less to oneDPL. However, we don’t foresee any issues with adding such
overloads later, if necessary.</p>
<h1 data-number="6" id="polls"><span class="header-section-number">6</span> Polls<a href="#polls" class="self-link"></a></h1>
<h2 data-number="6.1" id="sg1_sg9_st_louis_2024"><span class="header-section-number">6.1</span> Joint SG1 + SG9, St. Louis,
2024<a href="#sg1_sg9_st_louis_2024" class="self-link"></a></h2>
<p><strong>Poll</strong>: Continue work on P3179R2 for IS’26 with these
notes:</p>
<ol type="1">
<li>RandomAccess for inputs and outputs</li>
<li>Iterators for outputs</li>
<li>We believe the overloads are worth it</li>
</ol>
<table style="width:35%;">
<colgroup>
<col style="width: 6%" />
<col style="width: 6%" />
<col style="width: 6%" />
<col style="width: 6%" />
<col style="width: 6%" />
</colgroup>
<thead>
<tr class="header">
<th style="text-align: center;"><div style="text-align:center">
<strong>SF</strong>
</div></th>
<th style="text-align: center;"><div style="text-align:center">
<strong>F</strong>
</div></th>
<th style="text-align: center;"><div style="text-align:center">
<strong>N</strong>
</div></th>
<th style="text-align: center;"><div style="text-align:center">
<strong>A</strong>
</div></th>
<th style="text-align: center;"><div style="text-align:center">
<strong>SA</strong>
</div></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td style="text-align: center;">7</td>
<td style="text-align: center;">4</td>
<td style="text-align: center;">2</td>
<td style="text-align: center;">1</td>
<td style="text-align: center;">0</td>
</tr>
</tbody>
</table>
<h1 data-number="7" id="bibliography"><span class="header-section-number">7</span> References<a href="#bibliography" class="self-link"></a></h1>
<div id="refs" class="references csl-bib-body hanging-indent" data-entry-spacing="1" role="doc-bibliography">
<div id="ref-P2550R0" class="csl-entry" role="doc-biblioentry">
[P2550R0] Barry Revzin. 2022-02-17. ranges::copy should say
output_iterator somewhere. <a href="https://wg21.link/p2550r0"><div class="csl-block">https://wg21.link/p2550r0</div></a>
</div>
<div id="ref-P2760R1" class="csl-entry" role="doc-biblioentry">
[P2760R1] Barry Revzin. 2023-12-15. A Plan for C++26 Ranges. <a href="https://wg21.link/p2760r1"><div class="csl-block">https://wg21.link/p2760r1</div></a>
</div>
<div id="ref-P3179R2" class="csl-entry" role="doc-biblioentry">
[P3179R2] Ruslan Arutyunyan, Alexey Kukanov, Bryce Adelstein Lelbach.
2024-06-25. C++ parallel range algorithms. <a href="https://wg21.link/p3179r2"><div class="csl-block">https://wg21.link/p3179r2</div></a>
</div>
<div id="ref-P3179R3" class="csl-entry" role="doc-biblioentry">
[P3179R3] Ruslan Arutyunyan, Alexey Kukanov, Bryce Adelstein Lelbach.
2024-10-16. C++ parallel range algorithms. <a href="https://wg21.link/p3179r3"><div class="csl-block">https://wg21.link/p3179r3</div></a>
</div>
</div>
</div>
</div>
</body>
</html>
