package com.pturpin.quickcheck.generator;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;

import java.util.Collection;
import java.util.function.Function;
import java.util.function.Predicate;

import static com.google.common.base.Preconditions.checkArgument;

public class Generators {

  public static <T> Generator<T> constGen(T value) {
    return re -> value;
  }

  public static <T> Generator<T> oneOf(Collection<? extends T> objects) {
    checkArgument(!objects.isEmpty());
    return re -> Iterables.get(objects, re.nextInt(objects.size()));
  }

  @SafeVarargs
  public static <T> Generator<T> oneOf(T... objects) {
    return oneOf(ImmutableList.copyOf(objects));
  }

  public static <T> Generator<T> onGenOf(Collection<Generator<? extends T>> generators) {
    checkArgument(!generators.isEmpty());
    return re -> Iterables.get(generators, re.nextInt(generators.size())).get(re);
  }

  @SafeVarargs
  public static <T> Generator<T> onGenOf(Generator<? extends T>... generators) {
    return onGenOf(ImmutableList.copyOf(generators));
  }

  public static <T> Generator<T> nullable(Generator<? extends T> generator, double nullRate) {
    checkArgument(nullRate >= 0 && nullRate <= 1);
    return re -> re.nextDouble() <= nullRate ? null : generator.get(re);
  }

  public static <T> Generator<T> selection(Generator<? extends T> trueGen, Generator<? extends T> falseGen, Generator<Boolean> boolGen) {
    return re -> boolGen.get(re) ? trueGen.get(re) : falseGen.get(re);
  }

  public static <T> Generator<T> filter(Generator<? extends T> generator, Predicate<T> predicate) {
    return re -> {
      T value;
      do {
        value = generator.get(re);
      } while (!predicate.test(value)); // FIXME handle infinite loop
      return value;
    };
  }

  public static <T, R> Generator<R> map(Generator<? extends T> generator, Function<T, R> mapper) {
    return re -> mapper.apply(generator.get(re));
  }

}

@@ -188,53 +188,9 @@
 ion.
-Function;%0Aimport java.util.function.Predicate
+*
 ;%0A%0Ai
@@ -262,16 +262,22 @@
 %0Apublic 
+final 
 class Ge
@@ -295,79 +295,289 @@
 %0A  p
-ublic static %3CT%3E Generator%3CT%3E constGen(T value) %7B%0A    return re -%3E valu
+rivate static final int MAX_FILTER_LOOP = 1000;%0A%0A  private Generators() %7B%0A    /* nothing */%0A  %7D%0A%0A  public static %3CT%3E Generator%3CT%3E constGen(T value) %7B%0A    return re -%3E value;%0A  %7D%0A%0A  public static Generator%3CBoolean%3E coin(double trueRate) %7B%0A    return re -%3E re.nextDouble() %3C= trueRat
 e;%0A 
@@ -1834,24 +1834,46 @@
    T value;%0A
+      int nbLoop = 0;%0A
       do %7B%0A 
@@ -1916,97 +1916,1537 @@
     
-%7D while (!predicate.test(value)); // FIXME handle infinite loop%0A      return value;%0A    %7D
+  nbLoop++;%0A        if (nbLoop %3E MAX_FILTER_LOOP) %7B%0A          throw reachMaxFilterException();%0A        %7D%0A      %7D while (!predicate.test(value));%0A      return value;%0A    %7D;%0A  %7D%0A%0A  public static Generator%3CDouble%3E filter(Generator%3CDouble%3E generator, DoublePredicate predicate) %7B%0A    return re -%3E %7B%0A      double value;%0A      int nbLoop = 0;%0A      do %7B%0A        value = generator.get(re);%0A        nbLoop++;%0A        if (nbLoop %3E MAX_FILTER_LOOP) %7B%0A          throw reachMaxFilterException();%0A        %7D%0A      %7D while (!predicate.test(value)); // FIXME handle infinite loop%0A      return value;%0A    %7D;%0A  %7D%0A%0A  public static Generator%3CLong%3E filter(Generator%3CLong%3E generator, LongPredicate predicate) %7B%0A    return re -%3E %7B%0A      long value;%0A      int nbLoop = 0;%0A      do %7B%0A        value = generator.get(re);%0A        nbLoop++;%0A        if (nbLoop %3E MAX_FILTER_LOOP) %7B%0A          throw reachMaxFilterException();%0A        %7D%0A      %7D while (!predicate.test(value));%0A      return value;%0A    %7D;%0A  %7D%0A%0A  public static Generator%3CInteger%3E filter(Generator%3CInteger%3E generator, IntPredicate predicate) %7B%0A    return re -%3E %7B%0A      int value;%0A      int nbLoop = 0;%0A      do %7B%0A        value = generator.get(re);%0A        nbLoop++;%0A        if (nbLoop %3E MAX_FILTER_LOOP) %7B%0A          throw reachMaxFilterException();%0A        %7D%0A      %7D while (!predicate.test(value));%0A      return value;%0A    %7D;%0A  %7D%0A%0A  private static RuntimeException reachMaxFilterException() %7B%0A    return new RuntimeException(%22Generate %22 + MAX_FILTER_LOOP + %22 values but none match given predicate%22)
 ;%0A  

Handle "infinite" loop in filter generators