changeset 7679:1a6b75a82fe0

Eliminate Collector.Of{Int,Long,Double}; make {Int,Long,Double}Statistics top-level classes; add .statistics() methods to {Int,Long,DOuble}Stream; add Collectors.to{ILD}Statistics(mapper); add Collectors.reduce(mapper, reducer); eliminate Collectors.mapper forms; more efficient implementation of {ILD}Stream.average
author briangoetz
date Mon, 18 Mar 2013 13:06:17 -0400
parents b6d5e6bab103
children 6681e0e40f33
files src/share/classes/java/util/stream/Collector.java src/share/classes/java/util/stream/Collectors.java src/share/classes/java/util/stream/DoublePipeline.java src/share/classes/java/util/stream/DoubleStatistics.java src/share/classes/java/util/stream/DoubleStream.java src/share/classes/java/util/stream/IntPipeline.java src/share/classes/java/util/stream/IntStatistics.java src/share/classes/java/util/stream/IntStream.java src/share/classes/java/util/stream/LongPipeline.java src/share/classes/java/util/stream/LongStatistics.java src/share/classes/java/util/stream/LongStream.java src/share/classes/java/util/stream/ReduceOps.java test-ng/tests/org/openjdk/tests/java/util/stream/ReduceByOpTest.java test-ng/tests/org/openjdk/tests/java/util/stream/TabulatorsTest.java
diffstat 14 files changed, 479 insertions(+), 363 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/util/stream/Collector.java	Mon Mar 18 10:56:29 2013 +0100
+++ b/src/share/classes/java/util/stream/Collector.java	Mon Mar 18 13:06:17 2013 -0400
@@ -111,37 +111,4 @@
      * @return True if this collector is stable
      */
     default boolean isStable() { return false; }
-
-    interface OfInt<R> extends Collector<Integer, R> {
-        ObjIntConsumer<R> intAccumulator();
-
-        @Override
-        default BiFunction<R, Integer, R> accumulator() {
-            if (Tripwire.ENABLED)
-                Tripwire.trip(getClass(), "{0} calling Collector.OfInt.accumulator()");
-            return (r, i) -> { intAccumulator().accept(r, i); return r; };
-        }
-    }
-
-    interface OfLong<R> extends Collector<Long, R> {
-        ObjLongConsumer<R> longAccumulator();
-
-        @Override
-        default BiFunction<R, Long, R> accumulator() {
-            if (Tripwire.ENABLED)
-                Tripwire.trip(getClass(), "{0} calling Collector.OfLong.accumulator()");
-            return (r, l) -> { longAccumulator().accept(r, l); return r; };
-        }
-    }
-
-    interface OfDouble<R> extends Collector<Double, R> {
-        ObjDoubleConsumer<R> doubleAccumulator();
-
-        @Override
-        default BiFunction<R, Double, R> accumulator() {
-            if (Tripwire.ENABLED)
-                Tripwire.trip(getClass(), "{0} calling Collector.OfDouble.accumulator()");
-            return (r, d) -> { doubleAccumulator().accept(r, d); return r; };
-        }
-    }
 }
--- a/src/share/classes/java/util/stream/Collectors.java	Mon Mar 18 10:56:29 2013 +0100
+++ b/src/share/classes/java/util/stream/Collectors.java	Mon Mar 18 13:06:17 2013 -0400
@@ -36,21 +36,14 @@
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Objects;
-import java.util.OptionalDouble;
-import java.util.OptionalLong;
 import java.util.Set;
 import java.util.StringJoiner;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.function.BiFunction;
 import java.util.function.BinaryOperator;
-import java.util.function.DoubleConsumer;
 import java.util.function.Function;
-import java.util.function.IntConsumer;
-import java.util.function.LongConsumer;
-import java.util.function.ObjDoubleConsumer;
-import java.util.function.ObjIntConsumer;
-import java.util.function.ObjLongConsumer;
+import java.util.function.IntBinaryOperator;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 import java.util.function.ToDoubleFunction;
@@ -134,60 +127,6 @@
         }
     }
 
