1 /* 2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package org.openjdk.tests.java.util.stream; 26 27 import java.util.Arrays; 28 import java.util.Comparator; 29 import java.util.List; 30 import java.util.Spliterator; 31 import java.util.function.Consumer; 32 import java.util.function.DoubleConsumer; 33 import java.util.function.Function; 34 import java.util.function.IntConsumer; 35 import java.util.function.LongConsumer; 36 import java.util.function.UnaryOperator; 37 import java.util.stream.DoubleStream; 38 import java.util.stream.DoubleStreamTestDataProvider; 39 import java.util.stream.IntStream; 40 import java.util.stream.IntStreamTestDataProvider; 41 import java.util.stream.LambdaTestHelpers; 42 import java.util.stream.LongStream; 43 import java.util.stream.LongStreamTestDataProvider; 44 import java.util.stream.OpTestCase; 45 import java.util.stream.SpliteratorTestHelper; 46 import java.util.stream.Stream; 47 import java.util.stream.StreamSupport; 48 import java.util.stream.StreamTestDataProvider; 49 import java.util.stream.TestData; 50 51 import org.testng.Assert; 52 import org.testng.annotations.Test; 53 54 import static java.util.stream.LambdaTestHelpers.countTo; 55 import static java.util.stream.LambdaTestHelpers.dpEven; 56 import static java.util.stream.LambdaTestHelpers.ipEven; 57 import static java.util.stream.LambdaTestHelpers.irDoubler; 58 import static java.util.stream.LambdaTestHelpers.lpEven; 59 import static java.util.stream.LambdaTestHelpers.mDoubler; 60 import static java.util.stream.LambdaTestHelpers.pEven; 61 import static java.util.stream.LambdaTestHelpers.permuteStreamFunctions; 62 63 @Test 64 public class StreamSpliteratorTest extends OpTestCase { 65 66 private static class ProxyNoExactSizeSpliterator<T> implements Spliterator<T> { 67 final Spliterator<T> sp; 68 final boolean proxyEstimateSize; 69 int splits = 0; 70 int prefixSplits = 0; 71 72 long sizeOnTraversal = -1; 73 74 ProxyNoExactSizeSpliterator(Spliterator<T> sp, boolean proxyEstimateSize) { 75 this.sp = sp; 76 this.proxyEstimateSize = proxyEstimateSize; 77 } 78 79 @Override 80 public Spliterator<T> trySplit() { 81 splits++; 82 Spliterator<T> prefix = sp.trySplit(); 83 if (prefix != null) 84 prefixSplits++; 85 return prefix; 86 } 87 88 @Override 89 public boolean tryAdvance(Consumer<? super T> consumer) { 90 if (sizeOnTraversal == -1) 91 sizeOnTraversal = sp.getExactSizeIfKnown(); 92 return sp.tryAdvance(consumer); 93 } 94 95 @Override 96 public void forEachRemaining(Consumer<? super T> consumer) { 97 sizeOnTraversal = sp.getExactSizeIfKnown(); 98 sp.forEachRemaining(consumer); 99 } 100 101 @Override 102 public long estimateSize() { 103 return proxyEstimateSize ? sp.estimateSize() : Long.MAX_VALUE; 104 } 105 106 @Override 107 public Comparator<? super T> getComparator() { 108 return sp.getComparator(); 109 } 110 111 @Override 112 public int characteristics() { 113 if (proxyEstimateSize) 114 return sp.characteristics(); 115 else 116 return sp.characteristics() & ~(Spliterator.SUBSIZED | Spliterator.SIZED); 117 } 118 119 private static class OfInt extends ProxyNoExactSizeSpliterator<Integer> implements Spliterator.OfInt { 120 final Spliterator.OfInt psp; 121 122 private OfInt(Spliterator.OfInt sp, boolean proxyEstimateSize) { 123 super(sp, proxyEstimateSize); 124 this.psp = sp; 125 } 126 127 @Override 128 public Spliterator.OfInt trySplit() { 129 splits++; 130 Spliterator.OfInt prefix = psp.trySplit(); 131 if (prefix != null) 132 prefixSplits++; 133 return prefix; 134 } 135 136 @Override 137 public boolean tryAdvance(Consumer<? super Integer> consumer) { 138 return Spliterator.OfInt.super.tryAdvance(consumer); 139 } 140 141 @Override 142 public void forEachRemaining(Consumer<? super Integer> consumer) { 143 Spliterator.OfInt.super.forEachRemaining(consumer); 144 } 145 146 @Override 147 public boolean tryAdvance(IntConsumer consumer) { 148 if (sizeOnTraversal == -1) 149 sizeOnTraversal = sp.getExactSizeIfKnown(); 150 return psp.tryAdvance(consumer); 151 } 152 153 @Override 154 public void forEachRemaining(IntConsumer consumer) { 155 sizeOnTraversal = sp.getExactSizeIfKnown(); 156 psp.forEachRemaining(consumer); 157 } 158 } 159 160 private static class OfLong extends ProxyNoExactSizeSpliterator<Long> implements Spliterator.OfLong { 161 final Spliterator.OfLong psp; 162 163 private OfLong(Spliterator.OfLong sp, boolean proxyEstimateSize) { 164 super(sp, proxyEstimateSize); 165 this.psp = sp; 166 } 167 168 @Override 169 public Spliterator.OfLong trySplit() { 170 splits++; 171 Spliterator.OfLong prefix = psp.trySplit(); 172 if (prefix != null) 173 prefixSplits++; 174 return prefix; 175 } 176 177 @Override 178 public boolean tryAdvance(Consumer<? super Long> consumer) { 179 return Spliterator.OfLong.super.tryAdvance(consumer); 180 } 181 182 @Override 183 public void forEachRemaining(Consumer<? super Long> consumer) { 184 Spliterator.OfLong.super.forEachRemaining(consumer); 185 } 186 187 @Override 188 public boolean tryAdvance(LongConsumer consumer) { 189 if (sizeOnTraversal == -1) 190 sizeOnTraversal = sp.getExactSizeIfKnown(); 191 return psp.tryAdvance(consumer); 192 } 193 194 @Override 195 public void forEachRemaining(LongConsumer consumer) { 196 sizeOnTraversal = sp.getExactSizeIfKnown(); 197 psp.forEachRemaining(consumer); 198 } 199 } 200 201 private static class OfDouble extends ProxyNoExactSizeSpliterator<Double> 202 implements Spliterator.OfDouble { 203 final Spliterator.OfDouble psp; 204 205 private OfDouble(Spliterator.OfDouble sp, boolean proxyEstimateSize) { 206 super(sp, proxyEstimateSize); 207 this.psp = sp; 208 } 209 210 @Override 211 public Spliterator.OfDouble trySplit() { 212 splits++; 213 Spliterator.OfDouble prefix = psp.trySplit(); 214 if (prefix != null) 215 prefixSplits++; 216 return prefix; 217 } 218 219 @Override 220 public boolean tryAdvance(Consumer<? super Double> consumer) { 221 return Spliterator.OfDouble.super.tryAdvance(consumer); 222 } 223 224 @Override 225 public void forEachRemaining(Consumer<? super Double> consumer) { 226 Spliterator.OfDouble.super.forEachRemaining(consumer); 227 } 228 229 @Override 230 public boolean tryAdvance(DoubleConsumer consumer) { 231 if (sizeOnTraversal == -1) 232 sizeOnTraversal = sp.getExactSizeIfKnown(); 233 return psp.tryAdvance(consumer); 234 } 235 236 @Override 237 public void forEachRemaining(DoubleConsumer consumer) { 238 sizeOnTraversal = sp.getExactSizeIfKnown(); 239 psp.forEachRemaining(consumer); 240 } 241 } 242 } 243 244 public void testSplitting() { 245 // Size is assumed to be larger than the target size for no splitting 246 // @@@ Need way to obtain the target size 247 List<Integer> l = countTo(1000); 248 249 List<Consumer<Stream<Integer>>> terminalOps = Arrays.asList( 250 s -> s.toArray(), 251 s -> s.forEach(e -> { }), 252 s -> s.reduce(Integer::sum) 253 ); 254 255 List<UnaryOperator<Stream<Integer>>> intermediateOps = Arrays.asList( 256 s -> s.parallel(), 257 // The following ensures the wrapping spliterator is tested 258 s -> s.map(LambdaTestHelpers.identity()).parallel() 259 ); 260 261 for (Consumer<Stream<Integer>> terminalOp : terminalOps) { 262 for (UnaryOperator<Stream<Integer>> intermediateOp : intermediateOps) { 263 for (boolean proxyEstimateSize : new boolean[]{false, true}) { 264 Spliterator<Integer> sp = intermediateOp.apply(l.stream()).spliterator(); 265 ProxyNoExactSizeSpliterator<Integer> psp = new ProxyNoExactSizeSpliterator<>(sp, proxyEstimateSize); 266 Stream<Integer> s = StreamSupport.parallelStream(psp); 267 terminalOp.accept(s); 268 Assert.assertTrue(psp.splits > 0, 269 String.format("Number of splits should be greater that zero when proxyEstimateSize is %s", 270 proxyEstimateSize)); 271 Assert.assertTrue(psp.prefixSplits > 0, 272 String.format("Number of non-null prefix splits should be greater that zero when proxyEstimateSize is %s", 273 proxyEstimateSize)); 274 Assert.assertTrue(psp.sizeOnTraversal < l.size(), 275 String.format("Size on traversal of last split should be less than the size of the list, %d, when proxyEstimateSize is %s", 276 l.size(), proxyEstimateSize)); 277 } 278 } 279 } 280 } 281 282 @Test(dataProvider = "StreamTestData<Integer>", 283 dataProviderClass = StreamTestDataProvider.class, 284 groups = { "serialization-hostile" }) 285 public void testStreamSpliterators(String name, TestData.OfRef<Integer> data) { 286 for (Function<Stream<Integer>, Stream<Integer>> f : streamFunctions()) { 287 withData(data). 288 stream((Stream<Integer> in) -> { 289 Stream<Integer> out = f.apply(in); 290 return StreamSupport.stream(() -> out.spliterator(), OpTestCase.getStreamFlags(out)); 291 }). 292 exercise(); 293 294 withData(data). 295 stream((Stream<Integer> in) -> { 296 Stream<Integer> out = f.apply(in); 297 return StreamSupport.parallelStream(() -> out.spliterator(), OpTestCase.getStreamFlags(out)); 298 }). 299 exercise(); 300 } 301 } 302 303 @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class) 304 public void testSpliterators(String name, TestData.OfRef<Integer> data) { 305 for (Function<Stream<Integer>, Stream<Integer>> f : streamFunctions()) { 306 SpliteratorTestHelper.testSpliterator(() -> f.apply(data.stream()).spliterator()); 307 } 308 } 309 310 @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class) 311 public void testParSpliterators(String name, TestData.OfRef<Integer> data) { 312 for (Function<Stream<Integer>, Stream<Integer>> f : streamFunctions()) { 313 SpliteratorTestHelper.testSpliterator(() -> f.apply(data.parallelStream()).spliterator()); 314 } 315 } 316 317 private List<Function<Stream<Integer>, Stream<Integer>>> streamFunctions; 318 319 List<Function<Stream<Integer>, Stream<Integer>>> streamFunctions() { 320 if (streamFunctions == null) { 321 List<Function<Stream<Integer>, Stream<Integer>>> opFunctions = Arrays.asList( 322 s -> s.filter(pEven), 323 s -> s.map(mDoubler), 324 // @@@ Add distinct once asserting results with or without order 325 // is correctly supported 326 // s -> s.distinct(), 327 s -> s.sorted()); 328 329 streamFunctions = permuteStreamFunctions(opFunctions); 330 } 331 332 return streamFunctions; 333 } 334 335 // 336 337 public void testIntSplitting() { 338 List<Consumer<IntStream>> terminalOps = Arrays.asList( 339 s -> s.toArray(), 340 s -> s.forEach(e -> {}), 341 s -> s.reduce(Integer::sum) 342 ); 343 344 List<UnaryOperator<IntStream>> intermediateOps = Arrays.asList( 345 s -> s.parallel(), 346 // The following ensures the wrapping spliterator is tested 347 s -> s.map(i -> i).parallel() 348 ); 349 350 for (Consumer<IntStream> terminalOp : terminalOps) { 351 for (UnaryOperator<IntStream> intermediateOp : intermediateOps) { 352 for (boolean proxyEstimateSize : new boolean[]{false, true}) { 353 // Size is assumed to be larger than the target size for no splitting 354 // @@@ Need way to obtain the target size 355 Spliterator.OfInt sp = intermediateOp.apply(IntStream.range(0, 1000)).spliterator(); 356 ProxyNoExactSizeSpliterator.OfInt psp = new ProxyNoExactSizeSpliterator.OfInt(sp, proxyEstimateSize); 357 IntStream s = StreamSupport.intParallelStream(psp); 358 terminalOp.accept(s); 359 Assert.assertTrue(psp.splits > 0, 360 String.format("Number of splits should be greater that zero when proxyEstimateSize is %s", 361 proxyEstimateSize)); 362 Assert.assertTrue(psp.prefixSplits > 0, 363 String.format("Number of non-null prefix splits should be greater that zero when proxyEstimateSize is %s", 364 proxyEstimateSize)); 365 Assert.assertTrue(psp.sizeOnTraversal < 1000, 366 String.format("Size on traversal of last split should be less than the size of the list, %d, when proxyEstimateSize is %s", 367 1000, proxyEstimateSize)); 368 } 369 } 370 } 371 } 372 373 @Test(dataProvider = "IntStreamTestData", 374 dataProviderClass = IntStreamTestDataProvider.class, 375 groups = { "serialization-hostile" }) 376 public void testIntStreamSpliterators(String name, TestData.OfInt data) { 377 for (Function<IntStream, IntStream> f : intStreamFunctions()) { 378 withData(data). 379 stream(in -> { 380 IntStream out = f.apply(in); 381 return StreamSupport.intStream(() -> out.spliterator(), OpTestCase.getStreamFlags(out)); 382 }). 383 exercise(); 384 385 withData(data). 386 stream((in) -> { 387 IntStream out = f.apply(in); 388 return StreamSupport.intParallelStream(() -> out.spliterator(), OpTestCase.getStreamFlags(out)); 389 }). 390 exercise(); 391 } 392 } 393 394 @Test(dataProvider = "IntStreamTestData", dataProviderClass = IntStreamTestDataProvider.class) 395 public void testIntSpliterators(String name, TestData.OfInt data) { 396 for (Function<IntStream, IntStream> f : intStreamFunctions()) { 397 SpliteratorTestHelper.testIntSpliterator(() -> f.apply(data.stream()).spliterator()); 398 } 399 } 400 401 @Test(dataProvider = "IntStreamTestData", dataProviderClass = IntStreamTestDataProvider.class) 402 public void testIntParSpliterators(String name, TestData.OfInt data) { 403 for (Function<IntStream, IntStream> f : intStreamFunctions()) { 404 SpliteratorTestHelper.testIntSpliterator(() -> f.apply(data.parallelStream()).spliterator()); 405 } 406 } 407 408 private List<Function<IntStream, IntStream>> intStreamFunctions; 409 410 List<Function<IntStream, IntStream>> intStreamFunctions() { 411 if (intStreamFunctions == null) { 412 List<Function<IntStream, IntStream>> opFunctions = Arrays.asList( 413 s -> s.filter(ipEven), 414 s -> s.map(irDoubler), 415 s -> s.sorted()); 416 417 intStreamFunctions = permuteStreamFunctions(opFunctions); 418 } 419 420 return intStreamFunctions; 421 } 422 423 // 424 425 public void testLongSplitting() { 426 List<Consumer<LongStream>> terminalOps = Arrays.asList( 427 s -> s.toArray(), 428 s -> s.forEach(e -> {}), 429 s -> s.reduce(Long::sum) 430 ); 431 432 List<UnaryOperator<LongStream>> intermediateOps = Arrays.asList( 433 s -> s.parallel(), 434 // The following ensures the wrapping spliterator is tested 435 s -> s.map(i -> i).parallel() 436 ); 437 438 for (Consumer<LongStream> terminalOp : terminalOps) { 439 for (UnaryOperator<LongStream> intermediateOp : intermediateOps) { 440 for (boolean proxyEstimateSize : new boolean[]{false, true}) { 441 // Size is assumed to be larger than the target size for no splitting 442 // @@@ Need way to obtain the target size 443 Spliterator.OfLong sp = intermediateOp.apply(LongStream.range(0, 1000)).spliterator(); 444 ProxyNoExactSizeSpliterator.OfLong psp = new ProxyNoExactSizeSpliterator.OfLong(sp, proxyEstimateSize); 445 LongStream s = StreamSupport.longParallelStream(psp); 446 terminalOp.accept(s); 447 Assert.assertTrue(psp.splits > 0, 448 String.format("Number of splits should be greater that zero when proxyEstimateSize is %s", 449 proxyEstimateSize)); 450 Assert.assertTrue(psp.prefixSplits > 0, 451 String.format("Number of non-null prefix splits should be greater that zero when proxyEstimateSize is %s", 452 proxyEstimateSize)); 453 Assert.assertTrue(psp.sizeOnTraversal < 1000, 454 String.format("Size on traversal of last split should be less than the size of the list, %d, when proxyEstimateSize is %s", 455 1000, proxyEstimateSize)); 456 } 457 } 458 } 459 } 460 461 @Test(dataProvider = "LongStreamTestData", 462 dataProviderClass = LongStreamTestDataProvider.class, 463 groups = { "serialization-hostile" }) 464 public void testLongStreamSpliterators(String name, TestData.OfLong data) { 465 for (Function<LongStream, LongStream> f : longStreamFunctions()) { 466 withData(data). 467 stream(in -> { 468 LongStream out = f.apply(in); 469 return StreamSupport.longStream(() -> out.spliterator(), OpTestCase.getStreamFlags(out)); 470 }). 471 exercise(); 472 473 withData(data). 474 stream((in) -> { 475 LongStream out = f.apply(in); 476 return StreamSupport.longParallelStream(() -> out.spliterator(), OpTestCase.getStreamFlags(out)); 477 }). 478 exercise(); 479 } 480 } 481 482 @Test(dataProvider = "LongStreamTestData", dataProviderClass = LongStreamTestDataProvider.class) 483 public void testLongSpliterators(String name, TestData.OfLong data) { 484 for (Function<LongStream, LongStream> f : longStreamFunctions()) { 485 SpliteratorTestHelper.testLongSpliterator(() -> f.apply(data.stream()).spliterator()); 486 } 487 } 488 489 @Test(dataProvider = "LongStreamTestData", dataProviderClass = LongStreamTestDataProvider.class) 490 public void testLongParSpliterators(String name, TestData.OfLong data) { 491 for (Function<LongStream, LongStream> f : longStreamFunctions()) { 492 SpliteratorTestHelper.testLongSpliterator(() -> f.apply(data.parallelStream()).spliterator()); 493 } 494 } 495 496 private List<Function<LongStream, LongStream>> longStreamFunctions; 497 498 List<Function<LongStream, LongStream>> longStreamFunctions() { 499 if (longStreamFunctions == null) { 500 List<Function<LongStream, LongStream>> opFunctions = Arrays.asList( 501 s -> s.filter(lpEven), 502 s -> s.map(x -> x * 2L), 503 s -> s.sorted()); 504 505 longStreamFunctions = permuteStreamFunctions(opFunctions); 506 } 507 508 return longStreamFunctions; 509 } 510 511 // 512 513 public void testDoubleSplitting() { 514 List<Consumer<DoubleStream>> terminalOps = Arrays.asList( 515 s -> s.toArray(), 516 s -> s.forEach(e -> {}), 517 s -> s.reduce(Double::sum) 518 ); 519 520 List<UnaryOperator<DoubleStream>> intermediateOps = Arrays.asList( 521 s -> s.parallel(), 522 // The following ensures the wrapping spliterator is tested 523 s -> s.map(i -> i).parallel() 524 ); 525 526 for (Consumer<DoubleStream> terminalOp : terminalOps) { 527 for (UnaryOperator<DoubleStream> intermediateOp : intermediateOps) { 528 for (boolean proxyEstimateSize : new boolean[]{false, true}) { 529 // Size is assumed to be larger than the target size for no splitting 530 // @@@ Need way to obtain the target size 531 Spliterator.OfDouble sp = intermediateOp.apply(DoubleStream.range(0, 1000)).spliterator(); 532 ProxyNoExactSizeSpliterator.OfDouble psp = new ProxyNoExactSizeSpliterator.OfDouble(sp, proxyEstimateSize); 533 DoubleStream s = StreamSupport.doubleParallelStream(psp); 534 terminalOp.accept(s); 535 Assert.assertTrue(psp.splits > 0, 536 String.format("Number of splits should be greater that zero when proxyEstimateSize is %s", 537 proxyEstimateSize)); 538 Assert.assertTrue(psp.prefixSplits > 0, 539 String.format("Number of non-null prefix splits should be greater that zero when proxyEstimateSize is %s", 540 proxyEstimateSize)); 541 Assert.assertTrue(psp.sizeOnTraversal < 1000, 542 String.format("Size on traversal of last split should be less than the size of the list, %d, when proxyEstimateSize is %s", 543 1000, proxyEstimateSize)); 544 } 545 } 546 } 547 } 548 549 @Test(dataProvider = "DoubleStreamTestData", 550 dataProviderClass = DoubleStreamTestDataProvider.class, 551 groups = { "serialization-hostile" }) 552 public void testDoubleStreamSpliterators(String name, TestData.OfDouble data) { 553 for (Function<DoubleStream, DoubleStream> f : doubleStreamFunctions()) { 554 withData(data). 555 stream(in -> { 556 DoubleStream out = f.apply(in); 557 return StreamSupport.doubleStream(() -> out.spliterator(), OpTestCase.getStreamFlags(out)); 558 }). 559 exercise(); 560 561 withData(data). 562 stream((in) -> { 563 DoubleStream out = f.apply(in); 564 return StreamSupport.doubleParallelStream(() -> out.spliterator(), OpTestCase.getStreamFlags(out)); 565 }). 566 exercise(); 567 } 568 } 569 570 @Test(dataProvider = "DoubleStreamTestData", dataProviderClass = DoubleStreamTestDataProvider.class) 571 public void testDoubleSpliterators(String name, TestData.OfDouble data) { 572 for (Function<DoubleStream, DoubleStream> f : doubleStreamFunctions()) { 573 SpliteratorTestHelper.testDoubleSpliterator(() -> f.apply(data.stream()).spliterator()); 574 } 575 } 576 577 @Test(dataProvider = "DoubleStreamTestData", dataProviderClass = DoubleStreamTestDataProvider.class) 578 public void testDoubleParSpliterators(String name, TestData.OfDouble data) { 579 for (Function<DoubleStream, DoubleStream> f : doubleStreamFunctions()) { 580 SpliteratorTestHelper.testDoubleSpliterator(() -> f.apply(data.parallelStream()).spliterator()); 581 } 582 } 583 584 private List<Function<DoubleStream, DoubleStream>> doubleStreamFunctions; 585 586 List<Function<DoubleStream, DoubleStream>> doubleStreamFunctions() { 587 if (doubleStreamFunctions == null) { 588 List<Function<DoubleStream, DoubleStream>> opFunctions = Arrays.asList( 589 s -> s.filter(dpEven), 590 s -> s.map(x -> x * 2.0), 591 s -> s.sorted()); 592 593 doubleStreamFunctions = permuteStreamFunctions(opFunctions); 594 } 595 596 return doubleStreamFunctions; 597 } 598 }