changeset 14075:6ac303e36f22

Incremental progress on streams port; more test cases
author briangoetz
date Thu, 30 Jun 2016 16:14:28 -0400
parents 7ad88bd23c4e
children 62c3b03ef4ed
files src/java.base/share/classes/java/anyutil/OptionalInt.java src/java.base/share/classes/java/anyutil/PrimitiveIterator.java src/java.base/share/classes/java/anyutil/Spliterator.java src/java.base/share/classes/java/anyutil/stream/DoubleStream.java src/java.base/share/classes/java/anyutil/stream/ForEachOps.java src/java.base/share/classes/java/anyutil/stream/IntStream.java src/java.base/share/classes/java/anyutil/stream/LongStream.java src/java.base/share/classes/java/anyutil/stream/Pipeline.java src/java.base/share/classes/java/anyutil/stream/Sink.java src/java.base/share/classes/java/anyutil/stream/SliceOps.java src/java.base/share/classes/java/anyutil/stream/Stream.java src/java.base/share/classes/java/anyutil/stream/StreamSupport.java src/java.base/share/classes/java/anyutil/stream/Streams.java test/valhalla/test/valhalla/anyutil/SimplePipelineTest.java test/valhalla/test/valhalla/model3/EncoderTest.java
diffstat 15 files changed, 643 insertions(+), 286 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/anyutil/OptionalInt.java	Thu Jun 30 12:05:03 2016 +0100
+++ b/src/java.base/share/classes/java/anyutil/OptionalInt.java	Thu Jun 30 16:14:28 2016 -0400
@@ -89,6 +89,10 @@
         return opt.isPresent() ? new OptionalInt(opt.get()) : empty();
     }
 
+    public static Optional<int> adapt(OptionalInt opt) {
+        return opt.isPresent() ? Optional.<int>of(opt.getAsInt()) : Optional.<int>empty();
+    }
+
     /**
      * Construct an instance with the value present.
      *
--- a/src/java.base/share/classes/java/anyutil/PrimitiveIterator.java	Thu Jun 30 12:05:03 2016 +0100
+++ b/src/java.base/share/classes/java/anyutil/PrimitiveIterator.java	Thu Jun 30 16:14:28 2016 -0400
@@ -147,6 +147,43 @@
             forEachRemaining((IntConsumer) action::accept);
         }
 
+        static PrimitiveIterator.OfInt adapt(Iterator<int> iterator) {
+            return new PrimitiveIterator.OfInt() {
+                @Override
+                public boolean hasNext() {
+                    return iterator.hasNext();
+                }
+
+                @Override
+                public int nextInt() {
+                    return iterator.next();
+                }
+
+                @Override
+                public void remove() {
+                    iterator.remove();
+                }
+            };
+        }
+
+        static Iterator<int> adapt(PrimitiveIterator.OfInt iterator) {
+            return new Iterator<int>() {
+                @Override
+                public boolean hasNext() {
+                    return iterator.hasNext();
+                }
+
+                @Override
+                public int next() {
+                    return iterator.next();
+                }
+
+                @Override
+                public void remove() {
+                    iterator.remove();
+                }
+            };
+        }
     }
 
     /**
--- a/src/java.base/share/classes/java/anyutil/Spliterator.java	Thu Jun 30 12:05:03 2016 +0100
+++ b/src/java.base/share/classes/java/anyutil/Spliterator.java	Thu Jun 30 16:14:28 2016 -0400
@@ -690,12 +690,12 @@
             forEachRemaining((IntConsumer) action::accept);
         }
 
-        public static Spliterator.OfInt adapt(Spliterator<int> spliterator) {
+        public static Spliterator.OfInt fromSpliterator(Spliterator<int> spliterator) {
             return new Spliterator.OfInt() {
                 @Override
                 public OfInt trySplit() {
                     Spliterator<int> split = spliterator.trySplit();
-                    return split == null ? null : adapt(spliterator);
+                    return split == null ? null : Spliterator.OfInt.fromSpliterator(spliterator);
                 }
 
                 @Override
@@ -744,6 +744,31 @@
                 }
             };
         }
+
+        public default Spliterator<int> asSpliterator() {
+            return new Spliterator<int>() {
+                @Override
+                public boolean tryAdvance(Consumer<int> action) {
+                    return Spliterator.OfInt.this.tryAdvance((IntConsumer) (int i) -> action.accept(i));
+                }
+
+                @Override
+                public Spliterator<int> trySplit() {
+                    OfInt split = Spliterator.OfInt.this.trySplit();
+                    return (split == null) ? null : split.asSpliterator();
+                }
+
+                @Override
+                public long estimateSize() {
+                    return Spliterator.OfInt.this.estimateSize();
+                }
+
+                @Override
+                public int characteristics() {
+                    return Spliterator.OfInt.this.characteristics();
+                }
+            };
+        }
     }
 
     /**
@@ -799,12 +824,12 @@
             forEachRemaining((LongConsumer) action::accept);
         }
 
-        public static Spliterator.OfLong adapt(Spliterator<long> spliterator) {
+        public static Spliterator.OfLong fromSpliterator(Spliterator<long> spliterator) {
             return new Spliterator.OfLong() {
                 @Override
                 public OfLong trySplit() {
                     Spliterator<long> split = spliterator.trySplit();
-                    return split == null ? null : adapt(spliterator);
+                    return split == null ? null : fromSpliterator(spliterator);
                 }
 
                 @Override
@@ -853,6 +878,32 @@
                 }
             };
         }
+
+        public default Spliterator<long> asSpliterator() {
+            return new Spliterator<long>() {
+                @Override
+                public boolean tryAdvance(Consumer<long> action) {
+                    return Spliterator.OfLong.this.tryAdvance((LongConsumer) (long i) -> action.accept(i));
+                }
+
+                @Override
+                public Spliterator<long> trySplit() {
+                    OfLong split = Spliterator.OfLong.this.trySplit();
+                    return (split == null) ? null : split.asSpliterator();
+                }
+
+                @Override
+                public long estimateSize() {
+                    return Spliterator.OfLong.this.estimateSize();
+                }
+
+                @Override
+                public int characteristics() {
+                    return Spliterator.OfLong.this.characteristics();
+                }
+            };
+        }
+
     }
 
     /**
@@ -909,12 +960,12 @@
             forEachRemaining((DoubleConsumer) action::accept);
         }
 
-        public static Spliterator.OfDouble adapt(Spliterator<double> spliterator) {
+        public static Spliterator.OfDouble fromSpliterator(Spliterator<double> spliterator) {
             return new Spliterator.OfDouble() {
                 @Override
                 public OfDouble trySplit() {
                     Spliterator<double> split = spliterator.trySplit();
-                    return split == null ? null : adapt(spliterator);
+                    return split == null ? null : fromSpliterator(spliterator);
                 }
 
                 @Override
@@ -963,5 +1014,30 @@
                 }
             };
         }
+
+        public default Spliterator<double> asSpliterator() {
+            return new Spliterator<double>() {
+                @Override
+                public boolean tryAdvance(Consumer<double> action) {
+                    return Spliterator.OfDouble.this.tryAdvance((DoubleConsumer) (double i) -> action.accept(i));
+                }
+
+                @Override
+                public Spliterator<double> trySplit() {
+                    OfDouble split = Spliterator.OfDouble.this.trySplit();
+                    return (split == null) ? null : split.asSpliterator();
+                }
+
+                @Override
+                public long estimateSize() {
+                    return Spliterator.OfDouble.this.estimateSize();
+                }
+
+                @Override
+                public int characteristics() {
+                    return Spliterator.OfDouble.this.characteristics();
+                }
+            };
+        }
     }
 }
--- a/src/java.base/share/classes/java/anyutil/stream/DoubleStream.java	Thu Jun 30 12:05:03 2016 +0100
+++ b/src/java.base/share/classes/java/anyutil/stream/DoubleStream.java	Thu Jun 30 16:14:28 2016 -0400
@@ -24,20 +24,13 @@
  */
 package java.anyutil.stream;
 