-    static final class IntCollectorImpl<R>
-            extends AbstractCollectorImpl<Integer, R> implements Collector.OfInt<R> {
-        private final ObjIntConsumer<R> intAccumulator;
-
-        IntCollectorImpl(Supplier<R> resultSupplier,
-                         ObjIntConsumer<R> accumulator,
-                         BinaryOperator<R> combiner,
-                         boolean isConcurrent) {
-            super(resultSupplier, combiner, isConcurrent, true);
-            this.intAccumulator = accumulator;
-        }
-
-        @Override
-        public ObjIntConsumer<R> intAccumulator() {
-            return intAccumulator;
-        }
-    }
-
-    static final class DoubleCollectorImpl<R>
-            extends AbstractCollectorImpl<Double, R> implements Collector.OfDouble<R> {
-        private final ObjDoubleConsumer<R> doubleAccumulator;
-
-        DoubleCollectorImpl(Supplier<R> resultSupplier,
-                            ObjDoubleConsumer<R> accumulator,
-                            BinaryOperator<R> combiner,
-                            boolean isConcurrent) {
-            super(resultSupplier, combiner, isConcurrent, true);
-            this.doubleAccumulator = accumulator;
-        }
-
-        @Override
-        public ObjDoubleConsumer<R> doubleAccumulator() {
-            return doubleAccumulator;
-        }
-    }
-
-    static final class LongCollectorImpl<R>
-            extends AbstractCollectorImpl<Long, R> implements Collector.OfLong<R> {
-        private final ObjLongConsumer<R> longAccumulator;
-
-        LongCollectorImpl(Supplier<R> resultSupplier,
-                          ObjLongConsumer<R> accumulator,
-                          BinaryOperator<R> combiner,
-                          boolean isConcurrent) {
-            super(resultSupplier, combiner, isConcurrent, true);
-            this.longAccumulator = accumulator;
-        }
-
-        @Override
-        public ObjLongConsumer<R> longAccumulator() {
-            return longAccumulator;
-        }
-    }
-
     /**
      * Accumulate elements into a new {@code Collection}, which is created by the provided factory.
      *
@@ -326,33 +265,16 @@
                                    downstream.combiner(), downstream.isConcurrent(), downstream.isStable());
     }
 
-    public static <T, R> Collector<T, R>
-    mapping(ToIntFunction<? super T> mapper, Collector.OfInt<R> downstream) {
-        ObjIntConsumer<R> downstreamAccumulator = downstream.intAccumulator();
-        return new CollectorImpl<>(downstream.resultSupplier(),
-                                   (r, t) -> { downstreamAccumulator.accept(r, mapper.applyAsInt(t)); return r; },
-                                   downstream.combiner(), downstream.isConcurrent(), downstream.isStable());
+    public static <T> Collector<T, T>
+    reducing(BinaryOperator<T> op) {
+        return new CollectorImpl<>(() -> null, (r, t) -> (r == null ? t : op.apply(r, t)), op, false, false);
     }
 
-    public static <T, R> Collector<T, R>
-    mapping(ToDoubleFunction<? super T> mapper, Collector.OfDouble<R> downstream) {
-        ObjDoubleConsumer<R> downstreamAccumulator = downstream.doubleAccumulator();
-        return new CollectorImpl<>(downstream.resultSupplier(),
-                                   (r, t) -> { downstreamAccumulator.accept(r, mapper.applyAsDouble(t)); return r; },
-                                   downstream.combiner(), downstream.isConcurrent(), downstream.isStable());
-    }
-
-    public static <T, R> Collector<T, R>
-    mapping(ToLongFunction<? super T> mapper, Collector.OfLong<R> downstream) {
-        ObjLongConsumer<R> downstreamAccumulator = downstream.longAccumulator();
-        return new CollectorImpl<>(downstream.resultSupplier(),
-                                   (r, t) -> { downstreamAccumulator.accept(r, mapper.applyAsLong(t)); return r; },
-                                   downstream.combiner(), downstream.isConcurrent(), downstream.isStable());
-    }
-
-    public static <T> Collector<T, T>
-    reducing(BinaryOperator<T> op) {
-        return new CollectorImpl<>(() -> null, (r, t) -> (r == null ? t : op.apply(t, r)), op, false, false);
+    public static <T, U> Collector<T, U>
+    reducing(Function<? super T, ? extends U> mapper, BinaryOperator<U> op) {
+        return new CollectorImpl<>(() -> null,
+                                   (r, t) -> (r == null ? mapper.apply(t) : op.apply(r, mapper.apply(t))),
+                                   op, false, false);
     }
 
     public static<T, K> Collector<T, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> classifier) {
@@ -417,7 +339,7 @@
                 downstreamAccumulator.apply(m.computeIfAbsent(key, k -> downstreamSupplier.get()), t);
                 return m;
             };
-            return new Collectors.CollectorImpl<>(mapFactory, accumulator, leftMapMerger(downstream.combiner()), true, true);
+            return new CollectorImpl<>(mapFactory, accumulator, leftMapMerger(downstream.combiner()), true, true);
         }
         else if (downstream.isStable()) {
             BiFunction<M, T, M> accumulator = (m, t) -> {
@@ -428,7 +350,7 @@
                 }
                 return m;
             };
-            return new Collectors.CollectorImpl<>(mapFactory, accumulator, leftMapMerger(downstream.combiner()), true, true);
+            return new CollectorImpl<>(mapFactory, accumulator, leftMapMerger(downstream.combiner()), true, true);
         }
         else {
             BiFunction<M, T, M> accumulator = (m, t) -> {
@@ -451,7 +373,7 @@
                     }
                 } while (true);
             };
-            return new Collectors.CollectorImpl<>(mapFactory, accumulator, leftMapMerger(downstream.combiner()), true, true);
+            return new CollectorImpl<>(mapFactory, accumulator, leftMapMerger(downstream.combiner()), true, true);
         }
     }
 
@@ -566,7 +488,22 @@
                                     Supplier<M> mapSupplier,
                                     BinaryOperator<U> mergeFunction) {
         BiFunction<M, T, M> accumulator = (map, value) -> { map.merge(value, mapper.apply(value), mergeFunction); return map; };
-        return new Collectors.CollectorImpl<>(mapSupplier, accumulator, Collectors.leftMapMerger(mergeFunction), true, true);
+        return new CollectorImpl<>(mapSupplier, accumulator, leftMapMerger(mergeFunction), true, true);
+    }
+
+    public static<T> Collector<T, IntStatistics> toIntStatistics(ToIntFunction<? super T> mapper) {
+        return new CollectorImpl<>(IntStatistics::new, (r, t) -> { r.accept(mapper.applyAsInt(t)); return r; },
+                                   (l, r) -> { l.combine(r); return l; }, false, true);
+    }
+
+    public static<T> Collector<T, LongStatistics> toLongStatistics(ToLongFunction<? super T> mapper) {
+        return new CollectorImpl<>(LongStatistics::new, (r, t) -> { r.accept(mapper.applyAsLong(t)); return r; },
+                                   (l, r) -> { l.combine(r); return l; }, false, true);
+    }
+
+    public static<T> Collector<T, DoubleStatistics> toDoubleStatistics(ToDoubleFunction<? super T> mapper) {
+        return new CollectorImpl<>(DoubleStatistics::new, (r, t) -> { r.accept(mapper.applyAsDouble(t)); return r; },
+                                   (l, r) -> { l.combine(r); return l; }, false, true);
     }
 
     static final class Partition<T> extends AbstractMap<Boolean, T> implements Map<Boolean, T> {
@@ -647,137 +584,4 @@
             return left;
         };
     }
-
-    /**
-     * A state object for collecting statistics such as count, min, max, sum, and average for {@code long} values.
-     */
-    public static final class LongStatistics implements LongConsumer, IntConsumer {
-        private long count;
-        private long sum;
-        private long min = Long.MAX_VALUE;
-        private long max = Long.MIN_VALUE;
-
-        @Override
-        public void accept(int value) {
-            accept((long) value);
-        }
-
-        @Override
-        public void accept(long value) {
-            ++count;
-            sum += value;
-            min = Math.min(min, value);
-            max = Math.max(max, value);
-        }
-
-        private void combine(LongStatistics other) {
-            count += other.count;
-            sum += other.sum;
-            min = Math.min(min, other.min);
-            max = Math.min(max, other.max);
-        }
-
-        public long getCount() {
-            return count;
-        }
-
-        public long getSum() {
-            return sum;
-        }
-
-        public OptionalLong getMin() {
-            return count > 0 ? OptionalLong.of(min) : OptionalLong.empty();
-        }
-
-        public OptionalLong getMax() {
-            return count > 0 ? OptionalLong.of(max) : OptionalLong.empty();
-        }
-
-        public OptionalDouble getMean() {
-            return count > 0 ? OptionalDouble.of((double) sum / count) : OptionalDouble.empty();
-        }
-
-        @Override
-        public String toString() {
-            return new StringJoiner(", ", this.getClass().getSimpleName() + "{", "}")
-            .add("count=" + getCount())
-                    .add("sum=" + getSum())
-                    .add("min=" + getMin())
-                    .add("max=" + getMax())
-                    .toString();
-        }
-    }
-
-    /**
-     * A state object for collecting statistics such as count, min, max, sum, and average for {@code double} values.
-     */
-    public static final class DoubleStatistics implements DoubleConsumer {
-        private long count;
-        private double sum;
-        private double min = Double.POSITIVE_INFINITY;
-        private double max = Double.NEGATIVE_INFINITY;
-
-        @Override
-        public void accept(double value) {
-            ++count;
-            sum += value;
-            min = Math.min(min, value);
-            max = Math.max(max, value);
-        }
-
-        private void combine(DoubleStatistics other) {
-            count += other.count;
-            sum += other.sum;
-            min = Math.min(min, other.min);
-            max = Math.min(max, other.max);
-        }
-
-        public long getCount() {
-            return count;
-        }
-
-        public double getSum() {
-            return sum;
-        }
-
-        public OptionalDouble getMin() {
-            return count > 0 ? OptionalDouble.of(min) : OptionalDouble.empty();
-        }
-
-        public OptionalDouble getMax() {
-            return count > 0 ? OptionalDouble.of(max) : OptionalDouble.empty();
-        }
-
-        public OptionalDouble getMean() {
-            return count > 0 ? OptionalDouble.of(sum / count) : OptionalDouble.empty();
-        }
-
-        @Override
-        public String toString() {
-            return new StringJoiner(", ", this.getClass().getSimpleName() + "{", "}")
-                    .add("count=" + getCount())
-                    .add("sum=" + getSum())
-                    .add("min=" + getMin())
-                    .add("max=" + getMax())
-                    .toString();
-        }
-    }
-
-    /**
-     * Collect summary statistics for a set of {@code long} values.
-     * @return A {@code Collector} which summarizes {@code long} values into a {@link LongStatistics}
-     */
-    public static Collector.OfLong<LongStatistics> toLongStatistics() {
-        return new LongCollectorImpl<>(LongStatistics::new, LongStatistics::accept,
-                                      (l, r) -> { l.combine(r); return l; }, false);
-    }
-
-    /**
-     * Collect summary statistics for a set of {@code double} values.
-     * @return A {@code Collector} which summarizes {@code double} values into a {@link DoubleStatistics}
-     */
-    public static Collector.OfDouble<DoubleStatistics> toDoubleStatistics() {
-        return new DoubleCollectorImpl<>(DoubleStatistics::new, DoubleStatistics::accept,
-                                         (l, r) -> { l.combine(r); return l; }, false);
-    }
 }
