Skip to content

Commit 2ac9d52

Browse files
committed
functionaljava#176: Implemented min, minKey, max and maxKey for tree maps. Added arbitrary tree maps.
1 parent 63c87b8 commit 2ac9d52

File tree

9 files changed

+173
-44
lines changed

9 files changed

+173
-44
lines changed

core/src/main/java/fj/data/TreeMap.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,34 @@ public <W> TreeMap<K, W> map(final F<V, W> f) {
308308
return new TreeMap<K, W>(tree.map(TreeMap.<K, Option<W>>ord(o), g));
309309
}
310310

311+
/**
312+
* Returns the minimum (key, value) pair in the tree if the tree is not empty.
313+
*/
314+
public Option<P2<K, V>> min() {
315+
return tree.min().map(p -> p(p._1(), p._2().some()));
316+
}
317+
318+
/**
319+
* Returns the minimum key in the tree if the tree is not empty.
320+
*/
321+
public Option<K> minKey() {
322+
return tree.min().map(p -> p._1());
323+
}
324+
325+
/**
326+
* Returns the maximum (key, value) pair in the tree if the tree is not empty.
327+
*/
328+
public Option<P2<K, V>> max() {
329+
return tree.max().map(p -> p(p._1(), p._2().some()));
330+
}
331+
332+
/**
333+
* Returns the maximum key in the tree if the tree is not empty.
334+
*/
335+
public Option<K> maxKey() {
336+
return tree.max().map(p -> p._1());
337+
}
338+
311339
/**
312340
* The expression <code>t1.union(t2)</code> takes the left-biased union of <code>t1</code>
313341
* and <code>t2</code>. It prefers <code>t1</code> when duplicate keys are encountered.

core/src/test/java/fj/data/TreeMapTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.junit.Test;
1212

1313
import static fj.P.p;
14+
import static fj.data.Option.none;
1415
import static fj.data.Option.some;
1516
import static org.hamcrest.CoreMatchers.equalTo;
1617
import static org.junit.Assert.assertEquals;
@@ -112,4 +113,13 @@ public void testString() {
112113
assertThat(t2.toStream(), equalTo(s));
113114
}
114115

116+
@Test
117+
public void minKey() {
118+
TreeMap<Integer, Integer> t1 = TreeMap.<Integer, Integer>empty(Ord.intOrd);
119+
assertThat(t1.minKey(), equalTo(none()));
120+
TreeMap<Integer, Integer> t2 = t1.set(1, 2).set(2, 4).set(10, 20).set(5, 10).set(0, 100);
121+
assertThat(t2.minKey(), equalTo(some(0)));
122+
assertThat(t2.delete(0).minKey(), equalTo(some(1)));
123+
}
124+
115125
}

props-core-scalacheck/src/main/scala/fj/data/ArbitraryTreeMap.scala

Lines changed: 0 additions & 16 deletions
This file was deleted.

props-core-scalacheck/src/test/scala/fj/Tests.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ object Tests {
1313
fj.data.CheckHashMap.properties,
1414
fj.data.CheckHashSet.properties,
1515
fj.data.CheckSet.properties,
16-
fj.data.CheckTreeMap.properties,
1716
fj.control.parallel.CheckStrategy.properties,
1817
fj.control.parallel.CheckParModule.properties
1918
).flatten

props-core-scalacheck/src/test/scala/fj/data/CheckTreeMap.scala

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package fj.data.properties;
2+
3+
import fj.Equal;
4+
import fj.data.TreeMap;
5+
import fj.test.Arbitrary;
6+
import fj.test.Gen;
7+
import fj.test.Property;
8+
import fj.test.reflect.CheckParams;
9+
import fj.test.runner.PropertyTestRunner;
10+
import org.junit.runner.RunWith;
11+
12+
import java.util.concurrent.atomic.AtomicInteger;
13+
14+
import static fj.Equal.stringEqual;
15+
import static fj.Ord.intOrd;
16+
import static fj.test.Arbitrary.arbInteger;
17+
import static fj.test.Arbitrary.arbString;
18+
import static fj.test.Arbitrary.arbTreeMap;
19+
import static fj.test.Property.*;
20+
21+
/**
22+
* Created by MarkPerry on 29/08/2015.
23+
*/
24+
@RunWith(PropertyTestRunner.class)
25+
@CheckParams(maxSize = 10000)
26+
public class TreeMapProperties {
27+
28+
private static final int smallMax = 5;
29+
private static final int midMax = 20;
30+
public static final Arbitrary<TreeMap<Integer, String>> smallArbTreeMapIS = arbTreeMap(intOrd, arbInteger, arbString, smallMax);
31+
public static final Arbitrary<TreeMap<Integer, String>> arbTreeMapIS = arbTreeMap(intOrd, arbInteger, arbString, midMax);
32+
33+
public Property empty() {
34+
return property(smallArbTreeMapIS, tm -> impliesBoolean(tm.isEmpty(), tm.size() == 0));
35+
}
36+
37+
public Property set() {
38+
return property(arbTreeMapIS, arbInteger, arbString, (tm, k, v) ->
39+
prop(stringEqual.eq(tm.set(k, v).get(k).some(), v))
40+
);
41+
}
42+
43+
public Property updateId() {
44+
return property(arbTreeMapIS, arbInteger, arbString, (tm, k, v) ->
45+
prop(stringEqual.eq(tm.set(k, v).update(k, x -> x)._2().get(k).some(), v))
46+
);
47+
}
48+
49+
public Property update() {
50+
return property(arbTreeMapIS, arbInteger, arbString, arbString, (tm, k, v, v2) ->
51+
prop(stringEqual.eq(tm.set(k, v).update(k, x -> x + v2)._2().get(k).some(), v + v2))
52+
);
53+
}
54+
55+
public Property minKey() {
56+
return property(arbTreeMapIS, tm ->
57+
impliesBoolean(!tm.isEmpty(), () -> Equal.intEqual.eq(tm.minKey().some(), tm.toStream().head()._1()))
58+
);
59+
}
60+
61+
public Property maxKey() {
62+
return property(arbTreeMapIS, tm ->
63+
impliesBoolean(!tm.isEmpty(),
64+
() -> Equal.intEqual.eq(tm.maxKey().some(), tm.toStream().reverse().head()._1())
65+
)
66+
);
67+
}
68+
69+
}

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

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import fj.F7;
1010
import fj.F8;
1111
import fj.Function;
12+
import fj.Bottom;
13+
1214
import static fj.Function.compose;
1315
import static fj.P.p;
1416
import fj.P1;
@@ -1115,22 +1117,59 @@ public Stack<A> f(final Array<A> a) {
11151117
}
11161118