-import java.anyutil.OptionalInt;
-import java.nio.charset.Charset;
-import java.nio.file.Files;
-import java.nio.file.Path;
 import java.anyutil.Arrays;
-import java.anyutil.Collection;
 import java.util.DoubleSummaryStatistics;
-import java.util.IntSummaryStatistics;
 import java.util.Objects;
 import java.anyutil.OptionalDouble;
 import java.anyutil.PrimitiveIterator;
 import java.anyutil.Spliterator;
 import java.anyutil.Spliterators;
-import java.util.concurrent.ConcurrentHashMap;
 import java.anyutil.function.*;
 
 /**
@@ -830,11 +823,10 @@
         Objects.requireNonNull(a);
         Objects.requireNonNull(b);
 
-//        Spliterator.OfDouble split = new Streams.ConcatSpliterator.OfDouble(
-//                a.spliterator(), b.spliterator());
-//        DoubleStream stream = StreamSupport.doubleStream(split, a.isParallel() || b.isParallel());
-//        return stream.onClose(Streams.composedClose(a, b));
-        throw new UnsupportedOperationException();
+        Spliterator<double> split = new Streams.ConcatSpliterator<>(a.spliterator().asSpliterator(),
+                                                                    b.spliterator().asSpliterator());
+        return DoubleStream.adapt(StreamSupport.stream(split, a.isParallel() || b.isParallel())
+                                               .onClose(Streams.composedClose(a, b)));
     }
 
     /**
--- a/src/java.base/share/classes/java/anyutil/stream/ForEachOps.java	Thu Jun 30 12:05:03 2016 +0100
+++ b/src/java.base/share/classes/java/anyutil/stream/ForEachOps.java	Thu Jun 30 16:14:28 2016 -0400
@@ -172,7 +172,7 @@
             while (!isShortCircuit || !taskSink.cancellationRequested()) {
                 if (sizeEstimate <= sizeThreshold ||
                     (leftSplit = rightSplit.trySplit()) == null) {
-                    task.helper.copyInto(taskSink, rightSplit);
+                    helper.copyInto(taskSink, rightSplit);
                     break;
                 }
                 ForEachTask<E_SRC, T> leftTask = new ForEachTask<>(task, leftSplit);
--- a/src/java.base/share/classes/java/anyutil/stream/IntStream.java	Thu Jun 30 12:05:03 2016 +0100
+++ b/src/java.base/share/classes/java/anyutil/stream/IntStream.java	Thu Jun 30 16:14:28 2016 -0400
@@ -25,6 +25,16 @@
 package java.anyutil.stream;
 
 import java.anyutil.Arrays;
+import java.anyutil.Comparator;
+import java.anyutil.Iterator;
+import java.anyutil.Optional;
+import java.anyutil.function.BiFunction;
+import java.anyutil.function.BinaryOperator;
+import java.anyutil.function.Consumer;
+import java.anyutil.function.Predicate;
+import java.anyutil.function.ToDoubleFunction;
+import java.anyutil.function.ToIntFunction;
+import java.anyutil.function.ToLongFunction;
 import java.util.IntSummaryStatistics;
 import java.util.Objects;
 import java.anyutil.OptionalDouble;
@@ -816,11 +826,10 @@
         Objects.requireNonNull(a);
         Objects.requireNonNull(b);
 
-//        Spliterator.OfInt split = new Streams.ConcatSpliterator.OfInt(
-//                a.spliterator(), b.spliterator());
-//        IntStream stream = StreamSupport.intStream(split, a.isParallel() || b.isParallel());
-//        return stream.onClose(Streams.composedClose(a, b));
-        throw new UnsupportedOperationException();
+        Spliterator<int> split = new Streams.ConcatSpliterator<>(a.spliterator().asSpliterator(),
+                                                                 b.spliterator().asSpliterator());
+        return IntStream.adapt(StreamSupport.stream(split, a.isParallel() || b.isParallel())
+                                            .onClose(Streams.composedClose(a, b)));
     }
 
     /**
@@ -910,8 +919,7 @@
 
             @Override
             public IntStream flatMap(IntFunction<? extends IntStream> mapper) {
-                // @@@ Need conversion from IntStream to Stream<int>
-                return null;
+                return adapt(stream.<int>flatMap(i -> adapt(mapper.apply(i))));
             }
 
             @Override
@@ -1064,13 +1072,12 @@
 
             @Override
             public PrimitiveIterator.OfInt iterator() {
-                // @@@
-                return null;
+                return PrimitiveIterator.OfInt.adapt(stream.iterator());
             }
 
             @Override
             public Spliterator.OfInt spliterator() {
-                return Spliterator.OfInt.adapt(stream.spliterator());
+                return Spliterator.OfInt.fromSpliterator(stream.spliterator());
             }
 
             @Override
@@ -1094,4 +1101,215 @@
             }
         };
     }
-}
+
+    public static Stream<int> adapt(IntStream stream) {
+        return new Stream<int>() {
+            @Override
+            public Iterator<int> iterator() {
+                return PrimitiveIterator.OfInt.adapt(stream.iterator());
+            }
+
+            @Override
+            public Spliterator<int> spliterator() {
+                return stream.spliterator().asSpliterator();
+            }
+
+            @Override
+            public boolean isParallel() {
+                return stream.isParallel();
+            }
+
+            @Override
+            public Stream<int> sequential() {
+                return adapt(stream.sequential());
+            }
+
+            @Override
+            public Stream<int> parallel() {
+                return adapt(stream.parallel());
+            }
+
+            @Override
+            public Stream<int> unordered() {
+                return adapt(stream.unordered());
+            }
+
+            @Override
+            public Stream<int> onClose(Runnable closeHandler) {
+                return adapt(stream.onClose(closeHandler));
+            }
+
+            @Override
+            public void close() {
+                stream.close();
+            }
+
+            @Override
+            public Stream<int> filter(Predicate<int> predicate) {
+                return adapt(stream.filter(predicate::test));
+            }
+
+            @Override
+            public <any R> Stream<R> map(Function<int, ? extends R> mapper) {
+                // @@@ IntStream.map doesn't do what we expect, and mapToObj is the wrong name...
+                throw new UnsupportedOperationException("@@@");
+            }
+
+            @Override
+            public IntStream mapToInt(ToIntFunction<int> mapper) {
+                return stream.map(i -> mapper.apply(i));
+            }
+
+            @Override
+            public LongStream mapToLong(ToLongFunction<int> mapper) {
+                return stream.mapToLong(i -> mapper.apply(i));
+            }
+
+            @Override
+            public DoubleStream mapToDouble(ToDoubleFunction<int> mapper) {
+                return stream.mapToDouble(i -> mapper.apply(i));
+            }
+
+            @Override
+            public <any R> Stream<R> flatMap(Function<int, ? extends Stream<? extends R>> mapper) {
+                return null;
+            }
+
+            @Override
+            public IntStream flatMapToInt(Function<int, ? extends IntStream> mapper) {
+                return null;
+            }
+
+            @Override
+            public LongStream flatMapToLong(Function<int, ? extends LongStream> mapper) {
+                return null;
+            }
+
+            @Override
+            public DoubleStream flatMapToDouble(Function<int, ? extends DoubleStream> mapper) {
+                return null;
+            }
+
+            @Override
+            public Stream<int> distinct() {
+                return adapt(stream.distinct());
+            }
+
+            @Override
+            public Stream<int> sorted() {
+                return adapt(stream.sorted());
+            }
+
+            @Override
+            public Stream<int> sorted(Comparator<int> comparator) {
+                // @@@ IntStream does not have right underlying method
+                throw new UnsupportedOperationException("@@@");
+            }
+
+            @Override
+            public Stream<int> peek(Consumer<int> action) {
+                return adapt(stream.peek(action::accept));
+            }
+
+            @Override
+            public Stream<int> limit(long maxSize) {
+                return adapt(stream.limit(maxSize));
+            }
+
+            @Override
+            public Stream<int> skip(long n) {
+                return adapt(stream.skip(n));
+            }
+
+            @Override
+            public void forEach(Consumer<int> action) {
+                stream.forEach(action::accept);
+            }
+
+            @Override
+            public void forEachOrdered(Consumer<int> action) {
+                stream.forEachOrdered(action::accept);
+            }
+
+            @Override
+            public Object[] toArray() {
+                throw new UnsupportedOperationException("@@@");
+            }
+
+            @Override
+            public <A> A[] toArray(IntFunction<A[]> generator) {
+                return null;
+            }
+
+            @Override
+            public int reduce(int identity, BinaryOperator<int> accumulator) {
+                return stream.reduce(identity, accumulator::apply);
+            }
+
+            @Override
+            public Optional<int> reduce(BinaryOperator<int> accumulator) {
+                return OptionalInt.adapt(stream.reduce(accumulator::apply));
+            }
+
+            @Override
+            public <any U> U reduce(U identity, BiFunction<U, int, U> accumulator, BinaryOperator<U> combiner) {
+                // @@@ IntStream doesn't have this method
+                throw new UnsupportedOperationException("@@@");
+            }
+
+            @Override
+            public <R> R collect(Supplier<R> supplier, BiConsumer<R, int> accumulator, BiConsumer<R, R> combiner) {
+                // @@@ IntStream doesn't have this method
+                throw new UnsupportedOperationException("@@@");
+            }
+
+            @Override
+            public <R, A> R collect(Collector<int, A, R> collector) {
+                // @@@ IntStream doesn't have this method
+                throw new UnsupportedOperationException("@@@");
+            }
+
+            @Override
+            public Optional<int> min(Comparator<int> comparator) {
+                // @@@ IntStream doesn't have this method
+                throw new UnsupportedOperationException("@@@");
+            }
+
+            @Override
+            public Optional<int> max(Comparator<int> comparator) {
+                // @@@ IntStream doesn't have this method
+                throw new UnsupportedOperationException("@@@");
+            }
+
+            @Override
+            public long count() {
+                return stream.count();
+            }
+
+            @Override
+            public boolean anyMatch(Predicate<int> predicate) {
+                return stream.anyMatch(predicate::test);
+            }
+
+            @Override
+            public boolean allMatch(Predicate<int> predicate) {
+                return stream.allMatch(predicate::test);
+            }
+
+            @Override
+            public boolean noneMatch(Predicate<int> predicate) {
+                return stream.noneMatch(predicate::test);
+            }
+
+            @Override
+            public Optional<int> findFirst() {
+                return OptionalInt.adapt(stream.findFirst());
+            }
+
+            @Override
+            public Optional<int> findAny() {
+                return OptionalInt.adapt(stream.findAny());
+            }
+        };
+    }
+}
\ No newline at end of file
--- a/src/java.base/share/classes/java/anyutil/stream/LongStream.java	Thu Jun 30 12:05:03 2016 +0100
+++ b/src/java.base/share/classes/java/anyutil/stream/LongStream.java	Thu Jun 30 16:14:28 2016 -0400
@@ -24,20 +24,7 @@
  */
 package java.anyutil.stream;
 