--- a/src/share/classes/java/util/stream/DoublePipeline.java	Mon Mar 18 10:56:29 2013 +0100
+++ b/src/share/classes/java/util/stream/DoublePipeline.java	Mon Mar 18 13:06:17 2013 -0400
@@ -36,10 +36,11 @@
 import java.util.function.DoubleConsumer;
 import java.util.function.DoubleFunction;
 import java.util.function.DoublePredicate;
+import java.util.function.DoubleToIntFunction;
+import java.util.function.DoubleToLongFunction;
 import java.util.function.DoubleUnaryOperator;
 import java.util.function.IntFunction;
 import java.util.function.ObjDoubleConsumer;
-import java.util.function.ObjIntConsumer;
 import java.util.function.Supplier;
 
 /**
@@ -98,7 +99,7 @@
 
     @Override
     protected <T> AbstractPipeline<?, T, ?> stream(Supplier<? extends Spliterator<?>> supplier, int flags) {
-        return new DoublePipeline((Supplier<? extends Spliterator<Double>>) supplier, flags);
+        return new DoublePipeline(supplier, flags);
     }
 
     @Override
@@ -131,7 +132,7 @@
 
     @Override
     public Stream<Double> boxed() {
-        return map(i -> Double.valueOf(i));
+        return map(Double::valueOf);
     }
 
     static Spliterator.OfDouble adapt(Spliterator<Double> s) {
@@ -177,6 +178,30 @@
     }
 
     @Override
+    public IntStream map(DoubleToIntFunction mapper) {
+        Objects.requireNonNull(mapper);
+        return chainedToInt(StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT, StreamShape.DOUBLE_VALUE,
+                            (flags, sink) -> new Sink.ChainedDouble(sink) {
+                                @Override
+                                public void accept(double t) {
+                                    downstream.accept(mapper.applyAsInt(t));
+                                }
+                            });
+    }
+
+    @Override
+    public LongStream map(DoubleToLongFunction mapper) {
+        Objects.requireNonNull(mapper);
+        return chainedToLong(StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT, StreamShape.DOUBLE_VALUE,
+                             (flags, sink) -> new Sink.ChainedDouble(sink) {
+                                 @Override
+                                 public void accept(double t) {
+                                     downstream.accept(mapper.applyAsLong(t));
+                                 }
+                             });
+    }
+
+    @Override
     public DoubleStream flatMap(DoubleFunction<? extends DoubleStream> mapper) {
         return flatMap((double i, DoubleConsumer sink) -> mapper.apply(i).sequential().forEach(sink));
     }
@@ -287,7 +312,16 @@
 
     @Override
     public OptionalDouble average() {
-        return collect(Collectors.toDoubleStatistics()).getMean();
+        double[] avg = collect(() -> new double[2],
+                               (ll, i) -> {
+                                   ll[0]++;
+                                   ll[1] += i;
+                               },
+                               (ll, rr) -> {
+                                   ll[0] += rr[0];
+                                   ll[1] += rr[1];
+                               });
+        return avg[0] > 0 ? OptionalDouble.of(avg[1] / avg[0]) : OptionalDouble.empty();
     }
 
     @Override
@@ -296,6 +330,11 @@
     }
 
     @Override
+    public DoubleStatistics statistics() {
+        return collect(DoubleStatistics::new, DoubleStatistics::accept, DoubleStatistics::combine);
+    }
+
+    @Override
     public double reduce(double identity, DoubleBinaryOperator op) {
         return pipeline(ReduceOps.makeDouble(identity, op));
     }
@@ -311,25 +350,7 @@
             combiner.accept(left, right);
             return left;
         };
-        return collect(new Collectors.DoubleCollectorImpl<>(resultFactory, accumulator, operator, false));
-    }
-
-    @Override
-    public <R> R collect(Collector.OfDouble<R> collector) {
-        return pipeline(ReduceOps.makeDouble(collector));
-    }
-
-    @Override
-    public <R> R collectUnordered(Collector.OfDouble<R> collector) {
-        if (collector.isConcurrent()) {
-            R container = collector.resultSupplier().get();
-            ObjDoubleConsumer<R> accumulator = collector.doubleAccumulator();
-            forEach(u -> accumulator.accept(container, u));
-            return container;
-        }
-        else {
-            return collect(collector);
-        }
+        return pipeline(ReduceOps.makeDouble(resultFactory, accumulator, operator));
     }
 
     @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/stream/DoubleStatistics.java	Mon Mar 18 13:06:17 2013 -0400
@@ -0,0 +1,83 @@
+/*
+ * 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 java.util.stream;
+
+import java.util.StringJoiner;
+import java.util.function.DoubleConsumer;
+
+/**
+ * A state object for collecting statistics such as count, min, max, sum, and average for {@code double} values.
+ */
+public class DoubleStatistics implements DoubleConsumer {
+    private long count;
+    private double sum;
+    private double min = Double.POSITIVE_INFINITY;
+    private double max = Double.NEGATIVE_INFINITY;
+
+    @Override
+    public void accept(double value) {
+        ++count;
+        sum += value;
+        min = Math.min(min, value);
+        max = Math.max(max, value);
+    }
+
+    public void combine(DoubleStatistics other) {
+        count += other.count;
+        sum += other.sum;
+        min = Math.min(min, other.min);
+        max = Math.min(max, other.max);
+    }
+
+    public long getCount() {
+        return count;
+    }
+
+    public double getSum() {
+        return sum;
+    }
+
+    public double getMin() {
+        return min;
+    }
+
+    public double getMax() {
+        return max;
+    }
+
+    public double getMean() {
+        return count > 0 ? sum / count : 0.0d;
+    }
+
+    @Override
+    public String toString() {
+        return new StringJoiner(", ", this.getClass().getSimpleName() + "{", "}")
+                .add("count=" + getCount())
+                .add("sum=" + getSum())
+                .add("min=" + getMin())
+                .add("max=" + getMax())
+                .toString();
+    }
+}
--- a/src/share/classes/java/util/stream/DoubleStream.java	Mon Mar 18 10:56:29 2013 +0100
+++ b/src/share/classes/java/util/stream/DoubleStream.java	Mon Mar 18 13:06:17 2013 -0400
@@ -31,6 +31,8 @@
 import java.util.function.DoubleConsumer;
 import java.util.function.DoubleFunction;
 import java.util.function.DoublePredicate;
