1 /*
   2  * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package java.util.stream;
  26 
  27 import java.util.ArrayDeque;
  28 import java.util.Arrays;
  29 import java.util.Collection;
  30 import java.util.Deque;
  31 import java.util.List;
  32 import java.util.Objects;
  33 import java.util.Spliterator;
  34 import java.util.Spliterators;
  35 import java.util.concurrent.CountedCompleter;
  36 import java.util.function.Consumer;
  37 import java.util.function.DoubleConsumer;
  38 import java.util.function.IntConsumer;
  39 import java.util.function.IntFunction;
  40 import java.util.function.LongConsumer;
  41 
  42 import static java.util.stream.Collectors.toStringJoiner;
  43 
  44 /**
  45  * Factory methods for constructing implementations of {@link Node} and
  46  * {@link Node.Builder} and their primitive specializations.  Fork/Join tasks
  47  * for collecting output from a {@link PipelineHelper} to a {@link Node} and
  48  * flattening {@link Node}s.
  49  *
  50  * @since 1.8
  51  */
  52 final class Nodes {
  53 
  54     private Nodes() {
  55         throw new Error("no instances");
  56     }
  57 
  58     /**
  59      * The maximum size of an array that can be allocated.
  60      */
  61     static final long MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
  62 
  63     private static final Node EMPTY_NODE = new EmptyNode.OfRef();
  64     private static final Node.OfInt EMPTY_INT_NODE = new EmptyNode.OfInt();
  65     private static final Node.OfLong EMPTY_LONG_NODE = new EmptyNode.OfLong();
  66     private static final Node.OfDouble EMPTY_DOUBLE_NODE = new EmptyNode.OfDouble();
  67 
  68     // General shape-based node creation methods
  69 
  70     /**
  71      * Produces an empty node whose count is zero, has no children and no content.
  72      *
  73      * @param shape the shape of the node to be created.
  74      * @param <T> the type of elements of the created node.
  75      * @return an empty node.
  76      */
  77     @SuppressWarnings("unchecked")
  78     static <T> Node<T> emptyNode(StreamShape shape) {
  79         switch (shape) {
  80             case REFERENCE:    return (Node<T>) EMPTY_NODE;
  81             case INT_VALUE:    return (Node<T>) EMPTY_INT_NODE;
  82             case LONG_VALUE:   return (Node<T>) EMPTY_LONG_NODE;
  83             case DOUBLE_VALUE: return (Node<T>) EMPTY_DOUBLE_NODE;
  84             default:
  85                 throw new IllegalStateException("Unknown shape " + shape);
  86         }
  87     }
  88 
  89     /**
  90      * Produces a concatenated {@link Node} that has two or more children.
  91      * <p>The count of the concatenated node is equal to the sum of the count
  92      * of each child. Traversal of the concatenated node traverses the content
  93      * of each child in encounter order of the list of children. Splitting a
  94      * spliterator obtained from the concatenated node preserves the encounter
  95      * order of the list of children.
  96      *
  97      * <p>The result may be a concatenated node, the input sole node if the size
  98      * of the list is 1, or an empty node.
  99      *
 100      * @param shape the shape of the concatenated node to be created
 101      * @param nodes the input nodes
 102      * @param <T> the type of elements of the concatenated node
 103      * @return a {@code Node} covering the elements of the input nodes
 104      * @throws IllegalStateException if all {@link Node} elements of the list
 105      * are an not instance of type supported by this factory.
 106      */
 107     @SuppressWarnings("unchecked")
 108     static <T> Node<T> conc(StreamShape shape, List<? extends Node<T>> nodes) {
 109         int size = nodes.size();
 110         if (size == 0)
 111             return emptyNode(shape);
 112         else if (size == 1)
 113             return nodes.get(0);
 114         else {
 115             // Create a right-balanced tree when there are more that 2 nodes
 116             switch (shape) {
 117                 case REFERENCE: {
 118                     List<Node<T>> refNodes = (List<Node<T>>) nodes;
 119                     ConcNode<T> c = new ConcNode<>(refNodes.get(size - 2), refNodes.get(size - 1));
 120                     for (int i = size - 3; i >= 0; i--) {
 121                         c = new ConcNode<>(refNodes.get(i), c);
 122                     }
 123                     return c;
 124                 }
 125                 case INT_VALUE: {
 126                     List<? extends Node.OfInt> intNodes = (List<? extends Node.OfInt>) nodes;
 127                     IntConcNode c = new IntConcNode(intNodes.get(size - 2), intNodes.get(size - 1));
 128                     for (int i = size - 3; i >= 0; i--) {
 129                         c = new IntConcNode(intNodes.get(i), c);
 130                     }
 131                     return (Node<T>) c;
 132                 }
 133                 case LONG_VALUE: {
 134                     List<? extends Node.OfLong> longNodes = (List<? extends Node.OfLong>) nodes;
 135                     LongConcNode c = new LongConcNode(longNodes.get(size - 2), longNodes.get(size - 1));
 136                     for (int i = size - 3; i >= 0; i--) {
 137                         c = new LongConcNode(longNodes.get(i), c);
 138                     }
 139                     return (Node<T>) c;
 140                 }
 141                 case DOUBLE_VALUE: {
 142                     List<? extends Node.OfDouble> doubleNodes = (List<? extends Node.OfDouble>) nodes;
 143                     DoubleConcNode c = new DoubleConcNode(doubleNodes.get(size - 2), doubleNodes.get(size - 1));
 144                     for (int i = size - 3; i >= 0; i--) {
 145                         c = new DoubleConcNode(doubleNodes.get(i), c);
 146                     }
 147                     return (Node<T>) c;
 148                 }
 149                 default:
 150                     throw new IllegalStateException("Unknown shape " + shape);
 151             }
 152         }
 153 
 154     }
 155 
 156     /**
 157      * Truncate a {@link Node}, returning a node describing a subsequence of
 158      * the contents of the input node.
 159      *
 160      * @param input the input node
 161      * @param from the starting offset to include in the truncated node (inclusive)
 162      * @param to the ending offset ot include in the truncated node (exclusive)
 163      * @param generator the array factory (only used for reference nodes)
 164      * @param <T> the type of elements of the input node and truncated node
 165      * @return the truncated node
 166      */
 167     @SuppressWarnings("unchecked")
 168     static <T> Node<T> truncateNode(Node<T> input, long from, long to, IntFunction<T[]> generator) {
 169         StreamShape shape = input.getShape();
 170         long size = truncatedSize(input.count(), from, to);
 171         if (size == 0)
 172             return emptyNode(shape);
 173         else if (from == 0 && to >= input.count())
 174             return input;
 175 
 176         switch (shape) {
 177             case REFERENCE: {
 178                 Spliterator<T> spliterator = input.spliterator();
 179                 Node.Builder<T> nodeBuilder = Nodes.builder(size, generator);
 180                 nodeBuilder.begin(size);
 181                 for (int i = 0; i < from && spliterator.tryAdvance(e -> { }); i++) { }
 182                 for (int i = 0; (i < size) && spliterator.tryAdvance(nodeBuilder); i++) { }
 183                 nodeBuilder.end();
 184                 return nodeBuilder.build();
 185             }
 186             case INT_VALUE: {
 187                 Spliterator.OfInt spliterator = ((Node.OfInt) input).spliterator();
 188                 Node.Builder.OfInt nodeBuilder = Nodes.intBuilder(size);
 189                 nodeBuilder.begin(size);
 190                 for (int i = 0; i < from && spliterator.tryAdvance((IntConsumer) e -> { }); i++) { }
 191                 for (int i = 0; (i < size) && spliterator.tryAdvance((IntConsumer) nodeBuilder); i++) { }
 192                 nodeBuilder.end();
 193                 return (Node<T>) nodeBuilder.build();
 194             }
 195             case LONG_VALUE: {
 196                 Spliterator.OfLong spliterator = ((Node.OfLong) input).spliterator();
 197                 Node.Builder.OfLong nodeBuilder = Nodes.longBuilder(size);
 198                 nodeBuilder.begin(size);
 199                 for (int i = 0; i < from && spliterator.tryAdvance((LongConsumer) e -> { }); i++) { }
 200                 for (int i = 0; (i < size) && spliterator.tryAdvance((LongConsumer) nodeBuilder); i++) { }
 201                 nodeBuilder.end();
 202                 return (Node<T>) nodeBuilder.build();
 203             }
 204             case DOUBLE_VALUE: {
 205                 Spliterator.OfDouble spliterator = ((Node.OfDouble) input).spliterator();
 206                 Node.Builder.OfDouble nodeBuilder = Nodes.doubleBuilder(size);
 207                 nodeBuilder.begin(size);
 208                 for (int i = 0; i < from && spliterator.tryAdvance((DoubleConsumer) e -> { }); i++) { }
 209                 for (int i = 0; (i < size) && spliterator.tryAdvance((DoubleConsumer) nodeBuilder); i++) { }
 210                 nodeBuilder.end();
 211                 return (Node<T>) nodeBuilder.build();
 212             }
 213             default:
 214                 throw new IllegalStateException("Unknown shape " + shape);
 215         }
 216     }
 217 
 218     private static long truncatedSize(long size, long from, long to) {
 219         if (from >= 0)
 220             size = Math.max(0, size - from);
 221         long limit = to - from;
 222         if (limit >= 0)
 223             size = Math.min(size, limit);
 224         return size;
 225     }
 226 
 227     // Reference-based node methods
 228 
 229     /**
 230      * Produces a {@link Node} describing an array.
 231      * <p>
 232      * The node will hold a reference to the array and will not make a copy.
 233      *
 234      * @param array the array.
 235      * @param <T> the type of elements held by the node
 236      * @return a node holding an array
 237      */
 238     static<T> Node<T> node(T[] array) {
 239         return new ArrayNode<>(array);
 240     }
 241 
 242     /**
 243      * Produces a {@link Node} describing a {@link Collection}.
 244      * <p>
 245      * The node will hold a reference to the collection and will not make a copy.
 246      *
 247      * @param c the collection.
 248      * @param <T> the type of elements held by the node.
 249      * @return a node holding a collection.
 250      */
 251     static<T> Node<T> node(Collection<T> c) {
 252         return new CollectionNode<>(c);
 253     }
 254 
 255     /**
 256      * Produces a {@link Node.Builder}.
 257      *
 258      * @param exactSizeIfKnown -1 if a variable size builder is requested,
 259      * otherwise the exact capacity desired.  A fixed capacity builder will
 260      * fail if the wrong number of elements are added to the builder.
 261      * @param generator the array factory
 262      * @param <T> the type of elements of the node builder
 263      * @return a {@code Node.Builder}
 264      */
 265     static <T> Node.Builder<T> builder(long exactSizeIfKnown, IntFunction<T[]> generator) {
 266         return (exactSizeIfKnown >= 0 && exactSizeIfKnown < MAX_ARRAY_SIZE)
 267                ? new FixedNodeBuilder<>(exactSizeIfKnown, generator)
 268                : builder();
 269     }
 270 
 271     /**
 272      * Produces a variable size @{link Node.Builder}.
 273      *
 274      * @param <T> the type of elements of the node builder
 275      * @return a {@code Node.Builder}
 276      */
 277     static <T> Node.Builder<T> builder() {
 278         return new SpinedNodeBuilder<>();
 279     }
 280 
 281     // Int nodes
 282 
 283     /**
 284      * Produces a {@link Node.OfInt} describing an int[] array.
 285      * <p>
 286      * The node will hold a reference to the array and will not make a copy.
 287      *
 288      * @param array the array
 289      * @return a node holding an array
 290      */
 291     static Node.OfInt node(int[] array) {
 292         return new IntArrayNode(array);
 293     }
 294 
 295     /**
 296      * Produces a {@link Node.Builder.OfInt}.
 297      *
 298      * @param exactSizeIfKnown -1 if a variable size builder is requested,
 299      * otherwise the exact capacity desired.  A fixed capacity builder will
 300      * fail if the wrong number of elements are added to the builder.
 301      * @return a {@code Node.Builder.OfInt}
 302      */
 303     static Node.Builder.OfInt intBuilder(long exactSizeIfKnown) {
 304         return (exactSizeIfKnown >= 0 && exactSizeIfKnown < MAX_ARRAY_SIZE)
 305                ? new IntFixedNodeBuilder(exactSizeIfKnown)
 306                : intBuilder();
 307     }
 308 
 309     /**
 310      * Produces a variable size @{link Node.Builder.OfInt}.
 311      *
 312      * @return a {@code Node.Builder.OfInt}
 313      */
 314     static Node.Builder.OfInt intBuilder() {
 315         return new IntSpinedNodeBuilder();
 316     }
 317 
 318     // Long nodes
 319 
 320     /**
 321      * Produces a {@link Node.OfLong} describing a long[] array.
 322      * <p>
 323      * The node will hold a reference to the array and will not make a copy.
 324      *
 325      * @param array the array
 326      * @return a node holding an array
 327      */
 328     static Node.OfLong node(final long[] array) {
 329         return new LongArrayNode(array);
 330     }
 331 
 332     /**
 333      * Produces a {@link Node.Builder.OfLong}.
 334      *
 335      * @param exactSizeIfKnown -1 if a variable size builder is requested,
 336      * otherwise the exact capacity desired.  A fixed capacity builder will
 337      * fail if the wrong number of elements are added to the builder.
 338      * @return a {@code Node.Builder.OfLong}
 339      */
 340     static Node.Builder.OfLong longBuilder(long exactSizeIfKnown) {
 341         return (exactSizeIfKnown >= 0 && exactSizeIfKnown < MAX_ARRAY_SIZE)
 342                ? new LongFixedNodeBuilder(exactSizeIfKnown)
 343                : longBuilder();
 344     }
 345 
 346     /**
 347      * Produces a variable size @{link Node.Builder.OfLong}.
 348      *
 349      * @return a {@code Node.Builder.OfLong}
 350      */
 351     static Node.Builder.OfLong longBuilder() {
 352         return new LongSpinedNodeBuilder();
 353     }
 354 
 355     // Double nodes
 356 
 357     /**
 358      * Produces a {@link Node.OfDouble} describing a double[] array.
 359      * <p>
 360      * The node will hold a reference to the array and will not make a copy.
 361      *
 362      * @param array the array
 363      * @return a node holding an array
 364      */
 365     static Node.OfDouble node(final double[] array) {
 366         return new DoubleArrayNode(array);
 367     }
 368 
 369     /**
 370      * Produces a {@link Node.Builder.OfDouble}.
 371      *
 372      * @param exactSizeIfKnown -1 if a variable size builder is requested,
 373      * otherwise the exact capacity desired.  A fixed capacity builder will
 374      * fail if the wrong number of elements are added to the builder.
 375      * @return a {@code Node.Builder.OfDouble}
 376      */
 377     static Node.Builder.OfDouble doubleBuilder(long exactSizeIfKnown) {
 378         return (exactSizeIfKnown >= 0 && exactSizeIfKnown < MAX_ARRAY_SIZE)
 379                ? new DoubleFixedNodeBuilder(exactSizeIfKnown)
 380                : doubleBuilder();
 381     }
 382 
 383     /**
 384      * Produces a variable size @{link Node.Builder.OfDouble}.
 385      *
 386      * @return a {@code Node.Builder.OfDouble}
 387      */
 388     static Node.Builder.OfDouble doubleBuilder() {
 389         return new DoubleSpinedNodeBuilder();
 390     }
 391 
 392     // Parallel evaluation of pipelines to nodes
 393 
 394     /**
 395      * Collect, in parallel, elements output from a pipeline and describe those
 396      * elements with a {@link Node}.
 397      *
 398      * @implSpec
 399      * If the exact size of the output from the pipeline is known and the source
 400      * {@link Spliterator} has the {@link Spliterator#SUBSIZED} characteristic,
 401      * then a flat {@link Node} will be returned whose content is an array,
 402      * since the size is known the array can be constructed in advance and
 403      * output elements can be placed into the array concurrently by leaf
 404      * tasks at the correct offsets.  If the exact size is not known, output
 405      * elements are collected into a conc-node whose shape mirrors that
 406      * of the computation. This conc-node can then be flattened in
 407      * parallel to produce a flat {@code Node} if desired.
 408      *
 409      * @param helper the pipeline helper describing the pipeline
 410      * @param flattenTree whether a conc node should be flattened into a node
 411      *                    describing an array before returning
 412      * @param generator the array generator
 413      * @return a {@link Node} describing the output elements
 414      */
 415     public static <P_IN, P_OUT> Node<P_OUT> collect(PipelineHelper<P_OUT> helper,
 416                                                     Spliterator<P_IN> spliterator,
 417                                                     boolean flattenTree,
 418                                                     IntFunction<P_OUT[]> generator) {
 419         long size = helper.exactOutputSizeIfKnown(spliterator);
 420         if (size >= 0 && spliterator.hasCharacteristics(Spliterator.SUBSIZED)) {
 421             if (size >= MAX_ARRAY_SIZE)
 422                 throw new IllegalArgumentException("Stream size exceeds max array size");
 423             P_OUT[] array = generator.apply((int) size);
 424             new SizedCollectorTask.OfRef<>(spliterator, helper, array).invoke();
 425             return node(array);
 426         } else {
 427             Node<P_OUT> node = new CollectorTask<>(helper, generator, spliterator).invoke();
 428             return flattenTree ? flatten(node, generator) : node;
 429         }
 430     }
 431 
 432     /**
 433      * Collect, in parallel, elements output from an int-valued pipeline and
 434      * describe those elements with a {@link Node.OfInt}.
 435      *
 436      * @implSpec
 437      * If the exact size of the output from the pipeline is known and the source
 438      * {@link Spliterator} has the {@link Spliterator#SUBSIZED} characteristic,
 439      * then a flat {@link Node} will be returned whose content is an array,
 440      * since the size is known the array can be constructed in advance and
 441      * output elements can be placed into the array concurrently by leaf
 442      * tasks at the correct offsets.  If the exact size is not known, output
 443      * elements are collected into a conc-node whose shape mirrors that
 444      * of the computation. This conc-node can then be flattened in
 445      * parallel to produce a flat {@code Node.OfInt} if desired.
 446      *
 447      * @param helper the pipeline helper describing the pipeline
 448      * @param flattenTree whether a conc node should be flattened into a node
 449      *                    describing an array before returning
 450      * @return a {@link Node.OfInt} describing the output elements
 451      */
 452     public static <P_IN> Node.OfInt collectInt(PipelineHelper<Integer> helper,
 453                                                Spliterator<P_IN> spliterator,
 454                                                boolean flattenTree) {
 455         long size = helper.exactOutputSizeIfKnown(spliterator);
 456         if (size >= 0 && spliterator.hasCharacteristics(Spliterator.SUBSIZED)) {
 457             if (size >= MAX_ARRAY_SIZE)
 458                 throw new IllegalArgumentException("Stream size exceeds max array size");
 459             int[] array = new int[(int) size];
 460             new SizedCollectorTask.OfInt<>(spliterator, helper, array).invoke();
 461             return node(array);
 462         }
 463         else {
 464             Node.OfInt node = new IntCollectorTask<>(helper, spliterator).invoke();
 465             return flattenTree ? flattenInt(node) : node;
 466         }
 467     }
 468 
 469     /**
 470      * Collect, in parallel, elements output from a long-valued pipeline and
 471      * describe those elements with a {@link Node.OfLong}.
 472      *
 473      * @implSpec
 474      * If the exact size of the output from the pipeline is known and the source
 475      * {@link Spliterator} has the {@link Spliterator#SUBSIZED} characteristic,
 476      * then a flat {@link Node} will be returned whose content is an array,
 477      * since the size is known the array can be constructed in advance and
 478      * output elements can be placed into the array concurrently by leaf
 479      * tasks at the correct offsets.  If the exact size is not known, output
 480      * elements are collected into a conc-node whose shape mirrors that
 481      * of the computation. This conc-node can then be flattened in
 482      * parallel to produce a flat {@code Node.OfLong} if desired.
 483      *
 484      * @param helper the pipeline helper describing the pipeline
 485      * @param flattenTree whether a conc node should be flattened into a node
 486      *                    describing an array before returning
 487      * @return a {@link Node.OfLong} describing the output elements
 488      */
 489     public static <P_IN> Node.OfLong collectLong(PipelineHelper<Long> helper,
 490                                                  Spliterator<P_IN> spliterator,
 491                                                  boolean flattenTree) {
 492         long size = helper.exactOutputSizeIfKnown(spliterator);
 493         if (size >= 0 && spliterator.hasCharacteristics(Spliterator.SUBSIZED)) {
 494             if (size >= MAX_ARRAY_SIZE)
 495                 throw new IllegalArgumentException("Stream size exceeds max array size");
 496             long[] array = new long[(int) size];
 497             new SizedCollectorTask.OfLong<>(spliterator, helper, array).invoke();
 498             return node(array);
 499         }
 500         else {
 501             Node.OfLong node = new LongCollectorTask<>(helper, spliterator).invoke();
 502             return flattenTree ? flattenLong(node) : node;
 503         }
 504     }
 505 
 506     /**
 507      * Collect, in parallel, elements output from n double-valued pipeline and
 508      * describe those elements with a {@link Node.OfDouble}.
 509      *
 510      * @implSpec
 511      * If the exact size of the output from the pipeline is known and the source
 512      * {@link Spliterator} has the {@link Spliterator#SUBSIZED} characteristic,
 513      * then a flat {@link Node} will be returned whose content is an array,
 514      * since the size is known the array can be constructed in advance and
 515      * output elements can be placed into the array concurrently by leaf
 516      * tasks at the correct offsets.  If the exact size is not known, output
 517      * elements are collected into a conc-node whose shape mirrors that
 518      * of the computation. This conc-node can then be flattened in
 519      * parallel to produce a flat {@code Node.OfDouble} if desired.
 520      *
 521      * @param helper the pipeline helper describing the pipeline
 522      * @param flattenTree whether a conc node should be flattened into a node
 523      *                    describing an array before returning
 524      * @return a {@link Node.OfDouble} describing the output elements
 525      */
 526     public static <P_IN> Node.OfDouble collectDouble(PipelineHelper<Double> helper,
 527                                                      Spliterator<P_IN> spliterator,
 528                                                      boolean flattenTree) {
 529         long size = helper.exactOutputSizeIfKnown(spliterator);
 530         if (size >= 0 && spliterator.hasCharacteristics(Spliterator.SUBSIZED)) {
 531             if (size >= MAX_ARRAY_SIZE)
 532                 throw new IllegalArgumentException("Stream size exceeds max array size");
 533             double[] array = new double[(int) size];
 534             new SizedCollectorTask.OfDouble<>(spliterator, helper, array).invoke();
 535             return node(array);
 536         }
 537         else {
 538             Node.OfDouble node = new DoubleCollectorTask<>(helper, spliterator).invoke();
 539             return flattenTree ? flattenDouble(node) : node;
 540         }
 541     }
 542 
 543     // Parallel flattening of nodes
 544 
 545     /**
 546      * Flatten, in parallel, a {@link Node}.  A flattened node is one that has
 547      * no children.  If the node is already flat, it is simply returned.
 548      *
 549      * @implSpec
 550      * If a new node is to be created, the generator is used to create an array
 551      * whose length is {@link Node#count()}.  Then the node tree is traversed
 552      * and leaf node elements are placed in the array concurrently by leaf tasks
 553      * at the correct offsets.
 554      *
 555      * @param node the node to flatten
 556      * @param generator the array factory used to create array instances
 557      * @param <T> type of elements contained by the node
 558      * @return a flat {@code Node}
 559      */
 560     public static <T> Node<T> flatten(Node<T> node, IntFunction<T[]> generator) {
 561         if (node.getChildCount() > 0) {
 562             T[] array = generator.apply((int) node.count());
 563             new ToArrayTask.OfRef<>(node, array, 0).invoke();
 564             return node(array);
 565         } else {
 566             return node;
 567         }
 568     }
 569 
 570     /**
 571      * Flatten, in parallel, a {@link Node.OfInt}.  A flattened node is one that
 572      * has no children.  If the node is already flat, it is simply returned.
 573      *
 574      * @implSpec
 575      * If a new node is to be created, a new int[] array is created whose length
 576      * is {@link Node#count()}.  Then the node tree is traversed and leaf node
 577      * elements are placed in the array concurrently by leaf tasks at the
 578      * correct offsets.
 579      *
 580      * @param node the node to flatten
 581      * @return a flat {@code Node.OfInt}
 582      */
 583     public static Node.OfInt flattenInt(Node.OfInt node) {
 584         if (node.getChildCount() > 0) {
 585             int[] array = new int[(int) node.count()];
 586             new ToArrayTask.OfInt(node, array, 0).invoke();
 587             return node(array);
 588         } else {
 589             return node;
 590         }
 591     }
 592 
 593     /**
 594      * Flatten, in parallel, a {@link Node.OfLong}.  A flattened node is one that
 595      * has no children.  If the node is already flat, it is simply returned.
 596      *
 597      * @implSpec
 598      * If a new node is to be created, a new long[] array is created whose length
 599      * is {@link Node#count()}.  Then the node tree is traversed and leaf node
 600      * elements are placed in the array concurrently by leaf tasks at the
 601      * correct offsets.
 602      *
 603      * @param node the node to flatten
 604      * @return a flat {@code Node.OfLong}
 605      */
 606     public static Node.OfLong flattenLong(Node.OfLong node) {
 607         if (node.getChildCount() > 0) {
 608             long[] array = new long[(int) node.count()];
 609             new ToArrayTask.OfLong(node, array, 0).invoke();
 610             return node(array);
 611         } else {
 612             return node;
 613         }
 614     }
 615 
 616     /**
 617      * Flatten, in parallel, a {@link Node.OfDouble}.  A flattened node is one that
 618      * has no children.  If the node is already flat, it is simply returned.
 619      *
 620      * @implSpec
 621      * If a new node is to be created, a new double[] array is created whose length
 622      * is {@link Node#count()}.  Then the node tree is traversed and leaf node
 623      * elements are placed in the array concurrently by leaf tasks at the
 624      * correct offsets.
 625      *
 626      * @param node the node to flatten
 627      * @return a flat {@code Node.OfDouble}
 628      */
 629     public static Node.OfDouble flattenDouble(Node.OfDouble node) {
 630         if (node.getChildCount() > 0) {
 631             double[] array = new double[(int) node.count()];
 632             new ToArrayTask.OfDouble(node, array, 0).invoke();
 633             return node(array);
 634         } else {
 635             return node;
 636         }
 637     }
 638 
 639     // Implementations
 640 
 641     private static abstract class EmptyNode<T, T_ARR, T_CONS> implements Node<T> {
 642         EmptyNode() { }
 643 
 644         @Override
 645         public T[] asArray(IntFunction<T[]> generator) {
 646             return generator.apply(0);
 647         }
 648 
 649         public void copyInto(T_ARR array, int offset) { }
 650 
 651         @Override
 652         public long count() {
 653             return 0;
 654         }
 655 
 656         public void forEach(T_CONS consumer) { }
 657 
 658         private static class OfRef<T> extends EmptyNode<T, T[], Consumer<? super T>> {
 659             private OfRef() {
 660                 super();
 661             }
 662 
 663             @Override
 664             public Spliterator<T> spliterator() {
 665                 return Spliterators.emptySpliterator();
 666             }
 667         }
 668 
 669         private static final class OfInt
 670                 extends EmptyNode<Integer, int[], IntConsumer>
 671                 implements Node.OfInt {
 672 
 673             OfInt() { } // Avoid creation of special accessor
 674 
 675             @Override
 676             public Spliterator.OfInt spliterator() {
 677                 return Spliterators.emptyIntSpliterator();
 678             }
 679 
 680             @Override
 681             public int[] asIntArray() {
 682                 return EMPTY_INT_ARRAY;
 683             }
 684         }
 685 
 686         private static final class OfLong
 687                 extends EmptyNode<Long, long[], LongConsumer>
 688                 implements Node.OfLong {
 689 
 690             OfLong() { } // Avoid creation of special accessor
 691 
 692             @Override
 693             public Spliterator.OfLong spliterator() {
 694                 return Spliterators.emptyLongSpliterator();
 695             }
 696 
 697             @Override
 698             public long[] asLongArray() {
 699                 return EMPTY_LONG_ARRAY;
 700             }
 701         }
 702 
 703         private static final class OfDouble
 704                 extends EmptyNode<Double, double[], DoubleConsumer>
 705                 implements Node.OfDouble {
 706 
 707             OfDouble() { } // Avoid creation of special accessor
 708 
 709             @Override
 710             public Spliterator.OfDouble spliterator() {
 711                 return Spliterators.emptyDoubleSpliterator();
 712             }
 713 
 714             @Override
 715             public double[] asDoubleArray() {
 716                 return EMPTY_DOUBLE_ARRAY;
 717             }
 718         }
 719     }
 720 
 721     /** Node class for a reference array */
 722     private static class ArrayNode<T> implements Node<T> {
 723         final T[] array;
 724         int curSize;
 725 
 726         @SuppressWarnings("unchecked")
 727         ArrayNode(long size, IntFunction<T[]> generator) {
 728             if (size >= MAX_ARRAY_SIZE)
 729                 throw new IllegalArgumentException("Stream size exceeds max array size");
 730             this.array = generator.apply((int) size);
 731             this.curSize = 0;
 732         }
 733 
 734         ArrayNode(T[] array) {
 735             this.array = array;
 736             this.curSize = array.length;
 737         }
 738 
 739         // Node
 740 
 741         @Override
 742         public Spliterator<T> spliterator() {
 743             return Arrays.spliterator(array, 0, curSize);
 744         }
 745 
 746         @Override
 747         public void copyInto(T[] dest, int destOffset) {
 748             System.arraycopy(array, 0, dest, destOffset, curSize);
 749         }
 750 
 751         @Override
 752         public T[] asArray(IntFunction<T[]> generator) {
 753             if (array.length == curSize) {
 754                 return array;
 755             } else {
 756                 throw new IllegalStateException();
 757             }
 758         }
 759 
 760         @Override
 761         public long count() {
 762             return curSize;
 763         }
 764 
 765         // Traversable
 766 
 767         @Override
 768         public void forEach(Consumer<? super T> consumer) {
 769             for (int i = 0; i < curSize; i++) {
 770                 consumer.accept(array[i]);
 771             }
 772         }
 773 
 774         //
 775 
 776         @Override
 777         public String toString() {
 778             return String.format("ArrayNode[%d][%s]",
 779                                  array.length - curSize, Arrays.toString(array));
 780         }
 781     }
 782 
 783     /** Node class for a Collection */
 784     private static final class CollectionNode<T> implements Node<T> {
 785         private final Collection<T> c;
 786 
 787         CollectionNode(Collection<T> c) {
 788             this.c = c;
 789         }
 790 
 791         // Node
 792 
 793         @Override
 794         public Spliterator<T> spliterator() {
 795             return c.stream().spliterator();
 796         }
 797 
 798         @Override
 799         public void copyInto(T[] array, int offset) {
 800             for (T t : c)
 801                 array[offset++] = t;
 802         }
 803 
 804         @Override
 805         @SuppressWarnings("unchecked")
 806         public T[] asArray(IntFunction<T[]> generator) {
 807             return c.toArray(generator.apply(c.size()));
 808         }
 809 
 810         @Override
 811         public long count() {
 812             return c.size();
 813         }
 814 
 815         @Override
 816         public void forEach(Consumer<? super T> consumer) {
 817             c.forEach(consumer);
 818         }
 819 
 820         //
 821 
 822         @Override
 823         public String toString() {
 824             return String.format("CollectionNode[%d][%s]", c.size(), c);
 825         }
 826     }
 827 
 828     /** Node class for an internal node with two or more children */
 829     static final class ConcNode<T> implements Node<T> {
 830         private final Node<T> left;
 831         private final Node<T> right;
 832 
 833         private final long size;
 834 
 835         ConcNode(Node<T> left, Node<T> right) {
 836             this.left = left;
 837             this.right = right;
 838             // The Node count will be required when the Node spliterator is
 839             // obtained and it is cheaper to aggressively calculate bottom up
 840             // as the tree is built rather than later on from the top down
 841             // traversing the tree
 842             this.size = left.count() + right.count();
 843         }
 844 
 845         // Node
 846 
 847         @Override
 848         public Spliterator<T> spliterator() {
 849             return new Nodes.InternalNodeSpliterator.OfRef<>(this);
 850         }
 851 
 852         @Override
 853         public int getChildCount() {
 854             return 2;
 855         }
 856 
 857         @Override
 858         public Node<T> getChild(int i) {
 859             if (i == 0) return left;
 860             if (i == 1) return right;
 861             throw new IndexOutOfBoundsException();
 862         }
 863 
 864         @Override
 865         public void copyInto(T[] array, int offset) {
 866             Objects.requireNonNull(array);
 867             left.copyInto(array, offset);
 868             right.copyInto(array, offset + (int) left.count());
 869         }
 870 
 871         @Override
 872         public T[] asArray(IntFunction<T[]> generator) {
 873             T[] array = generator.apply((int) count());
 874             copyInto(array, 0);
 875             return array;
 876         }
 877 
 878         @Override
 879         public long count() {
 880             return size;
 881         }
 882 
 883         @Override
 884         public void forEach(Consumer<? super T> consumer) {
 885             left.forEach(consumer);
 886             right.forEach(consumer);
 887         }
 888 
 889         @Override
 890         public String toString() {
 891             if (count() < 32) {
 892                 return String.format("ConcNode[%s]",
 893                                      Arrays.asList(left, right).stream()
 894                                            .map(Object::toString)
 895                                            .collect(toStringJoiner(","))
 896                                            .toString());
 897             } else {
 898                 return String.format("ConcNode[size=%d]", count());
 899             }
 900         }
 901     }
 902 
 903     /** Abstract class for spliterator for all internal node classes */
 904     private static abstract class InternalNodeSpliterator<T,
 905                                                           S extends Spliterator<T>,
 906                                                           N extends Node<T>, C>
 907             implements Spliterator<T> {
 908         // Node we are pointing to
 909         // null if full traversal has occurred
 910         N curNode;
 911 
 912         // next child of curNode to consume
 913         int curChildIndex;
 914 
 915         // The spliterator of the curNode if that node is last and has no children.
 916         // This spliterator will be delegated to for splitting and traversing.
 917         // null if curNode has children
 918         S lastNodeSpliterator;
 919 
 920         // spliterator used while traversing with tryAdvance
 921         // null if no partial traversal has occurred
 922         S tryAdvanceSpliterator;
 923 
 924         // node stack used when traversing to search and find leaf nodes
 925         // null if no partial traversal has occurred
 926         Deque<N> tryAdvanceStack;
 927 
 928         InternalNodeSpliterator(N curNode) {
 929             this.curNode = curNode;
 930         }
 931 
 932         /**
 933          * Initiate a stack containing, in left-to-right order, the child nodes
 934          * covered by this spliterator
 935          */
 936         protected final Deque<N> initStack() {
 937             // Bias size to the case where leaf nodes are close to this node
 938             // 8 is the minimum initial capacity for the ArrayDeque implementation
 939             Deque<N> stack = new ArrayDeque<>(8);
 940             for (int i = curNode.getChildCount() - 1; i >= curChildIndex; i--)
 941                 stack.addFirst((N) curNode.getChild(i));
 942             return stack;
 943         }
 944 
 945         /**
 946          * Depth first search, in left-to-right order, of the node tree, using
 947          * an explicit stack, to find the next non-empty leaf node.
 948          */
 949         protected final N findNextLeafNode(Deque<N> stack) {
 950             N n = null;
 951             while ((n = stack.pollFirst()) != null) {
 952                 if (n.getChildCount() == 0) {
 953                     if (n.count() > 0)
 954                         return n;
 955                 } else {
 956                     for (int i = n.getChildCount() - 1; i >= 0; i--)
 957                         stack.addFirst((N) n.getChild(i));
 958                 }
 959             }
 960 
 961             return null;
 962         }
 963 
 964         protected final boolean internalTryAdvance(C consumer) {
 965             if (curNode == null)
 966                 return false;
 967 
 968             if (tryAdvanceSpliterator == null) {
 969                 if (lastNodeSpliterator == null) {
 970                     // Initiate the node stack
 971                     tryAdvanceStack = initStack();
 972                     N leaf = findNextLeafNode(tryAdvanceStack);
 973                     if (leaf != null)
 974                         tryAdvanceSpliterator = (S) leaf.spliterator();
 975                     else {
 976                         // A non-empty leaf node was not found
 977                         // No elements to traverse
 978                         curNode = null;
 979                         return false;
 980                     }
 981                 }
 982                 else
 983                     tryAdvanceSpliterator = lastNodeSpliterator;
 984             }
 985 
 986             boolean hasNext = tryAdvance(tryAdvanceSpliterator, consumer);
 987             if (!hasNext) {
 988                 if (lastNodeSpliterator == null) {
 989                     // Advance to the spliterator of the next non-empty leaf node
 990                     Node<T> leaf = findNextLeafNode(tryAdvanceStack);
 991                     if (leaf != null) {
 992                         tryAdvanceSpliterator = (S) leaf.spliterator();
 993                         // Since the node is not-empty the spliterator can be advanced
 994                         return tryAdvance(tryAdvanceSpliterator, consumer);
 995                     }
 996                 }
 997                 // No more elements to traverse
 998                 curNode = null;
 999             }
1000             return hasNext;
1001         }
1002 
1003         protected abstract boolean tryAdvance(S spliterator, C consumer);
1004 
1005         @Override
1006         @SuppressWarnings("unchecked")
1007         public S trySplit() {
1008             if (curNode == null || tryAdvanceSpliterator != null)
1009                 return null; // Cannot split if fully or partially traversed
1010             else if (lastNodeSpliterator != null)
1011                 return (S) lastNodeSpliterator.trySplit();
1012             else if (curChildIndex < curNode.getChildCount() - 1)
1013                 return (S) curNode.getChild(curChildIndex++).spliterator();
1014             else {
1015                 curNode = (N) curNode.getChild(curChildIndex);
1016                 if (curNode.getChildCount() == 0) {
1017                     lastNodeSpliterator = (S) curNode.spliterator();
1018                     return (S) lastNodeSpliterator.trySplit();
1019                 }
1020                 else {
1021                     curChildIndex = 0;
1022                     return (S) curNode.getChild(curChildIndex++).spliterator();
1023                 }
1024             }
1025         }
1026 
1027         @Override
1028         public long estimateSize() {
1029             if (curNode == null)
1030                 return 0;
1031 
1032             // Will not reflect the effects of partial traversal.
1033             // This is compliant with the specification
1034             if (lastNodeSpliterator != null)
1035                 return lastNodeSpliterator.estimateSize();
1036             else {
1037                 long size = 0;
1038                 for (int i = curChildIndex; i < curNode.getChildCount(); i++)
1039                     size += curNode.getChild(i).count();
1040                 return size;
1041             }
1042         }
1043 
1044         @Override
1045         public int characteristics() {
1046             return Spliterator.SIZED;
1047         }
1048 
1049         private static final class OfRef<T>
1050                 extends InternalNodeSpliterator<T, Spliterator<T>, Node<T>, Consumer<? super T>> {
1051 
1052             OfRef(Node<T> curNode) {
1053                 super(curNode);
1054             }
1055 
1056             @Override
1057             public boolean tryAdvance(Consumer<? super T> consumer) {
1058                 return internalTryAdvance(consumer);
1059             }
1060 
1061             @Override
1062             protected boolean tryAdvance(Spliterator<T> spliterator,
1063                                          Consumer<? super T> consumer) {
1064                 return spliterator.tryAdvance(consumer);
1065             }
1066 
1067             @Override
1068             public void forEachRemaining(Consumer<? super T> consumer) {
1069                 if (curNode == null)
1070                     return;
1071 
1072                 if (tryAdvanceSpliterator == null) {
1073                     if (lastNodeSpliterator == null) {
1074                         Deque<Node<T>> stack = initStack();
1075                         Node<T> leaf;
1076                         while ((leaf = findNextLeafNode(stack)) != null) {
1077                             leaf.forEach(consumer);
1078                         }
1079                         curNode = null;
1080                     }
1081                     else
1082                         lastNodeSpliterator.forEachRemaining(consumer);
1083                 }
1084                 else
1085                     while(tryAdvance(consumer)) { }
1086             }
1087         }
1088 
1089         private static final class OfInt
1090                 extends InternalNodeSpliterator<Integer, Spliterator.OfInt, Node.OfInt, IntConsumer>
1091                 implements Spliterator.OfInt {
1092 
1093             OfInt(Node.OfInt cur) {
1094                 super(cur);
1095             }
1096 
1097             @Override
1098             public boolean tryAdvance(IntConsumer consumer) {
1099                 return internalTryAdvance(consumer);
1100             }
1101 
1102             @Override
1103             protected boolean tryAdvance(Spliterator.OfInt spliterator,
1104                                          IntConsumer consumer) {
1105                 return spliterator.tryAdvance(consumer);
1106             }
1107 
1108             @Override
1109             public void forEachRemaining(IntConsumer consumer) {
1110                 if (curNode == null)
1111                     return;
1112 
1113                 if (tryAdvanceSpliterator == null) {
1114                     if (lastNodeSpliterator == null) {
1115                         Deque<Node.OfInt> stack = initStack();
1116                         Node.OfInt leaf;
1117                         while ((leaf = findNextLeafNode(stack)) != null) {
1118                             leaf.forEach(consumer);
1119                         }
1120                         curNode = null;
1121                     }
1122                     else
1123                         lastNodeSpliterator.forEachRemaining(consumer);
1124                 }
1125                 else
1126                     while(tryAdvance(consumer)) { }
1127             }
1128         }
1129 
1130         private static final class OfLong
1131                 extends InternalNodeSpliterator<Long, Spliterator.OfLong, Node.OfLong, LongConsumer>
1132                 implements Spliterator.OfLong {
1133 
1134             OfLong(Node.OfLong cur) {
1135                 super(cur);
1136             }
1137 
1138             @Override
1139             public boolean tryAdvance(LongConsumer consumer) {
1140                 return internalTryAdvance(consumer);
1141             }
1142 
1143             @Override
1144             protected boolean tryAdvance(Spliterator.OfLong spliterator,
1145                                          LongConsumer consumer) {
1146                 return spliterator.tryAdvance(consumer);
1147             }
1148 
1149             @Override
1150             public void forEachRemaining(LongConsumer consumer) {
1151                 if (curNode == null)
1152                     return;
1153 
1154                 if (tryAdvanceSpliterator == null) {
1155                     if (lastNodeSpliterator == null) {
1156                         Deque<Node.OfLong> stack = initStack();
1157                         Node.OfLong leaf;
1158                         while ((leaf = findNextLeafNode(stack)) != null) {
1159                             leaf.forEach(consumer);
1160                         }
1161                         curNode = null;
1162                     }
1163                     else
1164                         lastNodeSpliterator.forEachRemaining(consumer);
1165                 }
1166                 else
1167                     while(tryAdvance(consumer)) { }
1168             }
1169         }
1170 
1171         private static final class OfDouble
1172                 extends InternalNodeSpliterator<Double, Spliterator.OfDouble, Node.OfDouble, DoubleConsumer>
1173                 implements Spliterator.OfDouble {
1174 
1175             OfDouble(Node.OfDouble cur) {
1176                 super(cur);
1177             }
1178 
1179             @Override
1180             public boolean tryAdvance(DoubleConsumer consumer) {
1181                 return internalTryAdvance(consumer);
1182             }
1183 
1184             @Override
1185             protected boolean tryAdvance(Spliterator.OfDouble spliterator,
1186                                          DoubleConsumer consumer) {
1187                 return spliterator.tryAdvance(consumer);
1188             }
1189 
1190             @Override
1191             public void forEachRemaining(DoubleConsumer consumer) {
1192                 if (curNode == null)
1193                     return;
1194 
1195                 if (tryAdvanceSpliterator == null) {
1196                     if (lastNodeSpliterator == null) {
1197                         Deque<Node.OfDouble> stack = initStack();
1198                         Node.OfDouble leaf;
1199                         while ((leaf = findNextLeafNode(stack)) != null) {
1200                             leaf.forEach(consumer);
1201                         }
1202                         curNode = null;
1203                     }
1204                     else
1205                         lastNodeSpliterator.forEachRemaining(consumer);
1206                 }
1207                 else
1208                     while(tryAdvance(consumer)) { }
1209             }
1210         }
1211     }
1212 
1213     /** Fixed-sized builder class for reference nodes */
1214     private static final class FixedNodeBuilder<T>
1215             extends ArrayNode<T>
1216             implements Node.Builder<T> {
1217 
1218         FixedNodeBuilder(long size, IntFunction<T[]> generator) {
1219             super(size, generator);
1220             assert size < MAX_ARRAY_SIZE;
1221         }
1222 
1223         @Override
1224         public Node<T> build() {
1225             if (curSize < array.length)
1226                 throw new IllegalStateException(String.format("Current size %d is less than fixed size %d",
1227                                                               curSize, array.length));
1228             return this;
1229         }
1230 
1231         @Override
1232         public void begin(long size) {
1233             if (size != array.length)
1234                 throw new IllegalStateException(String.format("Begin size %d is not equal to fixed size %d",
1235                                                               size, array.length));
1236             curSize = 0;
1237         }
1238 
1239         @Override
1240         public void accept(T t) {
1241             if (curSize < array.length) {
1242                 array[curSize++] = t;
1243             } else {
1244                 throw new IllegalStateException(String.format("Accept exceeded fixed size of %d",
1245                                                               array.length));
1246             }
1247         }
1248 
1249         @Override
1250         public void end() {
1251             if (curSize < array.length)
1252                 throw new IllegalStateException(String.format("End size %d is less than fixed size %d",
1253                                                               curSize, array.length));
1254         }
1255 
1256         @Override
1257         public String toString() {
1258             return String.format("FixedNodeBuilder[%d][%s]",
1259                                  array.length - curSize, Arrays.toString(array));
1260         }
1261     }
1262 
1263     /** Variable-sized builder class for reference nodes */
1264     private static final class SpinedNodeBuilder<T>
1265             extends SpinedBuffer<T>
1266             implements Node<T>, Node.Builder<T> {
1267         private boolean building = false;
1268 
1269         SpinedNodeBuilder() {} // Avoid creation of special accessor
1270 
1271         @Override
1272         public Spliterator<T> spliterator() {
1273             assert !building : "during building";
1274             return super.spliterator();
1275         }
1276 
1277         @Override
1278         public void forEach(Consumer<? super T> consumer) {
1279             assert !building : "during building";
1280             super.forEach(consumer);
1281         }
1282 
1283         //
1284         @Override
1285         public void begin(long size) {
1286             assert !building : "was already building";
1287             building = true;
1288             clear();
1289             ensureCapacity(size);
1290         }
1291 
1292         @Override
1293         public void accept(T t) {
1294             assert building : "not building";
1295             super.accept(t);
1296         }
1297 
1298         @Override
1299         public void end() {
1300             assert building : "was not building";
1301             building = false;
1302             // @@@ check begin(size) and size
1303         }
1304 
1305         @Override
1306         public void copyInto(T[] array, int offset) {
1307             assert !building : "during building";
1308             super.copyInto(array, offset);
1309         }
1310 
1311         @Override
1312         public T[] asArray(IntFunction<T[]> arrayFactory) {
1313             assert !building : "during building";
1314             return super.asArray(arrayFactory);
1315         }
1316 
1317         @Override
1318         public Node<T> build() {
1319             assert !building : "during building";
1320             return this;
1321         }
1322     }
1323 
1324     //
1325 
1326     private static final int[] EMPTY_INT_ARRAY = new int[0];
1327     private static final long[] EMPTY_LONG_ARRAY = new long[0];
1328     private static final double[] EMPTY_DOUBLE_ARRAY = new double[0];
1329 
1330     private abstract static class AbstractPrimitiveConcNode<E, N extends Node<E>>
1331             implements Node<E> {
1332         final N left;
1333         final N right;
1334         final long size;
1335 
1336         AbstractPrimitiveConcNode(N left, N right) {
1337             this.left = left;
1338             this.right = right;
1339             // The Node count will be required when the Node spliterator is
1340             // obtained and it is cheaper to aggressively calculate bottom up as
1341             // the tree is built rather than later on by traversing the tree
1342             this.size = left.count() + right.count();
1343         }
1344 
1345         @Override
1346         public int getChildCount() {
1347             return 2;
1348         }
1349 
1350         @Override
1351         public N getChild(int i) {
1352             if (i == 0) return left;
1353             if (i == 1) return right;
1354             throw new IndexOutOfBoundsException();
1355         }
1356 
1357         @Override
1358         public long count() {
1359             return size;
1360         }
1361 
1362         @Override
1363         public String toString() {
1364             if (count() < 32)
1365                 return String.format("%s[%s]", this.getClass().getName(),
1366                                      Arrays.asList(left, right).stream()
1367                                            .map(Object::toString)
1368                                            .collect(toStringJoiner(","))
1369                                            .toString());
1370             else
1371                 return String.format("%s[size=%d]", this.getClass().getName(), count());
1372         }
1373     }
1374 
1375     private static class IntArrayNode implements Node.OfInt {
1376         final int[] array;
1377         int curSize;
1378 
1379         IntArrayNode(long size) {
1380             if (size >= MAX_ARRAY_SIZE)
1381                 throw new IllegalArgumentException("Stream size exceeds max array size");
1382             this.array = new int[(int) size];
1383             this.curSize = 0;
1384         }
1385 
1386         IntArrayNode(int[] array) {
1387             this.array = array;
1388             this.curSize = array.length;
1389         }
1390 
1391         // Node
1392 
1393         @Override
1394         public Spliterator.OfInt spliterator() {
1395             return Arrays.spliterator(array, 0, curSize);
1396         }
1397 
1398         @Override
1399         public int[] asIntArray() {
1400             if (array.length == curSize) {
1401                 return array;
1402             } else {
1403                 return Arrays.copyOf(array, curSize);
1404             }
1405         }
1406 
1407         @Override
1408         public void copyInto(int[] dest, int destOffset) {
1409             System.arraycopy(array, 0, dest, destOffset, curSize);
1410         }
1411 
1412         @Override
1413         public long count() {
1414             return curSize;
1415         }
1416 
1417         @Override
1418         public void forEach(IntConsumer consumer) {
1419             for (int i = 0; i < curSize; i++) {
1420                 consumer.accept(array[i]);
1421             }
1422         }
1423 
1424         @Override
1425         public String toString() {
1426             return String.format("IntArrayNode[%d][%s]",
1427                                  array.length - curSize, Arrays.toString(array));
1428         }
1429     }
1430 
1431     private static class LongArrayNode implements Node.OfLong {
1432         final long[] array;
1433         int curSize;
1434 
1435         LongArrayNode(long size) {
1436             if (size >= MAX_ARRAY_SIZE)
1437                 throw new IllegalArgumentException("Stream size exceeds max array size");
1438             this.array = new long[(int) size];
1439             this.curSize = 0;
1440         }
1441 
1442         LongArrayNode(long[] array) {
1443             this.array = array;
1444             this.curSize = array.length;
1445         }
1446 
1447         @Override
1448         public Spliterator.OfLong spliterator() {
1449             return Arrays.spliterator(array, 0, curSize);
1450         }
1451 
1452         @Override
1453         public long[] asLongArray() {
1454             if (array.length == curSize) {
1455                 return array;
1456             } else {
1457                 return Arrays.copyOf(array, curSize);
1458             }
1459         }
1460 
1461         @Override
1462         public void copyInto(long[] dest, int destOffset) {
1463             System.arraycopy(array, 0, dest, destOffset, curSize);
1464         }
1465 
1466         @Override
1467         public long count() {
1468             return curSize;
1469         }
1470 
1471         @Override
1472         public void forEach(LongConsumer consumer) {
1473             for (int i = 0; i < curSize; i++) {
1474                 consumer.accept(array[i]);
1475             }
1476         }
1477 
1478         @Override
1479         public String toString() {
1480             return String.format("LongArrayNode[%d][%s]",
1481                                  array.length - curSize, Arrays.toString(array));
1482         }
1483     }
1484 
1485     private static class DoubleArrayNode implements Node.OfDouble {
1486         final double[] array;
1487         int curSize;
1488 
1489         DoubleArrayNode(long size) {
1490             if (size >= MAX_ARRAY_SIZE)
1491                 throw new IllegalArgumentException("Stream size exceeds max array size");
1492             this.array = new double[(int) size];
1493             this.curSize = 0;
1494         }
1495 
1496         DoubleArrayNode(double[] array) {
1497             this.array = array;
1498             this.curSize = array.length;
1499         }
1500 
1501         @Override
1502         public Spliterator.OfDouble spliterator() {
1503             return Arrays.spliterator(array, 0, curSize);
1504         }
1505 
1506         @Override
1507         public double[] asDoubleArray() {
1508             if (array.length == curSize) {
1509                 return array;
1510             } else {
1511                 return Arrays.copyOf(array, curSize);
1512             }
1513         }
1514 
1515         @Override
1516         public void copyInto(double[] dest, int destOffset) {
1517             System.arraycopy(array, 0, dest, destOffset, curSize);
1518         }
1519 
1520         @Override
1521         public long count() {
1522             return curSize;
1523         }
1524 
1525         @Override
1526         public void forEach(DoubleConsumer consumer) {
1527             for (int i = 0; i < curSize; i++) {
1528                 consumer.accept(array[i]);
1529             }
1530         }
1531 
1532         @Override
1533         public String toString() {
1534             return String.format("DoubleArrayNode[%d][%s]",
1535                                  array.length - curSize, Arrays.toString(array));
1536         }
1537     }
1538 
1539     static final class IntConcNode
1540             extends AbstractPrimitiveConcNode<Integer, Node.OfInt>
1541             implements Node.OfInt {
1542 
1543         IntConcNode(Node.OfInt left, Node.OfInt right) {
1544             super(left, right);
1545         }
1546 
1547         @Override
1548         public void forEach(IntConsumer consumer) {
1549             left.forEach(consumer);
1550             right.forEach(consumer);
1551         }
1552 
1553         @Override
1554         public Spliterator.OfInt spliterator() {
1555             return new InternalNodeSpliterator.OfInt(this);
1556         }
1557 
1558         @Override
1559         public void copyInto(int[] array, int offset) {
1560             left.copyInto(array, offset);
1561             right.copyInto(array, offset + (int) left.count());
1562         }
1563 
1564         @Override
1565         public int[] asIntArray() {
1566             int[] array = new int[(int) count()];
1567             copyInto(array, 0);
1568             return array;
1569         }
1570     }
1571 
1572     static final class LongConcNode
1573             extends AbstractPrimitiveConcNode<Long, Node.OfLong>
1574             implements Node.OfLong {
1575 
1576         LongConcNode(Node.OfLong left, Node.OfLong right) {
1577             super(left, right);
1578         }
1579 
1580         @Override
1581         public void forEach(LongConsumer consumer) {
1582             left.forEach(consumer);
1583             right.forEach(consumer);
1584         }
1585 
1586         @Override
1587         public Spliterator.OfLong spliterator() {
1588             return new InternalNodeSpliterator.OfLong(this);
1589         }
1590 
1591         @Override
1592         public void copyInto(long[] array, int offset) {
1593             left.copyInto(array, offset);
1594             right.copyInto(array, offset + (int) left.count());
1595         }
1596 
1597         @Override
1598         public long[] asLongArray() {
1599             long[] array = new long[(int) count()];
1600             copyInto(array, 0);
1601             return array;
1602         }
1603     }
1604 
1605     static final class DoubleConcNode
1606             extends AbstractPrimitiveConcNode<Double, Node.OfDouble>
1607             implements Node.OfDouble {
1608 
1609         DoubleConcNode(Node.OfDouble left, Node.OfDouble right) {
1610             super(left, right);
1611         }
1612 
1613         @Override
1614         public void forEach(DoubleConsumer consumer) {
1615             left.forEach(consumer);
1616             right.forEach(consumer);
1617         }
1618 
1619         @Override
1620         public Spliterator.OfDouble spliterator() {
1621             return new InternalNodeSpliterator.OfDouble(this);
1622         }
1623 
1624         @Override
1625         public void copyInto(double[] array, int offset) {
1626             left.copyInto(array, offset);
1627             right.copyInto(array, offset + (int) left.count());
1628         }
1629 
1630         @Override
1631         public double[] asDoubleArray() {
1632             double[] array = new double[(int) count()];
1633             copyInto(array, 0);
1634             return array;
1635         }
1636     }
1637 
1638     private static final class IntFixedNodeBuilder
1639             extends IntArrayNode
1640             implements Node.Builder.OfInt {
1641 
1642         IntFixedNodeBuilder(long size) {
1643             super(size);
1644             assert size < MAX_ARRAY_SIZE;
1645         }
1646 
1647         @Override
1648         public Node.OfInt build() {
1649             if (curSize < array.length) {
1650                 throw new IllegalStateException(String.format("Current size %d is less than fixed size %d",
1651                                                               curSize, array.length));
1652             }
1653 
1654             return this;
1655         }
1656 
1657         @Override
1658         public void begin(long size) {
1659             if (size != array.length) {
1660                 throw new IllegalStateException(String.format("Begin size %d is not equal to fixed size %d",
1661                                                               size, array.length));
1662             }
1663 
1664             curSize = 0;
1665         }
1666 
1667         @Override
1668         public void accept(int i) {
1669             if (curSize < array.length) {
1670                 array[curSize++] = i;
1671             } else {
1672                 throw new IllegalStateException(String.format("Accept exceeded fixed size of %d",
1673                                                               array.length));
1674             }
1675         }
1676 
1677         @Override
1678         public void end() {
1679             if (curSize < array.length) {
1680                 throw new IllegalStateException(String.format("End size %d is less than fixed size %d",
1681                                                               curSize, array.length));
1682             }
1683         }
1684 
1685         @Override
1686         public String toString() {
1687             return String.format("IntFixedNodeBuilder[%d][%s]",
1688                                  array.length - curSize, Arrays.toString(array));
1689         }
1690     }
1691 
1692     private static final class LongFixedNodeBuilder
1693             extends LongArrayNode
1694             implements Node.Builder.OfLong {
1695 
1696         LongFixedNodeBuilder(long size) {
1697             super(size);
1698             assert size < MAX_ARRAY_SIZE;
1699         }
1700 
1701         @Override
1702         public Node.OfLong build() {
1703             if (curSize < array.length) {
1704                 throw new IllegalStateException(String.format("Current size %d is less than fixed size %d",
1705                                                               curSize, array.length));
1706             }
1707 
1708             return this;
1709         }
1710 
1711         @Override
1712         public void begin(long size) {
1713             if (size != array.length) {
1714                 throw new IllegalStateException(String.format("Begin size %d is not equal to fixed size %d",
1715                                                               size, array.length));
1716             }
1717 
1718             curSize = 0;
1719         }
1720 
1721         @Override
1722         public void accept(long i) {
1723             if (curSize < array.length) {
1724                 array[curSize++] = i;
1725             } else {
1726                 throw new IllegalStateException(String.format("Accept exceeded fixed size of %d",
1727                                                               array.length));
1728             }
1729         }
1730 
1731         @Override
1732         public void end() {
1733             if (curSize < array.length) {
1734                 throw new IllegalStateException(String.format("End size %d is less than fixed size %d",
1735                                                               curSize, array.length));
1736             }
1737         }
1738 
1739         @Override
1740         public String toString() {
1741             return String.format("LongFixedNodeBuilder[%d][%s]",
1742                                  array.length - curSize, Arrays.toString(array));
1743         }
1744     }
1745 
1746     private static final class DoubleFixedNodeBuilder
1747             extends DoubleArrayNode
1748             implements Node.Builder.OfDouble {
1749 
1750         DoubleFixedNodeBuilder(long size) {
1751             super(size);
1752             assert size < MAX_ARRAY_SIZE;
1753         }
1754 
1755         @Override
1756         public Node.OfDouble build() {
1757             if (curSize < array.length) {
1758                 throw new IllegalStateException(String.format("Current size %d is less than fixed size %d",
1759                                                               curSize, array.length));
1760             }
1761 
1762             return this;
1763         }
1764 
1765         @Override
1766         public void begin(long size) {
1767             if (size != array.length) {
1768                 throw new IllegalStateException(String.format("Begin size %d is not equal to fixed size %d",
1769                                                               size, array.length));
1770             }
1771 
1772             curSize = 0;
1773         }
1774 
1775         @Override
1776         public void accept(double i) {
1777             if (curSize < array.length) {
1778                 array[curSize++] = i;
1779             } else {
1780                 throw new IllegalStateException(String.format("Accept exceeded fixed size of %d",
1781                                                               array.length));
1782             }
1783         }
1784 
1785         @Override
1786         public void end() {
1787             if (curSize < array.length) {
1788                 throw new IllegalStateException(String.format("End size %d is less than fixed size %d",
1789                                                               curSize, array.length));
1790             }
1791         }
1792 
1793         @Override
1794         public String toString() {
1795             return String.format("DoubleFixedNodeBuilder[%d][%s]",
1796                                  array.length - curSize, Arrays.toString(array));
1797         }
1798     }
1799 
1800     private static final class IntSpinedNodeBuilder
1801             extends SpinedBuffer.OfInt
1802             implements Node.OfInt, Node.Builder.OfInt {
1803         private boolean building = false;
1804 
1805         IntSpinedNodeBuilder() {} // Avoid creation of special accessor
1806 
1807         @Override
1808         public Spliterator.OfInt spliterator() {
1809             assert !building : "during building";
1810             return super.spliterator();
1811         }
1812 
1813         @Override
1814         public void forEach(IntConsumer consumer) {
1815             assert !building : "during building";
1816             super.forEach(consumer);
1817         }
1818 
1819         //
1820         @Override
1821         public void begin(long size) {
1822             assert !building : "was already building";
1823             building = true;
1824             clear();
1825             ensureCapacity(size);
1826         }
1827 
1828         @Override
1829         public void accept(int i) {
1830             assert building : "not building";
1831             super.accept(i);
1832         }
1833 
1834         @Override
1835         public void end() {
1836             assert building : "was not building";
1837             building = false;
1838             // @@@ check begin(size) and size
1839         }
1840 
1841         @Override
1842         public void copyInto(int[] array, int offset) throws IndexOutOfBoundsException {
1843             assert !building : "during building";
1844             super.copyInto(array, offset);
1845         }
1846 
1847         @Override
1848         public int[] asIntArray() {
1849             assert !building : "during building";
1850             return super.asIntArray();
1851         }
1852 
1853         @Override
1854         public Node.OfInt build() {
1855             assert !building : "during building";
1856             return this;
1857         }
1858     }
1859 
1860     private static final class LongSpinedNodeBuilder
1861             extends SpinedBuffer.OfLong
1862             implements Node.OfLong, Node.Builder.OfLong {
1863         private boolean building = false;
1864 
1865         LongSpinedNodeBuilder() {} // Avoid creation of special accessor
1866 
1867         @Override
1868         public Spliterator.OfLong spliterator() {
1869             assert !building : "during building";
1870             return super.spliterator();
1871         }
1872 
1873         @Override
1874         public void forEach(LongConsumer consumer) {
1875             assert !building : "during building";
1876             super.forEach(consumer);
1877         }
1878 
1879         //
1880         @Override
1881         public void begin(long size) {
1882             assert !building : "was already building";
1883             building = true;
1884             clear();
1885             ensureCapacity(size);
1886         }
1887 
1888         @Override
1889         public void accept(long i) {
1890             assert building : "not building";
1891             super.accept(i);
1892         }
1893 
1894         @Override
1895         public void end() {
1896             assert building : "was not building";
1897             building = false;
1898             // @@@ check begin(size) and size
1899         }
1900 
1901         @Override
1902         public void copyInto(long[] array, int offset) {
1903             assert !building : "during building";
1904             super.copyInto(array, offset);
1905         }
1906 
1907         @Override
1908         public long[] asLongArray() {
1909             assert !building : "during building";
1910             return super.asLongArray();
1911         }
1912 
1913         @Override
1914         public Node.OfLong build() {
1915             assert !building : "during building";
1916             return this;
1917         }
1918     }
1919 
1920     private static final class DoubleSpinedNodeBuilder
1921             extends SpinedBuffer.OfDouble
1922             implements Node.OfDouble, Node.Builder.OfDouble {
1923         private boolean building = false;
1924 
1925         DoubleSpinedNodeBuilder() {} // Avoid creation of special accessor
1926 
1927         @Override
1928         public Spliterator.OfDouble spliterator() {
1929             assert !building : "during building";
1930             return super.spliterator();
1931         }
1932 
1933         @Override
1934         public void forEach(DoubleConsumer consumer) {
1935             assert !building : "during building";
1936             super.forEach(consumer);
1937         }
1938 
1939         //
1940         @Override
1941         public void begin(long size) {
1942             assert !building : "was already building";
1943             building = true;
1944             clear();
1945             ensureCapacity(size);
1946         }
1947 
1948         @Override
1949         public void accept(double i) {
1950             assert building : "not building";
1951             super.accept(i);
1952         }
1953 
1954         @Override
1955         public void end() {
1956             assert building : "was not building";
1957             building = false;
1958             // @@@ check begin(size) and size
1959         }
1960 
1961         @Override
1962         public void copyInto(double[] array, int offset) {
1963             assert !building : "during building";
1964             super.copyInto(array, offset);
1965         }
1966 
1967         @Override
1968         public double[] asDoubleArray() {
1969             assert !building : "during building";
1970             return super.asDoubleArray();
1971         }
1972 
1973         @Override
1974         public Node.OfDouble build() {
1975             assert !building : "during building";
1976             return this;
1977         }
1978     }
1979 
1980     private static abstract class SizedCollectorTask<P_IN, P_OUT, T_SINK extends Sink<P_OUT>,
1981                                                      K extends SizedCollectorTask<P_IN, P_OUT, T_SINK, K>>
1982             extends CountedCompleter<Void>
1983             implements Sink<P_OUT> {
1984         protected final Spliterator<P_IN> spliterator;
1985         protected final PipelineHelper<P_OUT> helper;
1986         protected final long targetSize;
1987         protected long offset;
1988         protected long length;
1989         // For Sink implementation
1990         protected int index, fence;
1991 
1992         SizedCollectorTask(Spliterator<P_IN> spliterator,
1993                            PipelineHelper<P_OUT> helper,
1994                            int arrayLength) {
1995             assert spliterator.hasCharacteristics(Spliterator.SUBSIZED);
1996             this.spliterator = spliterator;
1997             this.helper = helper;
1998             this.targetSize = AbstractTask.suggestTargetSize(spliterator.estimateSize());
1999             this.offset = 0;
2000             this.length = arrayLength;
2001         }
2002 
2003         SizedCollectorTask(K parent, Spliterator<P_IN> spliterator,
2004                            long offset, long length, int arrayLength) {
2005             super(parent);
2006             assert spliterator.hasCharacteristics(Spliterator.SUBSIZED);
2007             this.spliterator = spliterator;
2008             this.helper = parent.helper;
2009             this.targetSize = parent.targetSize;
2010             this.offset = offset;
2011             this.length = length;
2012 
2013             if (offset < 0 || length < 0 || (offset + length - 1 >= arrayLength)) {
2014                 throw new IllegalArgumentException(
2015                         String.format("offset and length interval [%d, %d + %d) is not within array size interval [0, %d)",
2016                                       offset, offset, length, arrayLength));
2017             }
2018         }
2019 
2020         @Override
2021         public void compute() {
2022             SizedCollectorTask<P_IN, P_OUT, T_SINK, K> task = this;
2023             while (true) {
2024                 Spliterator<P_IN> leftSplit;
2025                 if (!AbstractTask.suggestSplit(task.spliterator, task.targetSize)
2026                     || ((leftSplit = task.spliterator.trySplit()) == null)) {
2027                     if (task.offset + task.length >= MAX_ARRAY_SIZE)
2028                         throw new IllegalArgumentException("Stream size exceeds max array size");
2029                     T_SINK sink = (T_SINK) task;
2030                     task.helper.wrapAndCopyInto(sink, task.spliterator);
2031                     task.propagateCompletion();
2032                     return;
2033                 }
2034                 else {
2035                     task.setPendingCount(1);
2036                     long leftSplitSize = leftSplit.estimateSize();
2037                     task.makeChild(leftSplit, task.offset, leftSplitSize).fork();
2038                     task = task.makeChild(task.spliterator, task.offset + leftSplitSize,
2039                                           task.length - leftSplitSize);
2040                 }
2041             }
2042         }
2043 
2044         abstract K makeChild(Spliterator<P_IN> spliterator, long offset, long size);
2045 
2046         @Override
2047         public void begin(long size) {
2048             if(size > length)
2049                 throw new IllegalStateException("size passed to Sink.begin exceeds array length");
2050             index = (int) offset;
2051             fence = (int) offset + (int) length;
2052         }
2053 
2054         static final class OfRef<P_IN, P_OUT>
2055                 extends SizedCollectorTask<P_IN, P_OUT, Sink<P_OUT>, OfRef<P_IN, P_OUT>>
2056                 implements Sink<P_OUT> {
2057             private final P_OUT[] array;
2058 
2059             OfRef(Spliterator<P_IN> spliterator, PipelineHelper<P_OUT> helper, P_OUT[] array) {
2060                 super(spliterator, helper, array.length);
2061                 this.array = array;
2062             }
2063 
2064             OfRef(OfRef<P_IN, P_OUT> parent, Spliterator<P_IN> spliterator,
2065                   long offset, long length) {
2066                 super(parent, spliterator, offset, length, parent.array.length);
2067                 this.array = parent.array;
2068             }
2069 
2070             @Override
2071             OfRef<P_IN, P_OUT> makeChild(Spliterator<P_IN> spliterator,
2072                                          long offset, long size) {
2073                 return new OfRef<>(this, spliterator, offset, size);
2074             }
2075 
2076             @Override
2077             public void accept(P_OUT value) {
2078                 if (index >= fence) {
2079                     throw new IndexOutOfBoundsException(Integer.toString(index));
2080                 }
2081                 array[index++] = value;
2082             }
2083         }
2084 
2085         static final class OfInt<P_IN>
2086                 extends SizedCollectorTask<P_IN, Integer, Sink.OfInt, OfInt<P_IN>>
2087                 implements Sink.OfInt {
2088             private final int[] array;
2089 
2090             OfInt(Spliterator<P_IN> spliterator, PipelineHelper<Integer> helper, int[] array) {
2091                 super(spliterator, helper, array.length);
2092                 this.array = array;
2093             }
2094 
2095             OfInt(SizedCollectorTask.OfInt<P_IN> parent, Spliterator<P_IN> spliterator,
2096                   long offset, long length) {
2097                 super(parent, spliterator, offset, length, parent.array.length);
2098                 this.array = parent.array;
2099             }
2100 
2101             @Override
2102             SizedCollectorTask.OfInt<P_IN> makeChild(Spliterator<P_IN> spliterator,
2103                                                      long offset, long size) {
2104                 return new SizedCollectorTask.OfInt<>(this, spliterator, offset, size);
2105             }
2106 
2107             @Override
2108             public void accept(int value) {
2109                 if (index >= fence) {
2110                     throw new IndexOutOfBoundsException(Integer.toString(index));
2111                 }
2112                 array[index++] = value;
2113             }
2114         }
2115 
2116         static final class OfLong<P_IN>
2117                 extends SizedCollectorTask<P_IN, Long, Sink.OfLong, OfLong<P_IN>>
2118                 implements Sink.OfLong {
2119             private final long[] array;
2120 
2121             OfLong(Spliterator<P_IN> spliterator, PipelineHelper<Long> helper, long[] array) {
2122                 super(spliterator, helper, array.length);
2123                 this.array = array;
2124             }
2125 
2126             OfLong(SizedCollectorTask.OfLong<P_IN> parent, Spliterator<P_IN> spliterator,
2127                    long offset, long length) {
2128                 super(parent, spliterator, offset, length, parent.array.length);
2129                 this.array = parent.array;
2130             }
2131 
2132             @Override
2133             SizedCollectorTask.OfLong<P_IN> makeChild(Spliterator<P_IN> spliterator,
2134                                                       long offset, long size) {
2135                 return new SizedCollectorTask.OfLong<>(this, spliterator, offset, size);
2136             }
2137 
2138             @Override
2139             public void accept(long value) {
2140                 if (index >= fence) {
2141                     throw new IndexOutOfBoundsException(Integer.toString(index));
2142                 }
2143                 array[index++] = value;
2144             }
2145         }
2146 
2147         static final class OfDouble<P_IN>
2148                 extends SizedCollectorTask<P_IN, Double, Sink.OfDouble, OfDouble<P_IN>>
2149                 implements Sink.OfDouble {
2150             private final double[] array;
2151 
2152             OfDouble(Spliterator<P_IN> spliterator, PipelineHelper<Double> helper, double[] array) {
2153                 super(spliterator, helper, array.length);
2154                 this.array = array;
2155             }
2156 
2157             OfDouble(SizedCollectorTask.OfDouble<P_IN> parent, Spliterator<P_IN> spliterator,
2158                      long offset, long length) {
2159                 super(parent, spliterator, offset, length, parent.array.length);
2160                 this.array = parent.array;
2161             }
2162 
2163             @Override
2164             SizedCollectorTask.OfDouble<P_IN> makeChild(Spliterator<P_IN> spliterator,
2165                                                         long offset, long size) {
2166                 return new SizedCollectorTask.OfDouble<>(this, spliterator, offset, size);
2167             }
2168 
2169             @Override
2170             public void accept(double value) {
2171                 if (index >= fence) {
2172                     throw new IndexOutOfBoundsException(Integer.toString(index));
2173                 }
2174                 array[index++] = value;
2175             }
2176         }
2177     }
2178 
2179     private static abstract class ToArrayTask<T, T_NODE extends Node<T>,
2180                                               K extends ToArrayTask<T, T_NODE, K>>
2181             extends CountedCompleter<Void> {
2182         protected final T_NODE node;
2183         protected final int offset;
2184 
2185         ToArrayTask(T_NODE node, int offset) {
2186             this.node = node;
2187             this.offset = offset;
2188         }
2189 
2190         ToArrayTask(K parent, T_NODE node, int offset) {
2191             super(parent);
2192             this.node = node;
2193             this.offset = offset;
2194         }
2195 
2196         abstract void copyNodeToArray();
2197 
2198         abstract K makeChild(int childIndex, int offset);
2199 
2200         @Override
2201         public void compute() {
2202             ToArrayTask<T, T_NODE, K> task = this;
2203             while (true) {
2204                 if (task.node.getChildCount() == 0) {
2205                     task.copyNodeToArray();
2206                     task.propagateCompletion();
2207                     return;
2208                 }
2209                 else {
2210                     task.setPendingCount(task.node.getChildCount() - 1);
2211 
2212                     int size = 0;
2213                     int i = 0;
2214                     for (;i < task.node.getChildCount() - 1; i++) {
2215                         K leftTask = task.makeChild(i, task.offset + size);
2216                         size += leftTask.node.count();
2217                         leftTask.fork();
2218                     }
2219                     task = task.makeChild(i, task.offset + size);
2220                 }
2221             }
2222         }
2223 
2224         private static final class OfRef<T>
2225                 extends ToArrayTask<T, Node<T>, OfRef<T>> {
2226             private final T[] array;
2227 
2228             private OfRef(Node<T> node, T[] array, int offset) {
2229                 super(node, offset);
2230                 this.array = array;
2231             }
2232 
2233             private OfRef(OfRef<T> parent, Node<T> node, int offset) {
2234                 super(parent, node, offset);
2235                 this.array = parent.array;
2236             }
2237 
2238             @Override
2239             OfRef<T> makeChild(int childIndex, int offset) {
2240                 return new OfRef<>(this, node.getChild(childIndex), offset);
2241             }
2242 
2243             @Override
2244             void copyNodeToArray() {
2245                 node.copyInto(array, offset);
2246             }
2247         }
2248 
2249         private static final class OfInt
2250                 extends ToArrayTask<Integer, Node.OfInt, OfInt> {
2251             private final int[] array;
2252 
2253             private OfInt(Node.OfInt node, int[] array, int offset) {
2254                 super(node, offset);
2255                 this.array = array;
2256             }
2257 
2258             private OfInt(OfInt parent, Node.OfInt node, int offset) {
2259                 super(parent, node, offset);
2260                 this.array = parent.array;
2261             }
2262 
2263             @Override
2264             OfInt makeChild(int childIndex, int offset) {
2265                 return new OfInt(this, node.getChild(childIndex), offset);
2266             }
2267 
2268             @Override
2269             void copyNodeToArray() {
2270                 node.copyInto(array, offset);
2271             }
2272         }
2273 
2274         private static final class OfLong
2275                 extends ToArrayTask<Long, Node.OfLong, OfLong> {
2276             private final long[] array;
2277 
2278             private OfLong(Node.OfLong node, long[] array, int offset) {
2279                 super(node, offset);
2280                 this.array = array;
2281             }
2282 
2283             private OfLong(OfLong parent, Node.OfLong node, int offset) {
2284                 super(parent, node, offset);
2285                 this.array = parent.array;
2286             }
2287 
2288             @Override
2289             OfLong makeChild(int childIndex, int offset) {
2290                 return new OfLong(this, node.getChild(childIndex), offset);
2291             }
2292 
2293             @Override
2294             void copyNodeToArray() {
2295                 node.copyInto(array, offset);
2296             }
2297         }
2298 
2299         private static final class OfDouble
2300                 extends ToArrayTask<Double, Node.OfDouble, OfDouble> {
2301             private final double[] array;
2302 
2303             private OfDouble(Node.OfDouble node, double[] array, int offset) {
2304                 super(node, offset);
2305                 this.array = array;
2306             }
2307 
2308             private OfDouble(OfDouble parent, Node.OfDouble node, int offset) {
2309                 super(parent, node, offset);
2310                 this.array = parent.array;
2311             }
2312 
2313             @Override
2314             OfDouble makeChild(int childIndex, int offset) {
2315                 return new OfDouble(this, node.getChild(childIndex), offset);
2316             }
2317 
2318             @Override
2319             void copyNodeToArray() {
2320                 node.copyInto(array, offset);
2321             }
2322         }
2323     }
2324 
2325     private static final class CollectorTask<P_IN, P_OUT>
2326             extends AbstractTask<P_IN, P_OUT, Node<P_OUT>, CollectorTask<P_IN, P_OUT>> {
2327         private final PipelineHelper<P_OUT> helper;
2328         private final IntFunction<P_OUT[]> generator;
2329 
2330         CollectorTask(PipelineHelper<P_OUT> helper,
2331                       IntFunction<P_OUT[]> generator,
2332                       Spliterator<P_IN> spliterator) {
2333             super(helper, spliterator);
2334             this.helper = helper;
2335             this.generator = generator;
2336         }
2337 
2338         CollectorTask(CollectorTask<P_IN, P_OUT> parent, Spliterator<P_IN> spliterator) {
2339             super(parent, spliterator);
2340             helper = parent.helper;
2341             generator = parent.generator;
2342         }
2343 
2344         @Override
2345         protected CollectorTask<P_IN, P_OUT> makeChild(Spliterator<P_IN> spliterator) {
2346             return new CollectorTask<>(this, spliterator);
2347         }
2348 
2349         @Override
2350         protected Node<P_OUT> doLeaf() {
2351             Node.Builder<P_OUT> builder
2352                     = builder(helper.exactOutputSizeIfKnown(spliterator),
2353                                     generator);
2354             return helper.wrapAndCopyInto(builder, spliterator).build();
2355         }
2356 
2357         @Override
2358         public void onCompletion(CountedCompleter caller) {
2359             if (!isLeaf()) {
2360                 setLocalResult(new ConcNode<>(leftChild.getLocalResult(), rightChild.getLocalResult()));
2361             }
2362             super.onCompletion(caller);
2363         }
2364     }
2365 
2366     private static final class IntCollectorTask<P_IN>
2367             extends AbstractTask<P_IN, Integer, Node.OfInt, IntCollectorTask<P_IN>> {
2368         private final PipelineHelper<Integer> helper;
2369 
2370         IntCollectorTask(PipelineHelper<Integer> helper, Spliterator<P_IN> spliterator) {
2371             super(helper, spliterator);
2372             this.helper = helper;
2373         }
2374 
2375         IntCollectorTask(IntCollectorTask<P_IN> parent, Spliterator<P_IN> spliterator) {
2376             super(parent, spliterator);
2377             helper = parent.helper;
2378         }
2379 
2380         @Override
2381         protected IntCollectorTask<P_IN> makeChild(Spliterator<P_IN> spliterator) {
2382             return new IntCollectorTask<>(this, spliterator);
2383         }
2384 
2385         @Override
2386         protected Node.OfInt doLeaf() {
2387             Node.Builder.OfInt builder = intBuilder(helper.exactOutputSizeIfKnown(spliterator));
2388             return helper.wrapAndCopyInto(builder, spliterator).build();
2389         }
2390 
2391         @Override
2392         public void onCompletion(CountedCompleter caller) {
2393             if (!isLeaf()) {
2394                 setLocalResult(new IntConcNode(leftChild.getLocalResult(), rightChild.getLocalResult()));
2395             }
2396             super.onCompletion(caller);
2397         }
2398     }
2399 
2400     private static final class LongCollectorTask<P_IN>
2401             extends AbstractTask<P_IN, Long, Node.OfLong, LongCollectorTask<P_IN>> {
2402         private final PipelineHelper<Long> helper;
2403 
2404         LongCollectorTask(PipelineHelper<Long> helper, Spliterator<P_IN> spliterator) {
2405             super(helper, spliterator);
2406             this.helper = helper;
2407         }
2408 
2409         LongCollectorTask(LongCollectorTask<P_IN> parent, Spliterator<P_IN> spliterator) {
2410             super(parent, spliterator);
2411             helper = parent.helper;
2412         }
2413 
2414         @Override
2415         protected LongCollectorTask<P_IN> makeChild(Spliterator<P_IN> spliterator) {
2416             return new LongCollectorTask<>(this, spliterator);
2417         }
2418 
2419         @Override
2420         protected Node.OfLong doLeaf() {
2421             Node.Builder.OfLong builder = longBuilder(helper.exactOutputSizeIfKnown(spliterator));
2422             return helper.wrapAndCopyInto(builder, spliterator).build();
2423         }
2424 
2425         @Override
2426         public void onCompletion(CountedCompleter caller) {
2427             if (!isLeaf()) {
2428                 setLocalResult(new LongConcNode(leftChild.getLocalResult(), rightChild.getLocalResult()));
2429             }
2430             super.onCompletion(caller);
2431         }
2432     }
2433 
2434     private static final class DoubleCollectorTask<P_IN>
2435             extends AbstractTask<P_IN, Double, Node.OfDouble, DoubleCollectorTask<P_IN>> {
2436         private final PipelineHelper<Double> helper;
2437 
2438         DoubleCollectorTask(PipelineHelper<Double> helper, Spliterator<P_IN> spliterator) {
2439             super(helper, spliterator);
2440             this.helper = helper;
2441         }
2442 
2443         DoubleCollectorTask(DoubleCollectorTask<P_IN> parent, Spliterator<P_IN> spliterator) {
2444             super(parent, spliterator);
2445             helper = parent.helper;
2446         }
2447 
2448         @Override
2449         protected DoubleCollectorTask<P_IN> makeChild(Spliterator<P_IN> spliterator) {
2450             return new DoubleCollectorTask<>(this, spliterator);
2451         }
2452 
2453         @Override
2454         protected Node.OfDouble doLeaf() {
2455             Node.Builder.OfDouble builder
2456                     = doubleBuilder(helper.exactOutputSizeIfKnown(spliterator));
2457             return helper.wrapAndCopyInto(builder, spliterator).build();
2458         }
2459 
2460         @Override
2461         public void onCompletion(CountedCompleter caller) {
2462             if (!isLeaf()) {
2463                 setLocalResult(new DoubleConcNode(leftChild.getLocalResult(), rightChild.getLocalResult()));
2464             }
2465             super.onCompletion(caller);
2466         }
2467     }
2468 }