-import java.anyutil.function.DoubleBinaryOperator;
-import java.anyutil.function.DoubleConsumer;
-import java.anyutil.function.DoubleFunction;
-import java.anyutil.function.DoublePredicate;
-import java.anyutil.function.DoubleToIntFunction;
-import java.anyutil.function.DoubleToLongFunction;
-import java.anyutil.function.DoubleUnaryOperator;
-import java.anyutil.function.ObjDoubleConsumer;
-import java.nio.charset.Charset;
-import java.nio.file.Files;
-import java.nio.file.Path;
 import java.anyutil.Arrays;
-import java.anyutil.Collection;
-import java.util.DoubleSummaryStatistics;
 import java.util.LongSummaryStatistics;
 import java.util.Objects;
 import java.anyutil.OptionalDouble;
@@ -45,7 +32,6 @@
 import java.anyutil.PrimitiveIterator;
 import java.anyutil.Spliterator;
 import java.anyutil.Spliterators;
-import java.util.concurrent.ConcurrentHashMap;
 import java.anyutil.function.BiConsumer;
 import java.anyutil.function.Function;
 import java.anyutil.function.LongBinaryOperator;
@@ -58,10 +44,6 @@
 import java.anyutil.function.LongUnaryOperator;
 import java.anyutil.function.ObjLongConsumer;
 import java.anyutil.function.Supplier;