+import java.util.function.DoubleToIntFunction;
+import java.util.function.DoubleToLongFunction;
 import java.util.function.DoubleUnaryOperator;
 import java.util.function.ObjDoubleConsumer;
 import java.util.function.ObjIntConsumer;
@@ -57,6 +59,10 @@
 
     <U> Stream<U> map(DoubleFunction<U> mapper);
 
+    IntStream map(DoubleToIntFunction mapper);
+
+    LongStream map(DoubleToLongFunction mapper);
+
     DoubleStream flatMap(DoubleFunction<? extends DoubleStream> mapper);
 
     DoubleStream flatMap(FlatMapper.OfDoubleToDouble mapper);
@@ -87,10 +93,6 @@
                   ObjDoubleConsumer<R> accumulator,
                   BiConsumer<R, R> combiner);
 
-    <R> R collect(Collector.OfDouble<R> collector);
-
-    <R> R collectUnordered(Collector.OfDouble<R> collector);
-
     boolean anyMatch(DoublePredicate predicate);
 
     boolean allMatch(DoublePredicate predicate);
@@ -115,5 +117,7 @@
 
     long count();
 
+    DoubleStatistics statistics();
+
     double[] toArray();
 }
--- a/src/share/classes/java/util/stream/IntPipeline.java	Mon Mar 18 10:56:29 2013 +0100
+++ b/src/share/classes/java/util/stream/IntPipeline.java	Mon Mar 18 13:06:17 2013 -0400
@@ -37,6 +37,8 @@
 import java.util.function.IntConsumer;
 import java.util.function.IntFunction;
 import java.util.function.IntPredicate;