11171119
/**
1118-
* Returns an arbitrary implementation for tree maps.
1120+
* Returns an arbitrary implementation for java.util tree maps.
11191121
*
11201122
* @param ak An arbitrary implementation for the type over which the tree map's keys are defined.
11211123
* @param av An arbitrary implementation for the type over which the tree map's values are
11221124
* defined.
11231125
* @return An arbitrary implementation for tree maps.
11241126
*/
1125-
public static <K, V> Arbitrary<TreeMap<K, V>> arbTreeMap(final Arbitrary<K> ak, final Arbitrary<V> av) {
1126-
return arbitrary(arbHashtable(ak, av).gen.map(new F<Hashtable<K, V>, TreeMap<K, V>>() {
1127+
public static <K, V> Arbitrary<java.util.TreeMap<K, V>> arbJavaTreeMap(final Arbitrary<K> ak, final Arbitrary<V> av) {
1128+
return arbitrary(arbHashtable(ak, av).gen.map(new F<Hashtable<K, V>, java.util.TreeMap<K, V>>() {
11271129
@SuppressWarnings({"UseOfObsoleteCollectionType"})
1128-
public TreeMap<K, V> f(final Hashtable<K, V> ht) {
1129-
return new TreeMap<K, V>(ht);
1130+
public java.util.TreeMap<K, V> f(final Hashtable<K, V> ht) {
1131+
return new java.util.TreeMap<K, V>(ht);
11301132
}
11311133
}));
11321134
}
11331135

1136+
/**
1137+
* Returns an arbitrary implementation for tree maps.
1138+
*/
1139+
public static <K, V> Arbitrary<fj.data.TreeMap<K, V>> arbTreeMap(Ord<K> ord, Arbitrary<List<P2<K, V>>> al) {
1140+
return arbitrary(al.gen.map(list -> fj.data.TreeMap.treeMap(ord, list)));
1141+
}
1142+
1143+
/**
1144+
* Returns an arbitrary implementation for tree maps.
1145+
*/
1146+
public static <K, V> Arbitrary<fj.data.TreeMap<K, V>> arbTreeMap(Ord<K> ord, Arbitrary<K> ak, Arbitrary<V> av) {
1147+
return arbTreeMap(ord, arbList(arbP2(ak, av)));
1148+
}
1149+
1150+
/**
1151+
* Returns an arbitrary implementation for tree maps where the map size is the given arbitrary integer.
1152+
*/
1153+
public static <K, V> Arbitrary<fj.data.TreeMap<K, V>> arbTreeMap(Ord<K> ord, Arbitrary<K> ak, Arbitrary<V> av, Arbitrary<Integer> ai) {
1154+
Gen<List<P2<K, V>>> gl2 = ai.gen.bind(i -> {
1155+
if (i < 0) {
1156+
Bottom.error("Undefined: arbitrary natural is negative (" + i + ")");
1157+
}
1158+
return Gen.sequenceN(Math.max(i, 0), Arbitrary.arbP2(ak, av).gen);
1159+
});
1160+
return arbTreeMap(ord, arbitrary(gl2));
1161+
}
1162+
1163+
/**
1164+
* Returns an arbitrary implementation for tree maps where the size is less than or equal to the max size.
1165+
*/
1166+
public static <K, V> Arbitrary<fj.data.TreeMap<K, V>> arbTreeMap(Ord<K> ord, Arbitrary<K> ak, Arbitrary<V> av, int maxSize) {
1167+
if (maxSize < 0) {
1168+
Bottom.error("Undefined: arbitrary natural is negative (" + maxSize + ")");
1169+
}
1170+
return arbTreeMap(ord, ak, av, arbitrary(Gen.choose(0, maxSize)));
1171+
}
1172+
11341173
/**
11351174
* Returns an arbitrary implementation for tree sets.
11361175
*

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,22 @@ public Result f(final Rand r) {
362362
});
363363
}
364364

365+
/**
366+
* Returns a property that produces a result only if the given condition satisfies. The result
367+
* will be taken from the given boolean b.
368+
*/
369+
public static Property impliesBoolean(final boolean a, final boolean b) {
370+
return implies(a, () -> Property.prop(b));
371+
}
372+
373+
/**
374+
* Returns a property that produces a result only if the given condition satisfies. The result
375+
* will be taken from the given lazy boolean b.
376+
*/
377+
public static Property impliesBoolean(final boolean a, final F0<Boolean> b) {
378+
return implies(a, () -> Property.prop(b.f()));
379+
}
380+
365381
/**
366382
* Returns a property from the given function.
367383
*

quickcheck/src/main/java/fj/test/runner/PropertyTestRunner.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ public void run(RunNotifier notifier) {
4343
CheckResult result = checkProperty(p._1(), p._2());
4444

4545
try {
46-
CheckResult.summaryEx.showS(result);
46+
String s = CheckResult.summaryEx.showS(result);
47+
System.out.println(getLabel(desc) + ": " + s);
4748
} catch (Throwable t) {
4849
notifier.fireTestFailure(new Failure(desc, t));
4950
}
@@ -52,6 +53,10 @@ public void run(RunNotifier notifier) {
5253
});
5354
}
5455

56+
private static String getLabel(Description d) {
57+
return d.getDisplayName();
58+
}
59+
5560
private static CheckResult checkProperty(Property prop, Option<CheckParams> params) {
5661
for (CheckParams ps : params) {
5762
return prop.check(ps.minSuccessful(), ps.maxDiscarded(), ps.minSize(), ps.maxSize());

0 commit comments

Comments
 (0)