-import java.anyutil.stream.*;
-import java.anyutil.stream.BaseStream;
-import java.anyutil.stream.IntStream;
-import java.anyutil.stream.Stream;
 
 /**
  * A sequence of primitive long-valued elements supporting sequential and parallel
@@ -847,11 +829,10 @@
         Objects.requireNonNull(a);
         Objects.requireNonNull(b);
 
-//        Spliterator.OfLong split = new Streams.ConcatSpliterator.OfLong(
-//                a.spliterator(), b.spliterator());
-//        LongStream stream = StreamSupport.longStream(split, a.isParallel() || b.isParallel());
-//        return stream.onClose(Streams.composedClose(a, b));
-        throw new UnsupportedOperationException();
+        Spliterator<long> split = new Streams.ConcatSpliterator<>(a.spliterator().asSpliterator(),
+                                                                 b.spliterator().asSpliterator());
+        return LongStream.adapt(StreamSupport.stream(split, a.isParallel() || b.isParallel())
+                                             .onClose(Streams.composedClose(a, b)));
     }
 
     /**
--- a/src/java.base/share/classes/java/anyutil/stream/Pipeline.java	Thu Jun 30 12:05:03 2016 +0100
+++ b/src/java.base/share/classes/java/anyutil/stream/Pipeline.java	Thu Jun 30 16:14:28 2016 -0400
@@ -41,7 +41,6 @@
 import java.anyutil.function.ToLongFunction;
 import java.util.Objects;
 import java.anyutil.Spliterator;
-import java.util.stream.*;
 
 /**
  * Abstract base class for "pipeline" classes, which are the core
@@ -82,8 +81,7 @@
  * @param <E_OUT> type of output elements
  * @since 1.8
  */