+import java.util.function.IntToDoubleFunction;
+import java.util.function.IntToLongFunction;
 import java.util.function.IntUnaryOperator;
 import java.util.function.ObjIntConsumer;
 import java.util.function.Supplier;
@@ -97,7 +99,7 @@
 
     @Override
     protected <T> AbstractPipeline<?, T, ?> stream(Supplier<? extends Spliterator<?>> supplier, int flags) {
-        return new IntPipeline((Supplier<? extends Spliterator<Integer>>) supplier, flags);
+        return new IntPipeline(supplier, flags);
     }
 
     @Override
@@ -170,7 +172,7 @@
 
     @Override
     public Stream<Integer> boxed() {
-        return map(i -> Integer.valueOf(i));
+        return map(Integer::valueOf);
     }
 
     @Override
@@ -198,6 +200,30 @@
     }
 
     @Override
+    public LongStream map(IntToLongFunction mapper) {
+        Objects.requireNonNull(mapper);
+        return chainedToLong(StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT, StreamShape.INT_VALUE,
+                             (flags, sink) -> new Sink.ChainedInt(sink) {
+                                 @Override
+                                 public void accept(int t) {
+                                     downstream.accept(mapper.applyAsLong(t));
+                                 }
+                             });
+    }
+
+    @Override
+    public DoubleStream map(IntToDoubleFunction mapper) {
+        Objects.requireNonNull(mapper);
+        return chainedToDouble(StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT, StreamShape.INT_VALUE,
+                               (flags, sink) -> new Sink.ChainedInt(sink) {
+                                   @Override
+                                   public void accept(int t) {
+                                       downstream.accept(mapper.applyAsDouble(t));
+                                   }
+                               });
+    }
+
+    @Override
     public IntStream flatMap(IntFunction<? extends IntStream> mapper) {
         return flatMap((int i, IntConsumer sink) -> mapper.apply(i).sequential().forEach(sink));
     }
@@ -312,7 +338,21 @@
 
     @Override
     public OptionalDouble average() {
-        return longs().average();
+        long[] avg = collect(() -> new long[2],
+                             (ll, i) -> {
+                                 ll[0]++;
+                                 ll[1] += i;
+                             },
+                             (ll, rr) -> {
+                                 ll[0] += rr[0];
+                                 ll[1] += rr[1];
+                             });
+        return avg[0] > 0 ? OptionalDouble.of((double) avg[1] / avg[0]) : OptionalDouble.empty();
+    }
+
+    @Override
+    public IntStatistics statistics() {
+        return collect(IntStatistics::new, IntStatistics::accept, IntStatistics::combine);
     }
 
     @Override
@@ -331,25 +371,7 @@
             combiner.accept(left, right);
             return left;
         };
-        return collect(new Collectors.IntCollectorImpl<>(resultFactory, accumulator, operator, false));
-    }
-
-    @Override
-    public <R> R collect(Collector.OfInt<R> collector) {
-        return pipeline(ReduceOps.makeInt(collector));
-    }
-
-    @Override
-    public <R> R collectUnordered(Collector.OfInt<R> collector) {
-        if (collector.isConcurrent()) {
-            R container = collector.resultSupplier().get();
-            ObjIntConsumer<R> accumulator = collector.intAccumulator();
-            forEach(u -> accumulator.accept(container, u));
-            return container;
-        }
-        else {
-            return collect(collector);
-        }
+        return pipeline(ReduceOps.makeInt(resultFactory, accumulator, operator));
     }
 
     @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/stream/IntStatistics.java	Mon Mar 18 13:06:17 2013 -0400
@@ -0,0 +1,84 @@
+/*
+ * 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 java.util.stream;
+
+import java.util.StringJoiner;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+
+/**
+ * A state object for collecting statistics such as count, min, max, sum, and average for {@code int} values.
+ */
+public class IntStatistics implements IntConsumer {
+    private long count;
+    private int sum;
+    private int min = Integer.MIN_VALUE;
+    private int max = Integer.MAX_VALUE;
+
+    @Override
+    public void accept(int value) {
+        ++count;
+        sum += value;
+        min = Math.min(min, value);
+        max = Math.max(max, value);
+    }
+
+    public void combine(IntStatistics other) {
+        count += other.count;
+        sum += other.sum;
+        min = Math.min(min, other.min);
+        max = Math.min(max, other.max);
+    }
+
+    public long getCount() {
+        return count;
+    }
+
+    public int getSum() {
+        return sum;
+    }
+
+    public int getMin() {
+        return min;
+    }
+
+    public int getMax() {
+        return max;
+    }
+
+    public double getMean() {
+        return count > 0 ? sum / count : 0.0d;
+    }
+
+    @Override
+    public String toString() {
+        return new StringJoiner(", ", this.getClass().getSimpleName() + "{", "}")
+                .add("count=" + getCount())
+                .add("sum=" + getSum())
+                .add("min=" + getMin())
+                .add("max=" + getMax())
+                .toString();
+    }
+}
--- a/src/share/classes/java/util/stream/IntStream.java	Mon Mar 18 10:56:29 2013 +0100
+++ b/src/share/classes/java/util/stream/IntStream.java	Mon Mar 18 13:06:17 2013 -0400
@@ -31,6 +31,8 @@
 import java.util.function.IntConsumer;
 import java.util.function.IntFunction;
 import java.util.function.IntPredicate;
