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