Skip to content

Commit 521de97

Browse files
committed
Merge pull request #236 from mrbackend/master
#235 fj.test.Gen.pick is broken and #237 fj.test.Rand.reseed() is broken
2 parents 5d45d6a + 3548db1 commit 521de97

File tree

5 files changed

+521
-85
lines changed

5 files changed

+521
-85
lines changed

quickcheck/src/main/java/fj/test/Gen.java

Lines changed: 218 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,28 @@
11
package fj.test;
22

3-
import static fj.Bottom.error;
4-
import fj.Effect;
53
import fj.F;
64
import fj.Function;
7-
import static fj.Function.flip;
8-
import static fj.Function.curry;
95
import fj.P2;
10-
import static fj.P2.__1;
116
import fj.Unit;
12-
import fj.F2;
13-
import static fj.data.Array.array;
7+
import fj.control.Trampoline;
8+
import fj.data.Array;
149
import fj.data.List;
15-
import static fj.data.List.nil;
16-
import static fj.data.List.replicate;
1710
import fj.data.Option;
1811
import fj.function.Effect1;
1912

13+
import static fj.Bottom.error;
14+
import static fj.Function.curry;
15+
import static fj.Function.flip;
2016
import static fj.Monoid.intAdditionMonoid;
2117
import static fj.Ord.intOrd;
22-
18+
import static fj.P.lazy;
19+
import static fj.P2.__1;
20+
import static fj.control.Trampoline.pure;
21+
import static fj.control.Trampoline.suspend;
22+
import static fj.data.Array.array;
23+
import static fj.data.List.cons;
24+
import static fj.data.List.nil;
25+
import static fj.data.List.replicate;
2326
import static java.lang.Math.max;
2427
import static java.lang.Math.min;
2528

@@ -560,47 +563,231 @@ public static <A> Gen<List<A>> listOf1(final Gen<A> g) {
560563
return listOf(g, 1);
561564
}
562565