+import java.util.function.IntToDoubleFunction;
+import java.util.function.IntToLongFunction;
 import java.util.function.IntUnaryOperator;
 import java.util.function.ObjIntConsumer;
 import java.util.function.Supplier;
@@ -86,6 +88,22 @@
     <U> Stream<U> map(IntFunction<U> mapper);
 
     /**
+     * Transform this stream into one consisting of the result of applying the given function to the
+     * elements of this stream.  This is an <a href="package-summary.html#StreamOps">intermediate operation</a>.
+     * @param mapper The function to be applied to each element
+     * @return the new stream
+     */
+    LongStream map(IntToLongFunction mapper);
+
+    /**
+     * Transform this stream into one consisting of the result of applying the given function to the
+     * elements of this stream.  This is an <a href="package-summary.html#StreamOps">intermediate operation</a>.
+     * @param mapper The function to be applied to each element
+     * @return the new stream
+     */
+    DoubleStream map(IntToDoubleFunction mapper);
+
+    /**
      * Transform this stream into one where each element is replaced with the contents of the stream produced
      * by applying the provided function to that element.  This is
      * an <a href="package-summary.html#StreamOps">intermediate operation</a>.
@@ -146,10 +164,6 @@
                   ObjIntConsumer<R> accumulator,
                   BiConsumer<R, R> combiner);
 
-    <R> R collect(Collector.OfInt<R> collector);
-
-    <R> R collectUnordered(Collector.OfInt<R> collector);
-
     boolean anyMatch(IntPredicate predicate);
 
     boolean allMatch(IntPredicate predicate);
@@ -174,5 +188,7 @@
 
     OptionalDouble average();
 
+    IntStatistics statistics();
+
     int[] toArray();
 }
--- a/src/share/classes/java/util/stream/LongPipeline.java	Mon Mar 18 10:56:29 2013 +0100
+++ b/src/share/classes/java/util/stream/LongPipeline.java	Mon Mar 18 13:06:17 2013 -0400
@@ -38,8 +38,9 @@
 import java.util.function.LongConsumer;
 import java.util.function.LongFunction;
 import java.util.function.LongPredicate;
+import java.util.function.LongToDoubleFunction;
+import java.util.function.LongToIntFunction;
 import java.util.function.LongUnaryOperator;
-import java.util.function.ObjIntConsumer;
 import java.util.function.ObjLongConsumer;
 import java.util.function.Supplier;
 
@@ -99,7 +100,7 @@
 
     @Override
     protected <T> AbstractPipeline<?, T, ?> stream(Supplier<? extends Spliterator<?>> supplier, int flags) {
-        return new LongPipeline((Supplier<? extends Spliterator<Long>>) supplier, flags);
+        return new LongPipeline(supplier, flags);
     }
 
     @Override
@@ -161,7 +162,7 @@
 
     @Override
     public Stream<Long> boxed() {
-        return map(i -> Long.valueOf(i));
+        return map(Long::valueOf);
     }
 
     @Override
@@ -189,6 +190,30 @@
     }
 
     @Override
+    public IntStream map(LongToIntFunction mapper) {
+        Objects.requireNonNull(mapper);
+        return chainedToInt(StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT, StreamShape.LONG_VALUE,
+                            (flags, sink) -> new Sink.ChainedLong(sink) {
+                                @Override
+                                public void accept(long t) {
+                                    downstream.accept(mapper.applyAsInt(t));
+                                }
+                            });
+    }
+
+    @Override
+    public DoubleStream map(LongToDoubleFunction mapper) {
+        Objects.requireNonNull(mapper);
+        return chainedToDouble(StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT, StreamShape.LONG_VALUE,
+                               (flags, sink) -> new Sink.ChainedLong(sink) {
+                                   @Override
+                                   public void accept(long t) {
+                                       downstream.accept(mapper.applyAsDouble(t));
+                                   }
+                               });
+    }
+
+    @Override
     public LongStream flatMap(LongFunction<? extends LongStream> mapper) {
         return flatMap((long i, LongConsumer sink) -> mapper.apply(i).sequential().forEach(sink));
     }
@@ -299,7 +324,16 @@
 
     @Override
     public OptionalDouble average() {
-        return collect(Collectors.toLongStatistics()).getMean();
+        long[] avg = collect(() -> new long[2],
+                             (ll, i) -> {
+                                 ll[0]++;
+                                 ll[1] += i;
+                             },
+                             (ll, rr) -> {
+                                 ll[0] += rr[0];
+                                 ll[1] += rr[1];
+                             });
+        return avg[0] > 0 ? OptionalDouble.of((double) avg[1] / avg[0]) : OptionalDouble.empty();
     }
 
     @Override
@@ -308,6 +342,11 @@
     }
 
     @Override
+    public LongStatistics statistics() {
+        return collect(LongStatistics::new, LongStatistics::accept, LongStatistics::combine);
+    }
+
+    @Override
     public long reduce(long identity, LongBinaryOperator op) {
         return pipeline(ReduceOps.makeLong(identity, op));
     }
@@ -323,25 +362,7 @@
             combiner.accept(left, right);
             return left;
         };
-        return collect(new Collectors.LongCollectorImpl<>(resultFactory, accumulator, operator, false));
-    }
-
-    @Override
-    public <R> R collect(Collector.OfLong<R> collector) {
-        return pipeline(ReduceOps.makeLong(collector));
-    }
-
-    @Override
-    public <R> R collectUnordered(Collector.OfLong<R> collector) {
-        if (collector.isConcurrent()) {
-            R container = collector.resultSupplier().get();
-            ObjLongConsumer<R> accumulator = collector.longAccumulator();
-            forEach(u -> accumulator.accept(container, u));
-            return container;
-        }
-        else {
-            return collect(collector);
-        }
+        return pipeline(ReduceOps.makeLong(resultFactory, accumulator, operator));
     }
 
     @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/util/stream/LongStatistics.java	Mon Mar 18 13:06:17 2013 -0400
@@ -0,0 +1,89 @@
+/*
+ * 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 java.util.stream;
+
+import java.util.StringJoiner;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+/**
+ * A state object for collecting statistics such as count, min, max, sum, and average for {@code long} values.
+ */
+public class LongStatistics implements LongConsumer, IntConsumer {
+    private long count;
+    private long sum;
+    private long min = Long.MAX_VALUE;
+    private long max = Long.MIN_VALUE;
+
+    @Override
+    public void accept(int value) {
+        accept((long) value);
+    }
+
+    @Override
+    public void accept(long value) {
+        ++count;
+        sum += value;
+        min = Math.min(min, value);
+        max = Math.max(max, value);
+    }
+
+    public void combine(LongStatistics other) {
+        count += other.count;
+        sum += other.sum;
+        min = Math.min(min, other.min);
+        max = Math.min(max, other.max);
+    }
+
+    public long getCount() {
+        return count;
+    }
+
+    public long getSum() {
+        return sum;
+    }
+
+    public long getMin() {
+        return min;
+    }
+
+    public long getMax() {
+        return max;
+    }
+
+    public double getMean() {
+        return count > 0 ? (double) sum / count : 0.0d;
+    }
+
+    @Override
+    public String toString() {
+        return new StringJoiner(", ", this.getClass().getSimpleName() + "{", "}")
+        .add("count=" + getCount())
+                .add("sum=" + getSum())
+                .add("min=" + getMin())
+                .add("max=" + getMax())
+                .toString();
+    }
+}
--- a/src/share/classes/java/util/stream/LongStream.java	Mon Mar 18 10:56:29 2013 +0100
+++ b/src/share/classes/java/util/stream/LongStream.java	Mon Mar 18 13:06:17 2013 -0400
@@ -31,6 +31,8 @@
 import java.util.function.LongConsumer;
 import java.util.function.LongFunction;
 import java.util.function.LongPredicate;
