content: /*
   * Copyright 2015, 2024 StreamEx contributors
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package one.util.streamex.api;
  
  import one.util.streamex.DoubleStreamEx;
  import one.util.streamex.IntStreamEx;
  import one.util.streamex.LongStreamEx;
  import one.util.streamex.StreamEx;
  import org.junit.FixMethodOrder;
  import org.junit.Test;
  import org.junit.runners.MethodSorters;
  
  import java.nio.DoubleBuffer;
  import java.util.*;
  import java.util.PrimitiveIterator.OfDouble;
  import java.util.concurrent.atomic.AtomicInteger;
  import java.util.concurrent.atomic.AtomicReference;
  import java.util.function.*;
  import java.util.stream.DoubleStream;
  import java.util.stream.DoubleStream.Builder;
  import java.util.stream.LongStream;
  
  import static one.util.streamex.TestHelpers.checkSpliterator;
  import static one.util.streamex.TestHelpers.streamEx;
  import static org.junit.Assert.*;
  
  /**
   * @author Tagir Valeev
   */
  @FixMethodOrder(MethodSorters.NAME_ASCENDING)
  public class DoubleStreamExTest {
      @Test
      public void testCreate() {
          assertArrayEquals(new double[] {}, DoubleStreamEx.empty().toArray(), 0.0);
          // double check is intended
          assertArrayEquals(new double[] {}, DoubleStreamEx.empty().toArray(), 0.0);
          assertArrayEquals(new double[] { 1 }, DoubleStreamEx.of(1).toArray(), 0.0);
          assertArrayEquals(new double[] { 1 }, DoubleStreamEx.of(OptionalDouble.of(1)).toArray(), 0.0);
          assertArrayEquals(new double[] {}, DoubleStreamEx.of(OptionalDouble.empty()).toArray(), 0.0);
          assertArrayEquals(new double[] { 1, 2, 3 }, DoubleStreamEx.of(1, 2, 3).toArray(), 0.0);
          assertArrayEquals(new double[] { 4, 6 }, DoubleStreamEx.of(new double[] { 2, 4, 6, 8, 10 }, 1, 3).toArray(),
              0.0);
          assertArrayEquals(new double[] { 1, 2, 3 }, DoubleStreamEx.of(1.0f, 2.0f, 3.0f).toArray(), 0.0);
          assertArrayEquals(new double[] { 4, 6 }, DoubleStreamEx.of(new float[] { 2.0f, 4.0f, 6.0f, 8.0f, 10.0f }, 1, 3)
                  .toArray(), 0.0);
          assertArrayEquals(new double[] { 1, 2, 3 }, DoubleStreamEx.of(DoubleStream.of(1, 2, 3)).toArray(), 0.0);
          assertArrayEquals(new double[] { 1, 2, 3 }, DoubleStreamEx.of(Arrays.asList(1.0, 2.0, 3.0)).toArray(), 0.0);
          assertArrayEquals(new double[] { 1, 2, 4, 8, 16 }, DoubleStreamEx.iterate(1, x -> x * 2).limit(5).toArray(),
              0.0);
          assertArrayEquals(new double[] { 1, 1, 1, 1 }, DoubleStreamEx.generate(() -> 1).limit(4).toArray(), 0.0);
          assertArrayEquals(new double[] { 1, 1, 1, 1 }, DoubleStreamEx.constant(1.0, 4).toArray(), 0.0);
          assertEquals(10, DoubleStreamEx.of(new Random(), 10).count());
          assertArrayEquals(DoubleStreamEx.of(new Random(1), 10).toArray(), DoubleStreamEx.of(new Random(1)).limit(10)
                  .toArray(), 0.0);
          assertTrue(DoubleStreamEx.of(new Random(), 100, 1, 10).allMatch(x -> x >= 1 && x < 10));
          assertArrayEquals(DoubleStreamEx.of(new Random(1), 100, 1, 10).toArray(), DoubleStreamEx.of(new Random(1), 1,
              10).limit(100).toArray(), 0.0);
  
          DoubleStream stream = DoubleStreamEx.of(1, 2, 3);
          assertSame(stream, DoubleStreamEx.of(stream));
  
          assertArrayEquals(new double[] { 1, 5, 3 }, DoubleStreamEx.of(
              Spliterators.spliterator(new double[] { 1, 5, 3 }, 0)).toArray(), 0.0);
          assertArrayEquals(new double[] { 1, 5, 3 }, DoubleStreamEx.of(
              Spliterators.iterator(Spliterators.spliterator(new double[] { 1, 5, 3 }, 0))).toArray(), 0.0);
          assertArrayEquals(new double[0], DoubleStreamEx
                  .of(Spliterators.iterator(Spliterators.emptyDoubleSpliterator())).parallel().toArray(), 0.0);
  
          assertArrayEquals(new double[] { 2, 4, 6 }, DoubleStreamEx.of(new Double[] { 2.0, 4.0, 6.0 }).toArray(), 0.0);
      }
  
      @Test
      public void testOfDoubleBuffer() {
          double[] data = LongStreamEx.range(100).asDoubleStream().toArray();
          assertArrayEquals(data, DoubleStreamEx.of(DoubleBuffer.wrap(data)).toArray(), 0.0);
          assertArrayEquals(LongStreamEx.range(50, 70).asDoubleStream().toArray(), DoubleStreamEx.of(DoubleBuffer.wrap(
              data, 50, 20)).toArray(), 0.0);
          assertArrayEquals(data, DoubleStreamEx.of(DoubleBuffer.wrap(data)).parallel().toArray(), 0.0);
          assertArrayEquals(LongStreamEx.range(50, 70).asDoubleStream().toArray(), DoubleStreamEx.of(DoubleBuffer.wrap(
              data, 50, 20)).parallel().toArray(), 0.0);
      }
      
      @Test
      public void testIterate() {
          assertArrayEquals(new double[] { 1, 2, 4, 8, 16 }, DoubleStreamEx.iterate(1, x -> x * 2).limit(5).toArray(),
              0.0);
          assertArrayEquals(new double[] { 1, 2, 4, 8, 16, 32, 64 }, DoubleStreamEx.iterate(1, x -> x < 100, x -> x * 2)
                  .toArray(), 0.0);
          assertEquals(0, DoubleStreamEx.iterate(0, x -> x < 0, x -> 1 / x).count());
          assertFalse(DoubleStreamEx.iterate(1, x -> x < 100, x -> x * 2).findFirst(x -> x == 10).isPresent());
          checkSpliterator("iterate", () -> DoubleStreamEx.iterate(1, x -> x < 100, x -> x * 2).spliterator());
      }
  
      @Test
      public void testBasics() {
          assertFalse(DoubleStreamEx.of(1).isParallel());
          assertTrue(DoubleStreamEx.of(1).parallel().isParallel());
          assertFalse(DoubleStreamEx.of(1).parallel().sequential().isParallel());
          AtomicInteger i = new AtomicInteger();
          try (DoubleStreamEx s = DoubleStreamEx.of(1).onClose(i::incrementAndGet)) {
              assertEquals(1, s.count());
          }
          assertEquals(1, i.get());
          assertEquals(6, IntStreamEx.range(0, 4).asDoubleStream().sum(), 0);
          assertEquals(3, IntStreamEx.range(0, 4).asDoubleStream().max().getAsDouble(), 0);
          assertEquals(0, IntStreamEx.range(0, 4).asDoubleStream().min().getAsDouble(), 0);
          assertEquals(1.5, IntStreamEx.range(0, 4).asDoubleStream().average().getAsDouble(), 0.000001);
          assertEquals(4, IntStreamEx.range(0, 4).asDoubleStream().summaryStatistics().getCount());
          assertArrayEquals(new double[] { 1, 2, 3 },
              IntStreamEx.range(0, 5).asDoubleStream().skip(1).limit(3).toArray(), 0.0);
          assertArrayEquals(new double[] { 1, 2, 3 }, DoubleStreamEx.of(3, 1, 2).sorted().toArray(), 0.0);
          assertArrayEquals(new double[] { 1, 2, 3 }, DoubleStreamEx.of(1, 2, 1, 3, 2).distinct().toArray(), 0.0);
          assertArrayEquals(new int[] { 2, 4, 6 }, IntStreamEx.range(1, 4).asDoubleStream().mapToInt(x -> (int) x * 2)
                  .toArray());
          assertArrayEquals(new long[] { 2, 4, 6 }, IntStreamEx.range(1, 4).asDoubleStream().mapToLong(x -> (long) x * 2)
                  .toArray());
          assertArrayEquals(new double[] { 2, 4, 6 }, IntStreamEx.range(1, 4).asDoubleStream().map(x -> x * 2).toArray(),
              0.0);
          assertArrayEquals(new double[] { 1, 3 }, IntStreamEx.range(0, 5).asDoubleStream().filter(x -> x % 2 == 1)
                  .toArray(), 0.0);
          assertEquals(6.0, DoubleStreamEx.of(1.0, 2.0, 3.0).reduce(Double::sum).getAsDouble(), 0.0);
          assertEquals(Long.MAX_VALUE, LongStreamEx.rangeClosed(1, Long.MAX_VALUE).asDoubleStream().spliterator()
                  .getExactSizeIfKnown());
  
          assertArrayEquals(new double[] { 4, 2, 0, -2, -4 }, DoubleStreamEx.zip(new double[] { 5, 4, 3, 2, 1 },
              new double[] { 1, 2, 3, 4, 5 }, (a, b) -> a - b).toArray(), 0.0);
          assertEquals("1.0; 0.5; 0.25; 0.125", DoubleStreamEx.of(1.0, 0.5, 0.25, 0.125).mapToObj(String::valueOf)
                  .joining("; "));
          List<Double> list = new ArrayList<>();
          DoubleStreamEx.of(1.0, 0.5, 0.25, 0.125).forEach(list::add);
          assertEquals(Arrays.asList(1.0, 0.5, 0.25, 0.125), list);
          list = new ArrayList<>();
          DoubleStreamEx.of(1.0, 0.5, 0.25, 0.125).parallel().forEachOrdered(list::add);
          assertEquals(Arrays.asList(1.0, 0.5, 0.25, 0.125), list);
  
          assertFalse(DoubleStreamEx.of(1.0, 2.0, 2.5).anyMatch(x -> x < 0.0));
          assertTrue(DoubleStreamEx.of(1.0, 2.0, 2.5).anyMatch(x -> x >= 2.5));
          assertTrue(DoubleStreamEx.of(1.0, 2.0, 2.5).noneMatch(x -> x < 0.0));
          assertFalse(DoubleStreamEx.of(1.0, 2.0, 2.5).noneMatch(x -> x >= 2.5));
          assertEquals(5.0, DoubleStreamEx.of(1.0, 2.0, 2.5).reduce(1, (a, b) -> a * b), 0.0);
  
          assertTrue(DoubleStreamEx.of(1, 2, 3).spliterator().hasCharacteristics(Spliterator.ORDERED));
          assertFalse(DoubleStreamEx.of(1, 2, 3).unordered().spliterator().hasCharacteristics(Spliterator.ORDERED));
  
          OfDouble iterator = DoubleStreamEx.of(1.0, 2.0, 3.0).iterator();
          assertEquals(1.0, iterator.next(), 0.0);
          assertEquals(2.0, iterator.next(), 0.0);
          assertEquals(3.0, iterator.next(), 0.0);
          assertFalse(iterator.hasNext());
      }
  
      @Test
      public void testForEach() {
          streamEx(() -> StreamEx.of(1, 2, 3), s -> {
              AtomicInteger count = new AtomicInteger(0);
              s.get().mapToDouble(Integer::intValue).forEach(v -> count.addAndGet((int) v));
              assertEquals(6, count.get());
              s.get().mapToDouble(Integer::intValue).pairMap((a, b) -> b - a).forEach(v -> count.addAndGet((int) v));
              assertEquals(8, count.get());
          });
      }
  
      @Test
      public void testFlatMap() {
          assertArrayEquals(new int[] { 1, 5, 2, 3, 3, 2, 0, 9 }, DoubleStreamEx.of(1.5, 2.3, 3.2, 0.9).flatMapToInt(
              x -> IntStreamEx.of((int) Math.floor(x), (int) (Math.round(10 * (x - Math.floor(x)))))).toArray());
          assertEquals("1:.:5:2:2:.:3:3:.:2:0:.:9", DoubleStreamEx.of(1.5, 22.3, 3.2, 0.9).flatMapToObj(
              x -> StreamEx.split(String.valueOf(x), "")).joining(":"));
  
          assertArrayEquals(new double[] { 0.0, 0.0, 1.0, 0.0, 1.0, 2.0 }, DoubleStreamEx.of(1, 2, 3).flatMap(
              x -> IntStreamEx.range((int) x).asDoubleStream()).toArray(), 0.0);
  
          assertArrayEquals(new long[] { 0x3FF0000000000000L, 0x3FF0000000000000L, 0x4000000000000000L,
                  0x4000000000000000L, 0x7FF8000000000000L, 0x7FF8000000000000L }, DoubleStreamEx.of(1, 2, Double.NaN)
                  .flatMapToLong(x -> LongStream.of(Double.doubleToLongBits(x), Double.doubleToRawLongBits(x))).toArray());
      }
  
      @Test
      public void testPrepend() {
          assertArrayEquals(new double[] { -1, 0, 1, 2, 3 }, DoubleStreamEx.of(1, 2, 3).prepend(-1, 0).toArray(), 0.0);
          assertArrayEquals(new double[] { -1, 0, 1, 2, 3 }, DoubleStreamEx.of(1, 2, 3).prepend(DoubleStream.of(-1, 0))
                  .toArray(), 0.0);
          DoubleStreamEx s = DoubleStreamEx.of(1, 2, 3);
          assertSame(s, s.prepend());
      }
  
      @Test
      public void testAppend() {
          assertArrayEquals(new double[] { 1, 2, 3, 4, 5 }, DoubleStreamEx.of(1, 2, 3).append(4, 5).toArray(), 0.0);
          assertArrayEquals(new double[] { 1, 2, 3, 4, 5 }, DoubleStreamEx.of(1, 2, 3).append(DoubleStream.of(4, 5))
                  .toArray(), 0.0);
          DoubleStreamEx s = DoubleStreamEx.of(1, 2, 3);
          assertSame(s, s.append());
      }
  
      @Test
      public void testRanges() {
          assertArrayEquals(new double[] { 5, 4, Double.POSITIVE_INFINITY }, DoubleStreamEx.of(1, 5, 3, 4, -1,
              Double.POSITIVE_INFINITY).greater(3).toArray(), 0.0);
          assertArrayEquals(new double[] {}, DoubleStreamEx.of(1, 5, 3, 4, -1, Double.POSITIVE_INFINITY).greater(
              Double.POSITIVE_INFINITY).toArray(), 0.0);
          assertArrayEquals(new double[] { 5, 3, 4, Double.POSITIVE_INFINITY }, DoubleStreamEx.of(1, 5, 3, 4, -1,
              Double.POSITIVE_INFINITY).atLeast(3).toArray(), 0.0);
          assertArrayEquals(new double[] { Double.POSITIVE_INFINITY }, DoubleStreamEx.of(1, 5, 3, 4, -1,
              Double.POSITIVE_INFINITY).atLeast(Double.POSITIVE_INFINITY).toArray(), 0.0);
          assertArrayEquals(new double[] { 1, -1 }, DoubleStreamEx.of(1, 5, 3, 4, -1, Double.POSITIVE_INFINITY).less(3)
                  .toArray(), 0.0);
          assertArrayEquals(new double[] { 1, 3, -1 }, DoubleStreamEx.of(1, 5, 3, 4, -1, Double.POSITIVE_INFINITY)
                  .atMost(3).toArray(), 0.0);
          assertArrayEquals(new double[] { 1, 3, 4, -1 }, DoubleStreamEx.of(1, 5, 3, 4, -1, Double.POSITIVE_INFINITY)
                  .atMost(4).toArray(), 0.0);
      }
  
      @Test
      public void testFind() {
          assertEquals(6.0, LongStreamEx.range(1, 10).asDoubleStream().findFirst(i -> i > 5).getAsDouble(), 0.0);
          assertFalse(LongStreamEx.range(1, 10).asDoubleStream().findAny(i -> i > 10).isPresent());
      }
  
      @Test
      public void testRemove() {
          assertArrayEquals(new double[] { 1, 2 }, DoubleStreamEx.of(1, 2, 3).remove(x -> x > 2).toArray(), 0.0);
      }
  
      @Test
      public void testSort() {
          assertArrayEquals(new double[] { 3, 2, 1 }, DoubleStreamEx.of(1, 2, 3).sortedByDouble(x -> -x).toArray(), 0.0);
          assertArrayEquals(new double[] { Double.NaN, Double.NaN, Double.POSITIVE_INFINITY, Double.MAX_VALUE, 1000, 1,
                  Double.MIN_VALUE, 0, -0.0, -Double.MIN_VALUE * 3, -10, -Double.MAX_VALUE / 1.1, -Double.MAX_VALUE,
                  Double.NEGATIVE_INFINITY }, DoubleStreamEx.of(0, 1, -Double.MIN_VALUE * 3, 1000, -10,
              -Double.MAX_VALUE, Double.POSITIVE_INFINITY, Double.NaN, Double.NEGATIVE_INFINITY, Double.NaN,
              Double.MAX_VALUE, -0.0, Double.MIN_VALUE, -Double.MAX_VALUE / 1.1).reverseSorted().toArray(), 0.0);
          assertArrayEquals(new double[] { 1, 10, 2, 21, 9 }, DoubleStreamEx.of(1, 10, 2, 9, 21)
                  .sortedBy(String::valueOf).toArray(), 0.0);
  
          assertArrayEquals(new double[] { 0.4, 1.5, 1.3, 2.3, 2.1, 2.0, 3.7 }, DoubleStreamEx.of(1.5, 2.3, 1.3, 2.1,
              3.7, 0.4, 2.0).sortedByInt(x -> (int) x).toArray(), 0.0);
  
          assertArrayEquals(new double[] { 0.4, 1.5, 1.3, 2.3, 2.1, 2.0, 3.7 }, DoubleStreamEx.of(1.5, 2.3, 1.3, 2.1,
              3.7, 0.4, 2.0).sortedByLong(x -> (long) x).toArray(), 0.0);
          double nonCanonicalNan = Double.longBitsToDouble(0xfff8000000000001L);
          double nonCanonicalNan2 = Double.longBitsToDouble(0x7ff8000000000000L);
          assertTrue(Double.isNaN(nonCanonicalNan));
          assertTrue(Double.isNaN(nonCanonicalNan2));
          double[] data = {Double.NEGATIVE_INFINITY, 0.0, 1.0, Double.POSITIVE_INFINITY, Double.NaN, nonCanonicalNan,
              nonCanonicalNan2};
          double[] reverseData = {nonCanonicalNan, nonCanonicalNan2, Double.NaN, Double.POSITIVE_INFINITY, 1.0, 0.0,
              Double.NEGATIVE_INFINITY};
          assertArrayEquals(DoubleStreamEx.of(data).mapToLong(Double::doubleToRawLongBits).toArray(),
              DoubleStreamEx.of(data).sorted().mapToLong(Double::doubleToRawLongBits).toArray());
          assertArrayEquals(DoubleStreamEx.of(reverseData).mapToLong(Double::doubleToRawLongBits).toArray(),
              DoubleStreamEx.of(data).reverseSorted().mapToLong(Double::doubleToRawLongBits).toArray());
      }
  
      @SafeVarargs
      private static void checkEmpty(Function<DoubleStreamEx, OptionalDouble>... fns) {
          int i = 0;
          for (Function<DoubleStreamEx, OptionalDouble> fn : fns) {
              assertFalse("#" + i, fn.apply(DoubleStreamEx.empty()).isPresent());
              assertFalse("#" + i, fn.apply(DoubleStreamEx.of(1, 2, 3, 4).greater(5).parallel()).isPresent());
              assertEquals("#" + i, 10, fn.apply(DoubleStreamEx.of(1, 1, 1, 1, 10, 10, 10, 10).greater(5).parallel())
                      .getAsDouble(), 0.0);
              i++;
          }
      }
  
      @Test
      public void testMinMax() {
          checkEmpty(s -> s.maxBy(Double::valueOf), s -> s.maxByInt(x -> (int) x), s -> s.maxByLong(x -> (long) x),
              s -> s.maxByDouble(x -> x), s -> s.minBy(Double::valueOf), s -> s.minByInt(x -> (int) x), s -> s
                      .minByLong(x -> (long) x), s -> s.minByDouble(x -> x));
          assertEquals(9, IntStreamEx.range(5, 12).asDoubleStream().max(
                  Comparator.comparing(String::valueOf)).getAsDouble(), 0.0);
          assertEquals(10, IntStreamEx.range(5, 12).asDoubleStream().min(
                  Comparator.comparing(String::valueOf)).getAsDouble(), 0.0);
          assertEquals(9, IntStreamEx.range(5, 12).asDoubleStream().maxBy(String::valueOf).getAsDouble(), 0.0);
          assertEquals(10, IntStreamEx.range(5, 12).asDoubleStream().minBy(String::valueOf).getAsDouble(), 0.0);
          assertEquals(5, IntStreamEx.range(5, 12).asDoubleStream().maxByDouble(x -> 1.0 / x).getAsDouble(), 0.0);
          assertEquals(11, IntStreamEx.range(5, 12).asDoubleStream().minByDouble(x -> 1.0 / x).getAsDouble(), 0.0);
          assertEquals(29.0, DoubleStreamEx.of(15, 8, 31, 47, 19, 29).maxByInt(x -> (int) (x % 10 * 10 + x / 10))
                  .getAsDouble(), 0.0);
          assertEquals(31.0, DoubleStreamEx.of(15, 8, 31, 47, 19, 29).minByInt(x -> (int) (x % 10 * 10 + x / 10))
                  .getAsDouble(), 0.0);
          assertEquals(29.0, DoubleStreamEx.of(15, 8, 31, 47, 19, 29).maxByLong(x -> (long) (x % 10 * 10 + x / 10))
                  .getAsDouble(), 0.0);
          assertEquals(31.0, DoubleStreamEx.of(15, 8, 31, 47, 19, 29).minByLong(x -> (long) (x % 10 * 10 + x / 10))
                  .getAsDouble(), 0.0);
  
          Supplier<DoubleStreamEx> s = () -> DoubleStreamEx.of(1, 50, 120, 35, 130, 12, 0);
          DoubleToIntFunction intKey = x -> String.valueOf(x).length();
          DoubleToLongFunction longKey = x -> String.valueOf(x).length();
          DoubleUnaryOperator doubleKey = x -> String.valueOf(x).length();
          DoubleFunction<Integer> objKey = x -> String.valueOf(x).length();
          List<Function<DoubleStreamEx, OptionalDouble>> minFns = Arrays.asList(is -> is.minByInt(intKey), is -> is
                  .minByLong(longKey), is -> is.minByDouble(doubleKey), is -> is.minBy(objKey));
          List<Function<DoubleStreamEx, OptionalDouble>> maxFns = Arrays.asList(is -> is.maxByInt(intKey), is -> is
                  .maxByLong(longKey), is -> is.maxByDouble(doubleKey), is -> is.maxBy(objKey));
          minFns.forEach(fn -> assertEquals(1, fn.apply(s.get()).getAsDouble(), 0.0));
          minFns.forEach(fn -> assertEquals(1, fn.apply(s.get().parallel()).getAsDouble(), 0.0));
          maxFns.forEach(fn -> assertEquals(120, fn.apply(s.get()).getAsDouble(), 0.0));
          maxFns.forEach(fn -> assertEquals(120, fn.apply(s.get().parallel()).getAsDouble(), 0.0));
      }
  
      @Test
      public void testPairMap() {
          assertEquals(0, DoubleStreamEx.of().pairMap(Double::sum).count());
          assertEquals(0, DoubleStreamEx.of(1.0).pairMap(Double::sum).count());
          int[] data = new Random(1).ints(1000, 1, 1000).toArray();
          double[] expected = new double[data.length - 1];
          for (int i = 0; i < expected.length; i++)
              expected[i] = (data[i + 1] - data[i]) * 1.23;
          double[] result = IntStreamEx.of(data).parallel().asDoubleStream().pairMap((a, b) -> (b - a) * 1.23).toArray();
          assertArrayEquals(expected, result, 0.0);
          result = IntStreamEx.of(data).asDoubleStream().pairMap((a, b) -> (b - a) * 1.23).toArray();
          assertArrayEquals(expected, result, 0.0);
          assertEquals(984.0, IntStreamEx.of(data).asDoubleStream().parallel().pairMap((a, b) -> Math.abs(a - b)).max()
                  .getAsDouble(), 0.0);
          assertArrayEquals(new double[] { 1.0, 1.0 }, DoubleStreamEx.of(1.0, 2.0, 3.0).append().parallel().pairMap(
              (a, b) -> b - a).toArray(), 0.0);
  
          assertArrayEquals(LongStreamEx.range(1, 100).asDoubleStream().toArray(), LongStreamEx.range(100).map(
              i -> i * (i + 1) / 2).append(LongStream.empty()).asDoubleStream().parallel().pairMap((a, b) -> b - a)
                  .toArray(), 0.0);
          assertArrayEquals(LongStreamEx.range(1, 100).asDoubleStream().toArray(), LongStreamEx.range(100).map(
              i -> i * (i + 1) / 2).prepend(LongStream.empty()).asDoubleStream().parallel().pairMap((a, b) -> b - a)
                  .toArray(), 0.0);
  
          assertEquals(1, LongStreamEx.range(1000).mapToDouble(x -> x * x).pairMap((a, b) -> b - a).pairMap(
              (a, b) -> b - a).distinct().count());
  
          assertFalse(LongStreamEx.range(1000).asDoubleStream().greater(2000).parallel().pairMap((a, b) -> a).findFirst()
                  .isPresent());
      }
  
      @Test
      public void testToFloatArray() {
          float[] expected = new float[10000];
          for (int i = 0; i < expected.length; i++)
              expected[i] = i;
          assertArrayEquals(expected, IntStreamEx.range(0, 10000).asDoubleStream().toFloatArray(), 0.0f);
          assertArrayEquals(expected, IntStreamEx.range(0, 10000).asDoubleStream().parallel().toFloatArray(), 0.0f);
          assertArrayEquals(expected, IntStreamEx.range(0, 10000).asDoubleStream().greater(-1).toFloatArray(), 0.0f);
          assertArrayEquals(expected, IntStreamEx.range(0, 10000).asDoubleStream().parallel().greater(-1).toFloatArray(),
              0.0f);
          // Test when resize of internal buffer is not required on addAll (buffer size = 128)
          assertArrayEquals(Arrays.copyOf(expected, 100), 
              IntStreamEx.range(0, 100).asDoubleStream().parallel().toFloatArray(), 0.0f);
      }
  
      @Test
      public void testJoining() {
          assertEquals("0.4,5.0,3.6,4.8", DoubleStreamEx.of(0.4, 5.0, 3.6, 4.8).joining(","));
          assertEquals("0.4,5.0,3.6,4.8", DoubleStreamEx.of(0.4, 5.0, 3.6, 4.8).parallel().joining(","));
          assertEquals("[0.4,5.0,3.6,4.8]", DoubleStreamEx.of(0.4, 5.0, 3.6, 4.8).joining(",", "[", "]"));
          assertEquals("[0.4,5.0,3.6,4.8]", DoubleStreamEx.of(0.4, 5.0, 3.6, 4.8).parallel().joining(",", "[", "]"));
      }
  
      @Test
      public void testMapToEntry() {
          Map<Integer, List<Double>> map = DoubleStreamEx.of(0.3, 0.5, 1.3, 0.7, 1.9, 2.1).mapToEntry(x -> (int) x,
              x -> x).grouping();
          assertEquals(Arrays.asList(0.3, 0.5, 0.7), map.get(0));
          assertEquals(Arrays.asList(1.3, 1.9), map.get(1));
          assertEquals(Arrays.asList(2.1), map.get(2));
      }
  
      @Test
      public void testTakeWhile() {
          assertArrayEquals(LongStreamEx.range(100).asDoubleStream().toArray(), DoubleStreamEx.iterate(0, i -> i + 1)
                  .takeWhile(i -> i < 100).toArray(), 0.0);
          assertEquals(0, DoubleStreamEx.iterate(0, i -> i + 1).takeWhile(i -> i < 0).count());
          assertEquals(1, DoubleStreamEx.of(1, 3, 2).takeWhile(i -> i < 3).count());
          assertEquals(3, DoubleStreamEx.of(1, 2, 3).takeWhile(i -> i < 100).count());
      }
  
      @Test
      public void testTakeWhileInclusive() {
          assertArrayEquals(LongStreamEx.range(101).asDoubleStream().toArray(), DoubleStreamEx.iterate(0, i -> i + 1)
              .takeWhileInclusive(i -> i < 100).toArray(), 0.0);
          assertEquals(1, DoubleStreamEx.iterate(0, i -> i + 1).takeWhileInclusive(i -> i < 0).count());
          assertEquals(2, DoubleStreamEx.of(1, 3, 2).takeWhileInclusive(i -> i < 3).count());
          assertEquals(3, DoubleStreamEx.of(1, 2, 3).takeWhileInclusive(i -> i < 100).count());
      }
      
      @Test
      public void testDropWhile() {
          assertArrayEquals(new double[] { 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }, LongStreamEx.range(100).asDoubleStream()
                  .dropWhile(i -> i % 10 < 5).limit(10).toArray(), 0.0);
          assertEquals(100, LongStreamEx.range(100).asDoubleStream().sorted().dropWhile(i -> i % 10 < 0).count());
          assertEquals(0, LongStreamEx.range(100).asDoubleStream().dropWhile(i -> i % 10 < 10).count());
          assertEquals(OptionalDouble.of(0), LongStreamEx.range(100).asDoubleStream().dropWhile(i -> i % 10 < 0).findFirst());
          assertEquals(OptionalDouble.empty(), LongStreamEx.range(100).asDoubleStream().dropWhile(i -> i % 10 < 10).findFirst());
  
          java.util.Spliterator.OfDouble spltr = LongStreamEx.range(100).asDoubleStream().dropWhile(i -> i % 10 < 1).spliterator();
          assertTrue(spltr.tryAdvance((double x) -> assertEquals(1, x, 0.0)));
          Builder builder = DoubleStream.builder();
          spltr.forEachRemaining(builder);
          assertArrayEquals(LongStreamEx.range(2, 100).asDoubleStream().toArray(), builder.build().toArray(), 0.0);
      }
  
      @Test
      public void testIndexOf() {
          assertEquals(11, LongStreamEx.range(50, 100).asDoubleStream().indexOf(x -> x > 60).getAsLong());
          assertFalse(LongStreamEx.range(50, 100).asDoubleStream().indexOf(x -> x < 0).isPresent());
          assertEquals(11, LongStreamEx.range(50, 100).asDoubleStream().parallel().indexOf(x -> x > 60).getAsLong());
          assertFalse(LongStreamEx.range(50, 100).asDoubleStream().parallel().indexOf(x -> x < 0).isPresent());
      }
  
      @Test
      public void testFoldLeft() {
          // non-associative
          DoubleBinaryOperator accumulator = (x, y) -> (x + y) * (x + y);
          assertEquals(2322576, DoubleStreamEx.constant(3, 4).foldLeft(accumulator).orElse(-1), 0.0);
          assertEquals(2322576, DoubleStreamEx.constant(3, 4).parallel().foldLeft(accumulator).orElse(-1), 0.0);
          assertFalse(DoubleStreamEx.empty().foldLeft(accumulator).isPresent());
          assertEquals(144, DoubleStreamEx.of(1, 2, 3).foldLeft(0.0, accumulator), 144);
          assertEquals(144, DoubleStreamEx.of(1, 2, 3).parallel().foldLeft(0.0, accumulator), 144);
      }
  
      @Test
      public void testMapFirstLast() {
          assertArrayEquals(new double[] { -1, 2, 3, 4, 7 }, DoubleStreamEx.of(1, 2, 3, 4, 5).mapFirst(x -> x - 2.0)
                  .mapLast(x -> x + 2.0).toArray(), 0.0);
      }
  
      @Test
      public void testPeekFirst() {
          double[] input = {1, 10, 100, 1000};
          
          AtomicReference<Double> firstElement = new AtomicReference<>();
          assertArrayEquals(new double[] {10, 100, 1000}, DoubleStreamEx.of(input).peekFirst(firstElement::set).skip(1).toArray(), 0.0);
          assertEquals(1, firstElement.get(), 0.0);
  
          assertArrayEquals(new double[] {10, 100, 1000}, DoubleStreamEx.of(input).skip(1).peekFirst(firstElement::set).toArray(), 0.0);
          assertEquals(10, firstElement.get(), 0.0);
          
          firstElement.set(-1.0);
          assertArrayEquals(new double[] {}, DoubleStreamEx.of(input).skip(4).peekFirst(firstElement::set).toArray(), 0.0);
          assertEquals(-1, firstElement.get(), 0.0);
      }
      
      @Test
      public void testPeekLast() {
          double[] input = {1, 10, 100, 1000};
          AtomicReference<Double> lastElement = new AtomicReference<>();
          assertArrayEquals(new double[] {1, 10, 100}, DoubleStreamEx.of(input).peekLast(lastElement::set).limit(3).toArray(), 0.0);
          assertNull(lastElement.get());
  
          assertArrayEquals(new double[] { 1, 10, 100 }, DoubleStreamEx.of(input).less(1000).peekLast(lastElement::set)
                  .limit(3).toArray(), 0.0);
          assertEquals(100, lastElement.get(), 0.0);
          
          assertArrayEquals(input, DoubleStreamEx.of(input).peekLast(lastElement::set).limit(4).toArray(), 0.0);
          assertEquals(1000, lastElement.get(), 0.0);
          
          assertArrayEquals(new double[] {1, 10, 100}, DoubleStreamEx.of(input).limit(3).peekLast(lastElement::set).toArray(), 0.0);
          assertEquals(100, lastElement.get(), 0.0);
      }
      
      @Test
      public void testScanLeft() {
          assertArrayEquals(new double[] { 0, 1, 3, 6, 10, 15, 21, 28, 36, 45 }, LongStreamEx.range(10).asDoubleStream()
                  .scanLeft(Double::sum), 0.0);
          assertArrayEquals(new double[] { 0, 1, 3, 6, 10, 15, 21, 28, 36, 45 }, LongStreamEx.range(10).asDoubleStream()
                  .parallel().scanLeft(Double::sum), 0.0);
          assertArrayEquals(new double[] { 0, 1, 3, 6, 10, 15, 21, 28, 36, 45 }, LongStreamEx.range(10).asDoubleStream()
                  .filter(x -> true).scanLeft(Double::sum), 0.0);
          assertArrayEquals(new double[] { 0, 1, 3, 6, 10, 15, 21, 28, 36, 45 }, LongStreamEx.range(10).asDoubleStream()
                  .filter(x -> true).parallel().scanLeft(Double::sum), 0.0);
          assertArrayEquals(new double[] { 1, 1, 2, 6, 24, 120 }, LongStreamEx.rangeClosed(1, 5).asDoubleStream()
                  .scanLeft(1, (a, b) -> a * b), 0.0);
          assertArrayEquals(new double[] { 1, 1, 2, 6, 24, 120 }, LongStreamEx.rangeClosed(1, 5).asDoubleStream()
                  .parallel().scanLeft(1, (a, b) -> a * b), 0.0);
      }
  
      // Reads numbers from scanner stopping when non-number is encountered
      // leaving scanner in known state
      private static DoubleStreamEx scannerDoubles(Scanner sc) {
          return DoubleStreamEx.produce(action -> {
              if (sc.hasNextDouble())
                  action.accept(sc.nextDouble());
              return sc.hasNextDouble();
          });
      }
  
      @Test
      public void testProduce() {
          Scanner sc = new Scanner("1.0 2.5 3 -4.6 test");
          sc.useLocale(Locale.ENGLISH);
          assertArrayEquals(new double[] {1, 2.5, 3, -4.6}, scannerDoubles(sc).toArray(), 0.0);
          assertEquals("test", sc.next());
      }
  
      @Test
      public void testPrefix() {
          assertArrayEquals(new double[] { 1, 3, 6, 10, 20 }, DoubleStreamEx.of(1, 2, 3, 4, 10).prefix(Double::sum).toArray(), 0.0);
          assertEquals(OptionalDouble.of(10), DoubleStreamEx.of(1, 2, 3, 4, 10).prefix(Double::sum).findFirst(x -> x > 7));
          assertEquals(OptionalDouble.empty(), DoubleStreamEx.of(1, 2, 3, 4, 10).prefix(Double::sum).findFirst(x -> x > 20));
      }
      
      @Test
      public void testIntersperse() {
          assertArrayEquals(new double[] { 1, 0, 10, 0, 100, 0, 1000 }, DoubleStreamEx.of(1, 10, 100, 1000).intersperse(0)
                  .toArray(), 0.0);
          assertEquals(0L, IntStreamEx.empty().intersperse(1).count());
      }
  
      @Test
      public void testCount() {
          assertEquals(5L, DoubleStreamEx.of(0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4).count(x -> x == (int) x));
      }
  }
  