-// @@@Vahalla: temporarily public
-public abstract class Pipeline<any E_SRC, any E_IN, any E_OUT>
+abstract class Pipeline<any E_SRC, any E_IN, any E_OUT>
         extends PipelineHelper<E_SRC, E_OUT>
         implements BaseStream<E_OUT, Stream<E_OUT>>, Stream<E_OUT> {
     private static final String MSG_STREAM_LINKED = "stream has already been operated upon or closed";
@@ -93,36 +91,36 @@
      * Backlink to the head of the pipeline chain (self if this is the source
      * stage).
      */
-    public final Head<E_SRC> sourceStage;
+    protected final Head<E_SRC> sourceStage;
 
     /**
      * The "upstream" pipeline, or null if this is the source stage.
      */
-    public final Pipeline<E_SRC, any, E_IN> previousStage;
+    protected final Pipeline<E_SRC, any, E_IN> previousStage;
 
     /**
      * The operation flags for the intermediate operation represented by this
      * pipeline object.
      */
-    public final int sourceOrOpFlags;
+    protected final int sourceOrOpFlags;
 
     /**
      * The next stage in the pipeline, or null if this is the last stage.
      * Effectively final at the point of linking to the next pipeline.
      */
-    public Pipeline<E_SRC, E_OUT, any> nextStage;
+    protected Pipeline<E_SRC, E_OUT, any> nextStage;
 
     /**
      * The combined source and operation flags for the source and all operations
      * up to and including the operation represented by this pipeline object.
      * Valid at the point of pipeline preparation for evaluation.
      */
-    public int combinedFlags;
+    protected int combinedFlags;
 
     /**
      * True if this pipeline has been linked or consumed
      */
-    public boolean linkedOrConsumed;
+    protected boolean linkedOrConsumed;
 
     /**
      * Constructor for the head of a stream pipeline.  MUST only be called from
@@ -193,8 +191,7 @@
 
     // Terminal evaluation methods
 
-    // @@@Valhalla: temporarily public
-    public final<any R> R rewriteAndExecute(TerminalOp<any, E_OUT, R> terminalOp) {
+    private <any R> R rewriteAndExecute(TerminalOp<any, E_OUT, R> terminalOp) {
         TerminalOp<E_SRC, E_OUT, R> op = terminalOp.reparent();
         return op.evaluateParallel(this, sourceSpliterator(op.getOpFlags()));
     }
@@ -285,8 +282,7 @@
      * of all computations up to and including the most recent stateful
      * operation.
      */
-    // @@@Valhalla: temporarily public
-    public Spliterator<E_SRC> sourceSpliterator(int terminalFlags) {
+    private Spliterator<E_SRC> sourceSpliterator(int terminalFlags) {
         // Get the source spliterator of the pipeline
         Spliterator<E_SRC> spliterator = sourceStage.sourceSpliterator();
 
@@ -338,8 +334,8 @@
 
     @Override
     @SuppressWarnings("unchecked")
-    // @@@Valhalla: temporarily public
-    public final Sink<E_SRC> wrapSink(Sink<E_OUT> sink) {
+    final Sink<E_SRC> wrapSink(Sink<E_OUT> sink) {
+        // @@@ Push down into Head and *Op implementations
         return this == sourceStage
                ? (Sink<E_SRC>)Objects.requireNonNull(sink)
                : previousStage.wrapSink(opWrapSink(previousStage.combinedFlags, sink));
@@ -381,10 +377,10 @@
      * @param generator the array generator
      * @return a Node holding the output of the pipeline
      */
-    Node<E_OUT> evaluateToNode(PipelineHelper<E_SRC, E_OUT> helper,
-                               Spliterator<E_SRC> spliterator,
-                               boolean flattenTree,
-                               IntFunction<E_OUT[]> generator) {
+    private Node<E_OUT> evaluateToNode(PipelineHelper<E_SRC, E_OUT> helper,
+                                       Spliterator<E_SRC> spliterator,
+                                       boolean flattenTree,
+                                       IntFunction<E_OUT[]> generator) {
         return Nodes.collect(helper, spliterator, flattenTree, generator);
     }
 
@@ -397,9 +393,9 @@
      * @param supplier the supplier of a spliterator
      * @return a wrapping spliterator compatible with this shape
      */
-    Spliterator<E_OUT> wrap(PipelineHelper<E_SRC, E_OUT> ph,
-                            Supplier<Spliterator<E_SRC>> supplier,
-                            boolean isParallel) {
+    private Spliterator<E_OUT> wrap(PipelineHelper<E_SRC, E_OUT> ph,
+                                    Supplier<Spliterator<E_SRC>> supplier,
+                                    boolean isParallel) {
         return new StreamSpliterators.WrappingSpliterator<>(ph, supplier, isParallel);
     }
 
@@ -465,9 +461,7 @@
      *         each element, and passes the results (if any) to the provided
      *         {@code Sink}.
      */
-    Sink<E_IN> opWrapSink(int flags, Sink<E_OUT> sink) {
-        throw new UnsupportedOperationException();
-    }
+    abstract Sink<E_IN> opWrapSink(int flags, Sink<E_OUT> sink);
 
     /**
      * Performs a parallel evaluation of the operation using the specified
@@ -524,18 +518,18 @@
     @Override
     public Stream<E_OUT> filter(Predicate<? super E_OUT> predicate) {
         Objects.requireNonNull(predicate);
-        return new UnsizedChainedOp<E_SRC, E_OUT, E_OUT>(this, StreamOpFlag.NOT_SIZED,
-                                                         (downstream, element) -> {
-                                                             if (predicate.test(element))
-                                                                 downstream.accept(element);
-                                                         });
+        return new UnsizedChainedOp<>(this, StreamOpFlag.NOT_SIZED,
+                                      (downstream, element) -> {
+                                          if (predicate.test(element))
+                                              downstream.accept(element);
+                                      });
     }
 
     @Override
     public final <any R> Stream<R> map(Function<? super E_OUT, ? extends R> mapper) {
         Objects.requireNonNull(mapper);
-        return new ChainedOp<E_SRC, E_OUT, R>(this, StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT,
-                                              (downstream, element) -> downstream.accept(mapper.apply(element)));
+        return new ChainedOp<>(this, StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT,
+                               (downstream, element) -> downstream.accept(mapper.apply(element)));
     }
 
     @Override
@@ -554,10 +548,10 @@
     }
 
     @Override
-    public <R> Stream<R> flatMap(Function<? super E_OUT, ? extends Stream<? extends R>> mapper) {
+    public <any R> Stream<R> flatMap(Function<? super E_OUT, ? extends Stream<? extends R>> mapper) {
         Objects.requireNonNull(mapper);
         // We can do better than this, by polling cancellationRequested when stream is infinite
-        return new UnsizedChainedOp<E_SRC, E_OUT, R>(this,
+        return new UnsizedChainedOp<>(this,
                                       StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT | StreamOpFlag.NOT_SIZED,
                                       (downstream, t) -> {
                                           try (Stream<? extends R> result = mapper.apply(t)) {
@@ -570,22 +564,24 @@
 
     @Override
     public IntStream flatMapToInt(Function<? super E_OUT, ? extends IntStream> mapper) {
-        throw new UnsupportedOperationException();
+        return IntStream.adapt(this.flatMap(i -> IntStream.adapt(mapper.apply(i))));
     }
 
     @Override
     public LongStream flatMapToLong(Function<? super E_OUT, ? extends LongStream> mapper) {
-        throw new UnsupportedOperationException();
+//        return LongStream.adapt(this.flatMap(i -> LongStream.adapt(mapper.apply(i))));
+        throw new UnsupportedOperationException("@@@");
     }
 
     @Override
     public DoubleStream flatMapToDouble(Function<? super E_OUT, ? extends DoubleStream> mapper) {
-        throw new UnsupportedOperationException();
+//        return DoubleStream.adapt(this.flatMap(i -> DoubleStream.adapt(mapper.apply(i))));
+        throw new UnsupportedOperationException("@@@");
     }
 
     @Override
     public Stream<E_OUT> distinct() {
-        return DistinctOps.<E_SRC, E_OUT>make(this);
+        return DistinctOps.make(this);
     }
 
     @Override
@@ -722,7 +718,20 @@
 
     @Override
     public Stream<E_OUT> unordered() {
-        throw new UnsupportedOperationException();
+        if (!isOrdered())
+            return this;
+        else
+            return new StatelessOp<E_SRC, E_OUT, E_OUT>(this, StreamOpFlag.NOT_ORDERED) {
+                @Override
+                Sink<E_OUT> opWrapSink(int flags, Sink<E_OUT> sink) {
+                    return sink;
+                }
+
+                @Override
+                <any NEWSRC> Pipeline<NEWSRC, E_OUT, E_OUT> reparent(Pipeline<NEWSRC, any, E_OUT> upstream) {
+                    throw new UnsupportedOperationException("@@@");
+                }
+            };
     }
 
     // Stream
@@ -760,7 +769,7 @@
          * True if there are any stateful ops in the pipeline; only valid for the
          * source stage.
          */
-        public boolean anyStateful;
+        private boolean anyStateful;
 
         /**
          * True if pipeline is parallel, otherwise the pipeline is sequential; only
@@ -889,7 +898,6 @@
      * @param <E_OUT> type of elements in produced by this stage
      * @since 1.8
      */
-    // @@@Valhalla: adding back "abstract" keyword causes failure for local/anon classes?
     public abstract static class StatelessOp<any E_SRC, any E_IN, any E_OUT>
             extends Pipeline<E_SRC, E_IN, E_OUT> {
         /**
@@ -913,7 +921,7 @@
         }
     }
 
-    //@@@Valhalla: temporary hack to work around local class handling
+    // @@@Valhalla: temporary hack to work around local class handling
     public static class ChainedOp<any E_SRC, any T, any R>
             extends StatelessOp<E_SRC, T, R> {
         private final BiConsumer<Sink<? super R>, T> consumer;
@@ -927,27 +935,19 @@
 
         @Override
         Sink<T> opWrapSink(int flags, Sink<R> sink) {
-            return new ChainedSinkHelper<>(consumer, sink);
+            return new Sink.ChainedSink<>(sink) {
+                @Override
+                public void accept(T t) {
+                    consumer.accept(downstream, t);
+                }
+            };
         }
 
         @Override
         <any NEWSRC> ChainedOp<NEWSRC, T, R> reparent(Pipeline<NEWSRC, any, T> upstream) {
             return new ChainedOp<>(upstream, sourceOrOpFlags, consumer);
         }
-    }
 
-    private static class ChainedSinkHelper<any T, any R> extends Sink.ChainedSink<T, R> {
-        private final BiConsumer<Sink<? super R>, T> consumer;
-
-        public ChainedSinkHelper(BiConsumer<Sink<? super R>, T> consumer, Sink<R> sink) {
-            super(sink);
-            this.consumer = consumer;
-        }
-
-        @Override
-        public void accept(T t) {
-            consumer.accept(downstream, t);
-        }
     }
 
     public static class UnsizedChainedOp<any E_SRC, any T, any R>
@@ -963,7 +963,17 @@
 
         @Override
         Sink<T> opWrapSink(int flags, Sink<R> sink) {
-            return new UnsizedChainedSinkHelper<>(consumer, sink);
+            return new Sink.ChainedSink<>(sink) {
+                @Override
+                public void begin(long size) {
+                    downstream.begin(-1);
+                }
+
+                @Override
+                public void accept(T t) {
+                    consumer.accept(downstream, t);
+                }
+            };
         }
 
         @Override
@@ -972,25 +982,6 @@
         }
     }
 
-    private static class UnsizedChainedSinkHelper<any T, any R> extends Sink.ChainedSink<T, R> {
-        private final BiConsumer<Sink<? super R>, T> consumer;
-
-        public UnsizedChainedSinkHelper(BiConsumer<Sink<? super R>, T> consumer, Sink<R> sink) {
-            super(sink);
-            this.consumer = consumer;
-        }
-
-        @Override
-        public void begin(long size) {
-            downstream.begin(-1);
-        }
-
-        @Override
-        public void accept(T t) {
-            consumer.accept(downstream, t);
-        }
-    }
-
     /**
      * Base class for a stateful intermediate stage of a Stream.
      *
--- a/src/java.base/share/classes/java/anyutil/stream/Sink.java	Thu Jun 30 12:05:03 2016 +0100
+++ b/src/java.base/share/classes/java/anyutil/stream/Sink.java	Thu Jun 30 16:14:28 2016 -0400
@@ -26,9 +26,6 @@
 
 import java.util.Objects;
 import java.anyutil.function.Consumer;
-import java.anyutil.function.DoubleConsumer;
-import java.anyutil.function.IntConsumer;
-import java.anyutil.function.LongConsumer;
 
 /**
  * An extension of {@link Consumer} used to conduct values through the stages of
--- a/src/java.base/share/classes/java/anyutil/stream/SliceOps.java	Thu Jun 30 12:05:03 2016 +0100
+++ b/src/java.base/share/classes/java/anyutil/stream/SliceOps.java	Thu Jun 30 16:14:28 2016 -0400
@@ -33,7 +33,7 @@
  *
  * @author Brian Goetz
  */
-public class SliceOps {
+class SliceOps {
 
     // No instances
     private SliceOps() { }
@@ -75,7 +75,7 @@
         private final long skip;
         private final long limit;
 
-        public SliceOp(Pipeline<E_SRC, any, T> upstream, int opFlags, long skip, long limit) {
+        SliceOp(Pipeline<E_SRC, any, T> upstream, int opFlags, long skip, long limit) {
             super(upstream, opFlags);
             this.skip = skip;
             this.limit = limit;
@@ -153,43 +153,33 @@
 
         @Override
         Sink<T> opWrapSink(int flags, Sink<T> sink) {
-            return new SliceChainedSink<>(sink, skip, limit);
-        }
+            return new Sink.ChainedSink<>(sink) {
+                long n = skip;
+                long m = limit >= 0 ? limit : Long.MAX_VALUE;
 
-        private static class SliceChainedSink<any T> extends Sink.ChainedSink<T, T> {
-            long skip;
-            long n;
-            long m;
+                @Override
+                public void begin(long size) {
+                    downstream.begin(calcSize(size, skip, m));
+                }
 
-            public SliceChainedSink(Sink<T> sink, long skip, long limit) {
-                super(sink);
-                this.skip = skip;
-                n = skip;
-                m = limit >= 0 ? limit : Long.MAX_VALUE;
-            }
-
-            @Override
-            public void begin(long size) {
-                downstream.begin(calcSize(size, skip, m));
-            }
-
-            @Override
-            public void accept(T t) {
-                if (n == 0) {
-                    if (m > 0) {
-                        m--;
-                        downstream.accept(t);
+                @Override
+                public void accept(T t) {
+                    if (n == 0) {
+                        if (m > 0) {
+                            m--;
+                            downstream.accept(t);
+                        }
+                    }
+                    else {
+                        n--;
                     }
                 }
-                else {
-                    n--;
+
+                @Override
+                public boolean cancellationRequested() {
+                    return m == 0 || downstream.cancellationRequested();
                 }
-            }
-
-            @Override
-            public boolean cancellationRequested() {
-                return m == 0 || downstream.cancellationRequested();
-            }
+            };
         }
     };
 
--- a/src/java.base/share/classes/java/anyutil/stream/Stream.java	Thu Jun 30 12:05:03 2016 +0100
+++ b/src/java.base/share/classes/java/anyutil/stream/Stream.java	Thu Jun 30 16:14:28 2016 -0400
@@ -267,7 +267,7 @@
      *               of new values
      * @return the new stream
      */
-    <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
+    <any R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
 
     /**
      * Returns an {@code IntStream} consisting of the results of replacing each
--- a/src/java.base/share/classes/java/anyutil/stream/StreamSupport.java	Thu Jun 30 12:05:03 2016 +0100
+++ b/src/java.base/share/classes/java/anyutil/stream/StreamSupport.java	Thu Jun 30 16:14:28 2016 -0400
@@ -102,14 +102,11 @@
      * @return a new sequential or parallel {@code Stream}
      * @see #stream(java.util.Spliterator, boolean)
      */
-    public static <T> Stream<T> stream(Supplier<? extends Spliterator<T>> supplier,
-                                       int characteristics,
-                                       boolean parallel) {
-//        Objects.requireNonNull(supplier);
-//        return new ReferencePipeline.Head<>(supplier,
-//                                            StreamOpFlag.fromCharacteristics(characteristics),
-//                                            parallel);
-        throw new UnsupportedOperationException();
+    public static <any T> Stream<T> stream(Supplier<? extends Spliterator<T>> supplier,
+                                           int characteristics,
+                                           boolean parallel) {
+        Objects.requireNonNull(supplier);
+        return new Pipeline.Head<T>(supplier, StreamOpFlag.fromCharacteristics(characteristics), parallel);
     }
 
     /**
@@ -134,10 +131,8 @@
      * @return a new sequential or parallel {@code IntStream}
      */
     public static IntStream intStream(Spliterator.OfInt spliterator, boolean parallel) {
-//        return new IntPipeline.Head<>(spliterator,
-//                                      StreamOpFlag.fromCharacteristics(spliterator),
-//                                      parallel);
-        throw new UnsupportedOperationException();
+        Objects.requireNonNull(spliterator);
+        return IntStream.adapt(stream(spliterator.asSpliterator(), parallel));
     }
 
     /**
@@ -175,10 +170,8 @@
     public static IntStream intStream(Supplier<? extends Spliterator.OfInt> supplier,
                                       int characteristics,
                                       boolean parallel) {
-//        return new IntPipeline.Head<>(supplier,
-//                                      StreamOpFlag.fromCharacteristics(characteristics),
-//                                      parallel);
-        throw new UnsupportedOperationException();
+        Objects.requireNonNull(supplier);
+        return IntStream.adapt(StreamSupport.<int>stream(() -> supplier.get().asSpliterator(), characteristics, parallel));
     }
 
     /**
@@ -204,10 +197,8 @@
      */
     public static LongStream longStream(Spliterator.OfLong spliterator,
                                         boolean parallel) {
-//        return new LongPipeline.Head<>(spliterator,
-//                                       StreamOpFlag.fromCharacteristics(spliterator),
-//                                       parallel);
-        throw new UnsupportedOperationException();
+        Objects.requireNonNull(spliterator);
+        return LongStream.adapt(stream(spliterator.asSpliterator(), parallel));
     }
 
     /**
@@ -245,10 +236,8 @@
     public static LongStream longStream(Supplier<? extends Spliterator.OfLong> supplier,
                                         int characteristics,
                                         boolean parallel) {
-//        return new LongPipeline.Head<>(supplier,
-//                                       StreamOpFlag.fromCharacteristics(characteristics),
-//                                       parallel);
-        throw new UnsupportedOperationException();
+        Objects.requireNonNull(supplier);
+        return LongStream.adapt(StreamSupport.<long>stream(() -> supplier.get().asSpliterator(), characteristics, parallel));
     }
 
     /**
@@ -274,10 +263,8 @@
      */
     public static DoubleStream doubleStream(Spliterator.OfDouble spliterator,
                                             boolean parallel) {
-//        return new DoublePipeline.Head<>(spliterator,
-//                                         StreamOpFlag.fromCharacteristics(spliterator),
-//                                         parallel);
-        throw new UnsupportedOperationException();
+        Objects.requireNonNull(spliterator);
+        return DoubleStream.adapt(stream(spliterator.asSpliterator(), parallel));
     }
 
     /**
@@ -315,9 +302,7 @@
     public static DoubleStream doubleStream(Supplier<? extends Spliterator.OfDouble> supplier,
                                             int characteristics,
                                             boolean parallel) {
-//        return new DoublePipeline.Head<>(supplier,
-//                                         StreamOpFlag.fromCharacteristics(characteristics),
-//                                         parallel);
-        throw new UnsupportedOperationException();
+        Objects.requireNonNull(supplier);
+        return DoubleStream.adapt(StreamSupport.<double>stream(() -> supplier.get().asSpliterator(), characteristics, parallel));
     }
 }
--- a/src/java.base/share/classes/java/anyutil/stream/Streams.java	Thu Jun 30 12:05:03 2016 +0100
+++ b/src/java.base/share/classes/java/anyutil/stream/Streams.java	Thu Jun 30 16:14:28 2016 -0400
@@ -41,8 +41,7 @@
  *
  * @since 1.8
  */
-// @@@Valhalla: temporarily public
-public final class Streams {
+final class Streams {
 
     private Streams() {
         throw new Error("no instances");
@@ -471,7 +470,7 @@
                 count = -count - 1;
                 // Use this spliterator if 0 or 1 elements, otherwise use
                 // the spliterator of the spined buffer
-                return (c < 2) ? StreamSupport.intStream(this, false) : StreamSupport.intStream(Spliterator.OfInt.adapt(buffer.spliterator()), false);
+                return (c < 2) ? StreamSupport.intStream(this, false) : StreamSupport.intStream(Spliterator.OfInt.fromSpliterator(buffer.spliterator()), false);
             }
 
             throw new IllegalStateException();
@@ -562,7 +561,7 @@
                 count = -count - 1;
                 // Use this spliterator if 0 or 1 elements, otherwise use
                 // the spliterator of the spined buffer
-                return (c < 2) ? StreamSupport.longStream(this, false) : StreamSupport.longStream(Spliterator.OfLong.adapt(buffer.spliterator()), false);
+                return (c < 2) ? StreamSupport.longStream(this, false) : StreamSupport.longStream(Spliterator.OfLong.fromSpliterator(buffer.spliterator()), false);
             }
 
             throw new IllegalStateException();
@@ -653,7 +652,7 @@
                 count = -count - 1;
                 // Use this spliterator if 0 or 1 elements, otherwise use
                 // the spliterator of the spined buffer
-                return (c < 2) ? StreamSupport.doubleStream(this, false) : StreamSupport.doubleStream(Spliterator.OfDouble.adapt(buffer.spliterator()), false);
+                return (c < 2) ? StreamSupport.doubleStream(this, false) : StreamSupport.doubleStream(Spliterator.OfDouble.fromSpliterator(buffer.spliterator()), false);
             }
 
             throw new IllegalStateException();
@@ -688,16 +687,15 @@
         }
     }
 
-    static class ConcatSpliterator<any T, T_SPLITR extends Spliterator<T>>
-            implements Spliterator<T> {
-        protected final T_SPLITR aSpliterator;
-        protected final T_SPLITR bSpliterator;
+    static class ConcatSpliterator<any T> implements Spliterator<T> {
+        private final Spliterator<T> aSpliterator;
+        private final Spliterator<T> bSpliterator;
         // True when no split has occurred, otherwise false
         boolean beforeSplit;
         // Never read after splitting
         final boolean unsized;
 
-        public ConcatSpliterator(T_SPLITR aSpliterator, T_SPLITR bSpliterator) {
+        public ConcatSpliterator(Spliterator<T> aSpliterator, Spliterator<T> bSpliterator) {
             this.aSpliterator = aSpliterator;
             this.bSpliterator = bSpliterator;
             beforeSplit = true;
@@ -707,9 +705,8 @@
         }
 
         @Override
-        public T_SPLITR trySplit() {
-            @SuppressWarnings("unchecked")
-            T_SPLITR ret = beforeSplit ? aSpliterator : (T_SPLITR) bSpliterator.trySplit();
+        public Spliterator<T> trySplit() {
+            Spliterator<T> ret = beforeSplit ? aSpliterator : bSpliterator.trySplit();
             beforeSplit = false;
             return ret;
         }
@@ -768,66 +765,6 @@
                 throw new IllegalStateException();
             return bSpliterator.getComparator();
         }
-
-        static class OfRef<T> extends ConcatSpliterator<T, Spliterator<T>> {
-            OfRef(Spliterator<T> aSpliterator, Spliterator<T> bSpliterator) {
-                super(aSpliterator, bSpliterator);
-            }
-        }
-
-        private static abstract class OfPrimitive<T, T_CONS, T_SPLITR extends Spliterator.OfPrimitive<T, T_CONS, T_SPLITR>>
-                extends ConcatSpliterator<T, T_SPLITR>
-                implements Spliterator.OfPrimitive<T, T_CONS, T_SPLITR> {
-            private OfPrimitive(T_SPLITR aSpliterator, T_SPLITR bSpliterator) {
-                super(aSpliterator, bSpliterator);
-            }
-
-            @Override
-            public boolean tryAdvance(T_CONS action) {
-                boolean hasNext;
-                if (beforeSplit) {
-                    hasNext = aSpliterator.tryAdvance(action);
-                    if (!hasNext) {
-                        beforeSplit = false;
-                        hasNext = bSpliterator.tryAdvance(action);
-                    }
-                }
-                else
-                    hasNext = bSpliterator.tryAdvance(action);
-                return hasNext;
-            }
-
-            @Override
-            public void forEachRemaining(T_CONS action) {
-                if (beforeSplit)
-                    aSpliterator.forEachRemaining(action);
-                bSpliterator.forEachRemaining(action);
-            }
-        }
-
-        static class OfInt
-                extends ConcatSpliterator.OfPrimitive<Integer, IntConsumer, Spliterator.OfInt>
-                implements Spliterator.OfInt {
-            OfInt(Spliterator.OfInt aSpliterator, Spliterator.OfInt bSpliterator) {
-                super(aSpliterator, bSpliterator);
-            }
-        }
-
-        static class OfLong
-                extends ConcatSpliterator.OfPrimitive<Long, LongConsumer, Spliterator.OfLong>
-                implements Spliterator.OfLong {
-            OfLong(Spliterator.OfLong aSpliterator, Spliterator.OfLong bSpliterator) {
-                super(aSpliterator, bSpliterator);
-            }
-        }
-
-        static class OfDouble
-                extends ConcatSpliterator.OfPrimitive<Double, DoubleConsumer, Spliterator.OfDouble>
-                implements Spliterator.OfDouble {
-            OfDouble(Spliterator.OfDouble aSpliterator, Spliterator.OfDouble bSpliterator) {
-                super(aSpliterator, bSpliterator);
-            }
-        }
     }
 
     /**
--- a/test/valhalla/test/valhalla/anyutil/SimplePipelineTest.java	Thu Jun 30 12:05:03 2016 +0100
+++ b/test/valhalla/test/valhalla/anyutil/SimplePipelineTest.java	Thu Jun 30 16:14:28 2016 -0400
@@ -147,20 +147,44 @@
         }
     }
 
-//    public void testLimit() {
-//        List<String> limit = Stream.range(0, 100000)
-//                                   .limit(3)
-//                                   .map(Integer::toString)
-//                                   .collect(toList());
-//        assertEquals(limit, Arrays.asList("0", "1", "2"));
-//
-//        List<String> skipLimit = Stream.range(0, 100000)
-//                                       .skip(3)
-//                                       .limit(3)
-//                                       .map(Integer::toString)
-//                                       .collect(toList());
-//        assertEquals(limit, Arrays.asList("3", "4", "5"));
-//    }
+    public void testLimit() {
+        assertEquals(Stream.range(0, 5)
+                           .skip(0)
+                           .map(Integer::toString)
+                           .collect(toList()),
+                     Arrays.asList("0", "1", "2", "3", "4"));
+
+        assertEquals(Stream.range(0, 5)
+                           .skip(0)
+                           .map(Integer::toString)
+                           .collect(toList()),
+                     Arrays.asList("0", "1", "2", "3", "4"));
+
+        assertEquals(Stream.range(0, 5)
+                           .limit(10)
+                           .map(Integer::toString)
+                           .collect(toList()),
+                     Arrays.asList("0", "1", "2", "3", "4"));
+
+        List<String> limit = Stream.range(0, 100000)
+                                   .limit(3)
+                                   .map(Integer::toString)
+                                   .collect(toList());
+        assertEquals(limit, Arrays.asList("0", "1", "2"));
+
+        assertEquals(Stream.range(0, 5)
+                           .skip(3)
+                           .map(Integer::toString)
+                           .collect(toList()),
+                     Arrays.asList("3", "4"));
+
+        assertEquals(Stream.range(0, 100000)
+                           .skip(3)
+                           .limit(3)
+                           .map(Integer::toString)
+                           .collect(toList()),
+                     Arrays.asList("3", "4", "5"));
+    }
 //
 //    public void testParallelLimit() {
 //        Set<Thread> threads = new HashSet<>();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/valhalla/test/valhalla/model3/EncoderTest.java	Thu Jun 30 16:14:28 2016 -0400
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package valhalla.model3;
+
+import java.anyutil.function.Consumer;
+
+import org.testng.annotations.Test;
+import valhalla.model3.Model3Converter.ParameterizedType;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+/**
+ * EncoderTest
+ *
+ * @author Brian Goetz
+ */
+@Test
+public class EncoderTest {
+    private void assertEncode(String encoded, ParameterizedType pt) {
+        assertEquals(encoded, pt.toString());
+    }
+
+    private<T> void assertVarargs(T[] arr, T... values) {
+        assertEquals(arr, values);
+    }
+
+    private void decode(String s, Consumer<ParameterizedType> c) {
+        c.accept(ParameterizedType.decode(s));
+    }
+
+    private void enclosing(ParameterizedType pt, Consumer<ParameterizedType> c) {
+        c.accept(pt.enclosing);
+    }
+
+    public void testEncoder() {
+        assertEncode("Foo", new ParameterizedType("Foo"));
+        assertEncode("Foo", new ParameterizedType("Foo", "_"));
+        assertEncode("Foo", new ParameterizedType("Foo", ""));
+        assertEncode("Foo", new ParameterizedType("Foo", (String) null));
+
+        assertEncode("Foo$${I}", new ParameterizedType("Foo", "I"));
+        assertEncode("Foo$${IJ}", new ParameterizedType("Foo", "I", "J"));
+        assertEncode("Foo$${I_J}", new ParameterizedType("Foo", "I", null, "J"));
+
+//        @@@ Not sure logic for allErasedRecursive is right
+//        assertEncode("Foo$$$Bar", new ParameterizedType(new ParameterizedType("Foo"), "Bar"));
+
+        assertEncode("Foo$${I}$$$Bar", new ParameterizedType(new ParameterizedType("Foo", "I"), "Bar"));
+        assertEncode("Foo$${I}$$$Bar$${J}", new ParameterizedType(new ParameterizedType("Foo", "I"), "Bar", "J"));
+        assertEncode("Foo$$$Bar$${J}", new ParameterizedType(new ParameterizedType("Foo"), "Bar", "J"));
+
+        assertEncode("Foo$${I}$$$Bar$${J}$$$Baz$${I}", new ParameterizedType(new ParameterizedType(new ParameterizedType("Foo", "I"), "Bar", "J"), "Baz", "I"));
+    }
+
+    public void decodeTest() {
+        decode("Foo", pt -> {
+            assertEquals("Foo", pt.className);
+            assertNull(pt.enclosing);
+        });
+        decode("Foo$$$Bar", pt -> {
+            assertEquals("Bar", pt.className);
+            enclosing(pt, e -> {
+                          assertEquals("Foo", e.className);
+                          assertNull(e.enclosing);
+                      });
+        });
+        decode("Foo$${I}", pt -> {
+            assertEquals("Foo", pt.className);
+            assertNull(pt.enclosing);
+            assertVarargs(pt.params, "I");
+        });
+        decode("Foo$${I_J}", pt -> {
+            assertEquals("Foo", pt.className);
+            assertNull(pt.enclosing);
+            assertVarargs(pt.params, "I", "_", "J");
+        });
+        decode("Bar$${J}$$$Foo$${I_J}", pt -> {
+            assertEquals("Foo", pt.className);
+            assertVarargs(pt.params, "I", "_", "J");
+            enclosing(pt, e -> {
+                assertEquals("Bar", e.className);
+                assertNull(e.enclosing);
+                assertVarargs(e.params, "J");
+            });
+        });
+        decode("Bar$$$Foo$${I_J}", pt -> {
+            assertEquals("Foo", pt.className);
+            assertVarargs(pt.params, "I", "_", "J");
+            enclosing(pt, e -> {
+                assertEquals("Bar", e.className);
+                assertNull(e.enclosing);
+                assertVarargs(e.params);
+            });
+        });
+    }
+
+    public void encodeDecodeTest() {
+        ParameterizedType outer = ParameterizedType.decode("Foo$${I}");
+        ParameterizedType inner = ParameterizedType.decode("Bar$${J}");
+        assertEquals("Foo$${I}$$$Bar$${J}", new ParameterizedType(outer, inner.className, inner.params).encode());
+    }
+}