+import java.util.function.LongToDoubleFunction;
+import java.util.function.LongToIntFunction;
 import java.util.function.LongUnaryOperator;
 import java.util.function.ObjIntConsumer;
 import java.util.function.ObjLongConsumer;
@@ -59,6 +61,10 @@
 
     <U> Stream<U> map(LongFunction<U> mapper);
 
+    IntStream map(LongToIntFunction mapper);
+
+    DoubleStream map(LongToDoubleFunction mapper);
+
     LongStream flatMap(LongFunction<? extends LongStream> mapper);
 
     LongStream flatMap(FlatMapper.OfLongToLong mapper);
@@ -89,10 +95,6 @@
                   ObjLongConsumer<R> accumulator,
                   BiConsumer<R, R> combiner);
 
-    <R> R collect(Collector.OfLong<R> collector);
-
-    <R> R collectUnordered(Collector.OfLong<R> collector);
-
     boolean anyMatch(LongPredicate predicate);
 
     boolean allMatch(LongPredicate predicate);
@@ -117,5 +119,7 @@
 
     long count();
 
+    LongStatistics statistics();
+
     long[] toArray();
 }
--- a/src/share/classes/java/util/stream/ReduceOps.java	Mon Mar 18 10:56:29 2013 +0100
+++ b/src/share/classes/java/util/stream/ReduceOps.java	Mon Mar 18 13:06:17 2013 -0400
@@ -318,15 +318,17 @@
      * Constructs a {@code TerminalOp} that implements a mutable reduce on
      * {@code int} values
      *
-     * @param collector A {@code Collector} defining the reduction
+     * @param supplier
+     * @param accumulator
+     * @param combiner
      * @param <R> The type of the result
      * @return A {@code ReduceOp} implementing the reduction
      */
     public static <R> TerminalOp<Integer, R>