566+
/**
567+
* Returns a generator that picks one element from the given list. If the given list is empty, then the
568+
* returned generator will never produce a value.
569+
*
570+
* @param as The list from which to pick an element.
571+
* @return A generator that picks an element from the given list.
572+
*/
573+
public static <A> Gen<A> pickOne(List<A> as) {
574+
// This is the fastest of the four; functionally, any of them would do
575+
return wordOf(1, as).map(List::head);
576+
}
577+
563578
/**
564579
* Returns a generator of lists that picks the given number of elements from the given list. If
565580
* the given number is less than zero or greater than the length of the given list, then the
566581
* returned generator will never produce a value.
582+
* <p>
583+
* Note: pick is synonymous with combinationOf
567584
*
568585
* @param n The number of elements to pick from the given list.
569586
* @param as The list from which to pick elements.
570587
* @return A generator of lists that picks the given number of elements from the given list.
571588
*/
572-
public static <A> Gen<List<A>> pick(final int n, final List<A> as) {
573-
return n < 0 || n > as.length() ? Gen.<List<A>>fail() : sequenceN(n, choose(0, as.length() - 1)).map(new F<List<Integer>, List<A>>() {
574-
public List<A> f(final List<Integer> is) {
575-
List<A> r = nil();
589+
@Deprecated
590+
public static <A> Gen<List<A>> pick(int n, List<A> as) {
591+
return combinationOf(n, as);
592+
}
576593

577-
List<Integer> iis = is.sort(intOrd);
578-
List<P2<A, Integer>> aas = as.zipIndex();
594+
/**
595+
* Returns a generator of lists that picks the given number of elements from the given list. The selection is
596+
* a combination without replacement of elements from the given list, i.e.
597+
* <ul>
598+
* <li>For any given selection, a generated list will always contain its elements in the same order</li>
599+
* <li>An element will never be picked more than once</li>
600+
* </ul>
601+
* <p>
602+
* If the given number is less than zero or greater than the length of the given list, then the
603+
* returned generator will never produce a value.
604+
*
605+
* @param n The number of elements to pick from the given list.
606+
* @param as The list from which to pick elements.
607+
* @return A generator of lists that picks the given number of elements from the given list.
608+
*/
609+
public static <A> Gen<List<A>> combinationOf(int n, List<A> as) {
610+
int aLength = as.length();
611+
return ((n >= 0) && (n <= aLength)) ?
612+
parameterised(s -> r -> {
613+
final class Tramp {
614+
615+
// Picks elements in constant stack space
616+
private Trampoline<List<A>> tramp(List<A> remainAs, int remainN, int remainALength) {
617+
return suspend(lazy(() ->
618+
(remainN == 0) ?
619+
// We have picked N elements; stop
620+
pure(nil()) :
621+
// For M remaining elements of which N will be picked, pick remainAs.head() with probability N/M
622+
(r.choose(0, remainALength - 1) < remainN) ?
623+
tramp(remainAs.tail(), remainN - 1, remainALength - 1)
624+
.map(pickedTail -> cons(remainAs.head(), pickedTail)) :
625+
tramp(remainAs.tail(), remainN, remainALength - 1)));
626+
}
579627

580-
//noinspection ForLoopWithMissingComponent
581-
for(; iis.isNotEmpty() && aas.isNotEmpty(); aas = aas.tail())
582-
if(iis.head().equals(aas.head()._2()))
583-
iis = iis.tail();
584-
else
585-
r = r.snoc(aas.head()._1());
628+
}
629+
return value(new Tramp().tramp(as, n, aLength).run());
630+
}) :
631+
fail();
632+
}
586633

587-
return r;
588-
}
589-
});
634+
/**
635+
* Returns a generator of lists that picks the given number of elements from the given list. The selection is
636+
* a combination with replacement of elements from the given list, i.e.
637+
* <ul>
638+
* <li>For any given selection, a generated list will always contain its elements in the same order</li>
639+
* <li>Each element may be picked more than once</li>
640+
* </ul>
641+
* <p>
642+
* If the given number is less than zero, then the returned generator will never produce a value. Note that,
643+
* with replacement, the given number may be larger than the length of the given list.
644+
*
645+
* @param n The number of elements to pick from the given list.
646+
* @param as The list from which to pick elements.
647+
* @return A generator of lists that picks the given number of elements from the given list.
648+
*/
649+
public static <A> Gen<List<A>> selectionOf(int n, List<A> as) {
650+
Array<A> aArr = as.toArray();
651+
return (n >= 0) ?
652+
pick(indexWord(n, aArr.length()).map(indexes -> indexes.sort(intOrd)), aArr) :
653+
fail();
654+
}
655+
656+
/**
657+
* Returns a generator of lists that picks the given number of elements from the given list. The selection is
658+
* a permutation without replacement of elements from the given list, i.e.
659+
* <ul>
660+
* <li>For any given selection, a generated list may contain its elements in any order</li>
661+
* <li>An element will never be picked more than once</li>
662+
* </ul>
663+
* <p>
664+
* If the given number is less than zero or greater than the length of the given list, then the
665+
* returned generator will never produce a value.
666+
*
667+
* @param n The number of elements to pick from the given list.
668+
* @param as The list from which to pick elements.
669+
* @return A generator of lists that picks the given number of elements from the given list.
670+
*/
671+
public static <A> Gen<List<A>> permutationOf(int n, List<A> as) {
672+
return parameterised(s -> r ->
673+
combinationOf(n, as).map(combination -> {
674+
// Shuffle combination using the Fisher-Yates algorithm
675+
Array<A> aArr = combination.toArray();
676+
int length = aArr.length();
677+
for (int i = length - 1; i > 0; --i) {
678+
int j = r.choose(0, i);
679+
A tmp = aArr.get(i);
680+
aArr.set(i, aArr.get(j));
681+
aArr.set(j, tmp);
682+
}
683+
return aArr.toList();
684+
}));
685+
}
686+
687+
/**
688+
* Returns a generator of lists that picks the given number of elements from the given list. The selection is
689+
* a permutation with replacement of elements from the given list, i.e.
690+
* <ul>
691+
* <li>For any given selection, a generated list may contain its elements in any order</li>
692+
* <li>Each element may be picked more than once</li>
693+
* </ul>
694+
* <p>
695+
* If the given number is less than zero, then the returned generator will never produce a value. Note that,
696+
* with replacement, the given number may be larger than the length of the given list.
697+
*
698+
* @param n The number of elements to pick from the given list.
699+
* @param as The list from which to pick elements.
700+
* @return A generator of lists that picks the given number of elements from the given list.
701+
*/
702+
public static <A> Gen<List<A>> wordOf(int n, List<A> as) {
703+
Array<A> aArr = as.toArray();
704+
return (n >= 0) ?
705+
pick(indexWord(n, aArr.length()), aArr) :
706+
fail();
707+
}
708+
709+
private static Gen<List<Integer>> indexWord(int n, int m) {
710+
return sequenceN(n, choose(0, m - 1));
711+
}
712+
713+
private static <A> Gen<List<A>> pick(Gen<List<Integer>> indexesGen, Array<A> as) {
714+
return indexesGen.map(indexes ->
715+
indexes.foldLeft((acc, index) -> cons(as.get(index), acc), List.<A>nil()).reverse());
590716
}
591717

592718
/**
593719
* Returns a generator of lists that produces some of the values of the given list.
720+
* <p>
721+
* Note: someOf is synonymous with someCombinationOf
594722
*
595723
* @param as The list from which to pick values.
596724
* @return A generator of lists that produces some of the values of the given list.
597725
*/
598-
public static <A> Gen<List<A>> someOf(final List<A> as) {
599-
return choose(0, as.length()).bind(new F<Integer, Gen<List<A>>>() {
600-
public Gen<List<A>> f(final Integer i) {
601-
return pick(i, as);
602-
}
603-
});
726+
@Deprecated
727+
public static <A> Gen<List<A>> someOf(List<A> as) {
728+
return someCombinationOf(as);
729+
}
730+
731+
/**
732+
* Returns a generator of lists that produces some of the values of the given list. The selection is
733+
* a combination without replacement of elements from the given list, i.e.
734+
* <ul>
735+
* <li>For any given selection, a generated list will always contain its elements in the same order</li>
736+
* <li>An element will never be picked more than once</li>
737+
* </ul>
738+
*
739+
* @param as The list from which to pick values.
740+
* @return A generator of lists that produces some of the values of the given list.
741+
*/
742+
public static <A> Gen<List<A>> someCombinationOf(List<A> as) {
743+
return choose(0, as.length()).bind(n -> combinationOf(n, as));
744+
}
745+
746+
/**
747+
* Returns a generator of lists that produces some of the values of the given list. The selection is
748+
* a combination with replacement of elements from the given list, i.e.
749+
* <ul>
750+
* <li>For any given selection, a generated list will always contain its elements in the same order</li>
751+
* <li>Each element may be picked more than once</li>
752+
* </ul>
753+
*
754+
* @param maxLength The maximum length of a generated list
755+
* @param as The list from which to pick values.
756+
* @return A generator of lists that produces some of the values of the given list.
757+
*/
758+
public static <A> Gen<List<A>> someSelectionOf(int maxLength, List<A> as) {
759+
return choose(0, maxLength).bind(n -> selectionOf(n, as));
760+
}
761+
762+
/**
763+
* Returns a generator of lists that produces some of the values of the given list. The selection is
764+
* a permutation without replacement of elements from the given list, i.e.
765+
* <ul>
766+
* <li>For any given selection, a generated list may contain its elements in any order</li>
767+
* <li>An element will never be picked more than once</li>
768+
* </ul>
769+
*
770+
* @param as The list from which to pick values.
771+
* @return A generator of lists that produces some of the values of the given list.
772+
*/
773+
public static <A> Gen<List<A>> somePermutationOf(List<A> as) {
774+
return choose(0, as.length()).bind(n -> permutationOf(n, as));
775+
}
776+
777+
/**
778+
* Returns a generator of lists that produces some of the values of the given list. The selection is
779+
* a permutation with replacement of elements from the given list, i.e.
780+
* <ul>
781+
* <li>For any given selection, a generated list may contain its elements in any order</li>
782+
* <li>Each element may be picked more than once</li>
783+
* </ul>
784+
*
785+
* @param maxLength The maximum length of a generated list
786+
* @param as The list from which to pick values.
787+
* @return A generator of lists that produces some of the values of the given list.
788+
*/
789+
public static <A> Gen<List<A>> someWordOf(int maxLength, List<A> as) {
790+
return choose(0, maxLength).bind(n -> wordOf(n, as));
604791
}
605792

606793
/**

quickcheck/src/main/java/fj/test/Property.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ else if (x.isFalsified()) {
184184
}
185185

186186
/**
187-
* Checks this property using a {@link Rand#Rand(F, F) standard random generator} and the given
187+
* Checks this property using a {@link Rand#standard standard random generator} and the given
188188
* arguments to produce a result.
189189
*
190190
* @param minSuccessful The minimum number of successful tests before a result is reached.
@@ -226,7 +226,7 @@ public CheckResult check(final Rand r, final int minSize, final int maxSize) {
226226
}
227227

228228
/**
229-
* Checks this property using a {@link Rand#Rand(F, F) standard random generator}, 100 minimum
229+
* Checks this property using a {@link Rand#standard standard random generator}, 100 minimum
230230
* successful checks, 500 maximum discarded tests and the given arguments to produce a result.
231231
*
232232
* @param minSize The minimum size to use for checking.
@@ -239,7 +239,7 @@ public CheckResult check(final int minSize,
239239
}
240240

241241
/**
242-
* Checks this property using a {@link Rand#Rand(F, F) standard random generator}, 100 minimum
242+
* Checks this property using a {@link Rand#standard standard random generator}, 100 minimum
243243
* successful checks, 500 maximum discarded tests, minimum size of 0, maximum size of 100.
244244
*
245245
* @return A result after checking this property.
@@ -249,7 +249,7 @@ public CheckResult check() {
249249
}
250250

251251
/**
252-
* Checks this property using a {@link Rand#Rand(F, F) standard random generator}, the given minimum
252+
* Checks this property using a {@link Rand#standard standard random generator}, the given minimum
253253
* successful checks, 500 maximum discarded tests, minimum size of 0, maximum size of 100.
254254
*
255255
* @param minSuccessful The minimum number of successful tests before a result is reached.
@@ -272,7 +272,7 @@ public CheckResult minSuccessful(final Rand r, final int minSuccessful) {
272272
}
273273

274274
/**
275-
* Checks this property using a {@link Rand#Rand(F, F) standard random generator}, 100 minimum
275+
* Checks this property using a {@link Rand#standard standard random generator}, 100 minimum
276276
* successful checks, the given maximum discarded tests, minimum size of 0, maximum size of 100.
277277
*
278278
* @param maxDiscarded The maximum number of tests discarded because they did not satisfy
@@ -297,7 +297,7 @@ public CheckResult maxDiscarded(final Rand r, final int maxDiscarded) {
297297
}
298298

299299
/**
300-
* Checks this property using a {@link Rand#Rand(F, F) standard random generator}, 100 minimum
300+
* Checks this property using a {@link Rand#standard standard random generator}, 100 minimum
301301
* successful checks, 500 maximum discarded tests, the given minimum size, maximum size of 100.
302302
*
303303
* @param minSize The minimum size to use for checking.
@@ -320,7 +320,7 @@ public CheckResult minSize(final Rand r, final int minSize) {
320320
}
321321

322322
/**
323-
* Checks this property using a {@link Rand#Rand(F, F) standard random generator}, 100 minimum
323+
* Checks this property using a {@link Rand#standard standard random generator}, 100 minimum
324324
* successful checks, 500 maximum discarded tests, minimum size of 0, the given maximum size.
325325
*
326326
* @param maxSize The maximum size to use for checking.

0 commit comments

Comments
 (0)