-    makeInt(Collector.OfInt<R> collector) {
-        Supplier<R> supplier = Objects.requireNonNull(collector).resultSupplier();
-        ObjIntConsumer<R> accumulator = collector.intAccumulator();
-        BinaryOperator<R> combiner = collector.combiner();
+    makeInt(Supplier<R> supplier, ObjIntConsumer<R> accumulator, BinaryOperator<R> combiner) {
+        Objects.requireNonNull(supplier);
+        Objects.requireNonNull(accumulator);
+        Objects.requireNonNull(combiner);
         class ReducingSink extends Box<R> implements AccumulatingSink<Integer, R, ReducingSink>, Sink.OfInt {
             @Override
             public void begin(long size) {
@@ -446,15 +448,17 @@
      * Constructs a {@code TerminalOp} that implements a mutable reduce on
      * {@code long} values
      *
-     * @param collector A {@code Collector} defining the reduction
+     * @param supplier
+     * @param accumulator
+     * @param combiner
      * @param <R> The type of the result
      * @return A {@code TerminalOp} implementing the reduction
      */
     public static <R> TerminalOp<Long, R>
-    makeLong(Collector.OfLong<R> collector) {
-        Supplier<R> supplier = Objects.requireNonNull(collector).resultSupplier();
-        ObjLongConsumer<R> accumulator = collector.longAccumulator();
-        BinaryOperator<R> combiner = collector.combiner();
+    makeLong(Supplier<R> supplier, ObjLongConsumer<R> accumulator, BinaryOperator<R> combiner) {
+        Objects.requireNonNull(supplier);
+        Objects.requireNonNull(accumulator);
+        Objects.requireNonNull(combiner);
         class ReducingSink extends Box<R> implements AccumulatingSink<Long, R, ReducingSink>, Sink.OfLong {
             @Override
             public void begin(long size) {
@@ -574,14 +578,17 @@
      * Constructs a {@code TerminalOp} that implements a mutable reduce on
      * {@code double} values
      *
-     * @param collector A {@code Collector} defining the reduction
+     * @param supplier
+     * @param accumulator
+     * @param combiner
      * @param <R> The type of the result
      * @return A {@code TerminalOp} implementing the reduction
      */
-    public static <R> TerminalOp<Double, R> makeDouble(Collector.OfDouble<R> collector) {
-        Supplier<R> supplier = Objects.requireNonNull(collector).resultSupplier();
-        ObjDoubleConsumer<R> accumulator = collector.doubleAccumulator();
-        BinaryOperator<R> combiner = collector.combiner();
+    public static <R> TerminalOp<Double, R>
+    makeDouble(Supplier<R> supplier, ObjDoubleConsumer<R> accumulator, BinaryOperator<R> combiner) {
+        Objects.requireNonNull(supplier);
+        Objects.requireNonNull(accumulator);
+        Objects.requireNonNull(combiner);
         class ReducingSink extends Box<R> implements AccumulatingSink<Double, R, ReducingSink>, Sink.OfDouble {
             @Override
             public void begin(long size) {
--- a/test-ng/tests/org/openjdk/tests/java/util/stream/ReduceByOpTest.java	Mon Mar 18 10:56:29 2013 +0100
+++ b/test-ng/tests/org/openjdk/tests/java/util/stream/ReduceByOpTest.java	Mon Mar 18 13:06:17 2013 -0400
@@ -30,16 +30,11 @@
 import java.util.stream.StreamTestDataProvider;
 import org.testng.annotations.Test;
 
-import java.util.Collection;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.function.Functions;
-import java.util.stream.Collector;
-import java.util.stream.Collectors;
 
 import static java.util.stream.Collectors.groupingBy;
-import static java.util.stream.Collectors.mapping;
 import static java.util.stream.Collectors.reducing;
 import static java.util.stream.LambdaTestHelpers.*;
 
@@ -63,7 +58,7 @@
 
         int uniqueSize = data.into(new HashSet<Integer>()).size();
         Map<Integer, List<Integer>> mgResult = exerciseTerminalOps(data, s -> s.collect(groupingBy(mId)));
-        Map<Integer, Integer> miResult = exerciseTerminalOps(data, s -> s.collect(groupingBy(mId, mapping(e -> 1, reducing(Integer::sum)))));
+        Map<Integer, Integer> miResult = exerciseTerminalOps(data, s -> s.collect(groupingBy(mId, reducing(e -> 1, Integer::sum))));
         assertEquals(miResult.keySet().size(), uniqueSize);
         for (Map.Entry<Integer, Integer> entry : miResult.entrySet())
             assertEquals((int) entry.getValue(), mgResult.get(entry.getKey()).size());
--- a/test-ng/tests/org/openjdk/tests/java/util/stream/TabulatorsTest.java	Mon Mar 18 10:56:29 2013 +0100
+++ b/test-ng/tests/org/openjdk/tests/java/util/stream/TabulatorsTest.java	Mon Mar 18 13:06:17 2013 -0400
@@ -52,7 +52,6 @@
 
 import static java.util.stream.Collectors.groupingBy;
 import static java.util.stream.Collectors.groupingByConcurrent;
-import static java.util.stream.Collectors.mapping;
 import static java.util.stream.Collectors.partitioningBy;
 import static java.util.stream.Collectors.reducing;
 import static java.util.stream.Collectors.toCollection;
@@ -326,23 +325,23 @@
 
         // Single-level map-reduce
         exerciseMapTabulation(data,
-                              groupingBy(classifier, mapping(mDoubler, reducing(Integer::sum))),
+                              groupingBy(classifier, reducing(mDoubler, Integer::sum)),
                               new GroupedMapAssertion<>(classifier, HashMap.class,
                                                         new ReduceAssertion<>(mDoubler, Integer::sum)));
         // with concurrent
         exerciseMapTabulation(data,
-                              groupingByConcurrent(classifier, mapping(mDoubler, reducing(Integer::sum))),
+                              groupingByConcurrent(classifier, reducing(mDoubler, Integer::sum)),
                               new GroupedMapAssertion<>(classifier, ConcurrentHashMap.class,
                                                         new ReduceAssertion<>(mDoubler, Integer::sum)));
 
         // With explicit constructors
         exerciseMapTabulation(data,
-                              groupingBy(classifier, TreeMap::new, mapping(mDoubler, reducing(Integer::sum))),
+                              groupingBy(classifier, TreeMap::new, reducing(mDoubler, Integer::sum)),
                               new GroupedMapAssertion<>(classifier, TreeMap.class,
                                                         new ReduceAssertion<>(mDoubler, Integer::sum)));
         // with concurrent
         exerciseMapTabulation(data,
-                              groupingByConcurrent(classifier, ConcurrentSkipListMap::new, mapping(mDoubler, reducing(Integer::sum))),
+                              groupingByConcurrent(classifier, ConcurrentSkipListMap::new, reducing(mDoubler, Integer::sum)),
                               new GroupedMapAssertion<>(classifier, ConcurrentSkipListMap.class,
                                                         new ReduceAssertion<>(mDoubler, Integer::sum)));
     }