1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.functor.example;
18
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertTrue;
21 import static org.junit.Assert.fail;
22
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.Random;
27
28 import org.apache.commons.functor.BinaryFunction;
29 import org.apache.commons.functor.Function;
30 import org.apache.commons.functor.core.Constant;
31 import org.apache.commons.functor.core.collection.IsEmpty;
32 import org.apache.commons.functor.core.comparator.IsGreaterThanOrEqual;
33 import org.apache.commons.functor.core.comparator.IsLessThan;
34 import org.apache.commons.functor.core.composite.ConditionalFunction;
35 import org.apache.commons.functor.generator.FilteredGenerator;
36 import org.apache.commons.functor.generator.loop.IteratorToGeneratorAdapter;
37 import org.junit.Test;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 @SuppressWarnings("unchecked")
62 public class QuicksortExample {
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83 @Test
84 public void testSortEmpty() {
85 List<Object> empty = Collections.EMPTY_LIST;
86 List<?> result = quicksort(empty);
87 assertTrue("Sorting an empty list should produce an empty list.", result.isEmpty());
88 }
89
90
91
92
93
94
95 @Test
96 public void testSortSingleElementList() {
97 List<Object> list = new ArrayList<Object>();
98 list.add("element");
99
100 List<?> sorted = quicksort(list);
101
102 assertTrue(
103 "The quicksort() method should return a distinct list.",
104 list != sorted);
105
106 assertEquals(
107 "Sorting a single-element list should produce an equivalent list",
108 list,
109 sorted);
110 }
111
112
113
114
115
116
117 @Test
118 public void testSortSingleValueList() {
119 List<Object> list = new ArrayList<Object>();
120 for (int i = 0; i < 10; i++) {
121 list.add("element");
122 }
123 List<?> sorted = quicksort(list);
124
125 assertTrue(
126 "The quicksort() method should return a distinct list.",
127 list != sorted);
128
129 assertEquals(list, sorted);
130 }
131
132
133
134
135
136
137
138
139 @Test
140 public void testSortSorted() {
141 List<Object> list = new ArrayList<Object>();
142 for (int i = 0; i < 10; i++) {
143 list.add(Integer.valueOf(i));
144 }
145
146 List<?> sorted = quicksort(list);
147
148 assertTrue(
149 "The quicksort() method should return a distinct list.",
150 list != sorted);
151
152 assertEquals(
153 "Sorting an already sorted list should produce an equivalent list",
154 list,
155 sorted);
156 }
157
158
159
160
161
162 @Test
163 public void testSortReversed() {
164 List<Object> expected = new ArrayList<Object>();
165 List<Object> tosort = new ArrayList<Object>();
166 for (int i = 0; i < 10; i++) {
167
168
169
170 expected.add(Integer.valueOf(i));
171
172
173
174 tosort.add(Integer.valueOf(9 - i));
175 }
176
177 assertEquals(expected, quicksort(tosort));
178 }
179
180
181
182
183 @Test
184 public void testSortShuffled() {
185 List<Object> expected = new ArrayList<Object>();
186 for (int i = 0; i < 10; i++) {
187 expected.add(Integer.valueOf(i));
188 }
189 List<Object> tosort = new ArrayList<Object>(expected);
190 Collections.shuffle(tosort);
191
192 assertEquals(expected, quicksort(tosort));
193 }
194
195
196
197
198 @Test
199 public void testSortRandom() {
200 Random random = new Random();
201
202
203
204 List<Integer> tosort = new ArrayList<Integer>();
205 for (int i = 0; i < 10; i++) {
206 tosort.add(Integer.valueOf(random.nextInt(10)));
207 }
208
209
210
211
212 List<Integer> expected = new ArrayList<Integer>(tosort);
213 Collections.sort(expected);
214
215 assertEquals(expected, quicksort(tosort));
216 }
217
218
219
220
221
222
223
224
225 private static final int SIZE = 1000;
226 private static final int COUNT = 100;
227
228 @Test
229 public void testTimings() {
230
231
232
233 @SuppressWarnings("unused")
234 long elapsed = 0L;
235
236
237
238
239 Random random = new Random();
240
241
242
243
244 for (int i = 0; i < COUNT; i++) {
245
246
247
248
249 List<Object> tosort = new ArrayList<Object>(SIZE);
250 for (int j = 0; j < SIZE; j++) {
251 tosort.add(Integer.valueOf(random.nextInt(SIZE)));
252 }
253
254
255
256
257 long start = System.currentTimeMillis();
258
259
260
261
262
263 quicksort(tosort);
264
265
266
267
268 long stop = System.currentTimeMillis();
269
270
271
272
273 elapsed += stop - start;
274 }
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300 }
301
302
303
304
305
306
307
308
309
310
311
312
313 public List<?> quicksort(List<?> list) {
314 return (List<?>)(quicksort.evaluate(list));
315 }
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340 private Function<Object, Object> quicksort = new ConditionalFunction<Object, Object>(
341
342 IsEmpty.instance(),
343
344 new Constant<Object>(Collections.EMPTY_LIST),
345
346 new ListFunction() {
347 @Override
348 public Object evaluate(List<?> list) {
349
350 List<Object> result = new ArrayList<Object>(list.size());
351
352
353
354
355
356 result.addAll(
357 (List<?>) quicksort.evaluate(
358 lesserTail.evaluate(
359 head.evaluate(list),
360 tail.evaluate(list))));
361
362
363
364 result.add(head.evaluate(list));
365
366
367
368
369
370 result.addAll(
371 (List<?>) quicksort.evaluate(
372 greaterTail.evaluate(
373 head.evaluate(list),
374 tail.evaluate(list))));
375
376
377
378 return result;
379 }
380 });
381
382
383
384
385
386
387
388
389
390
391
392 public abstract class ListFunction implements Function<Object, Object> {
393 public abstract Object evaluate(List<?> list);
394
395 public Object evaluate(Object obj) {
396 if (obj instanceof List) {
397 return evaluate((List<?>) obj);
398 } else if (null == obj) {
399 throw new NullPointerException("The argument must not be null.");
400 } else {
401 throw new ClassCastException(
402 "The argument must be a List, found " +
403 obj.getClass().getName());
404 }
405 }
406 }
407
408
409
410
411
412
413 public abstract class ObjectListFunction implements BinaryFunction<Object, Object, Object> {
414 public abstract Object evaluate(Object head, List<?> tail);
415
416 public Object evaluate(Object left, Object right) {
417 if (left != null && right instanceof List) {
418 return evaluate(left, (List<?>) right);
419 } else if (null == left) {
420 throw new NullPointerException("The left argument must not be null.");
421 } else if (null == right) {
422 throw new NullPointerException("The right argument must not be null.");
423 } else {
424 throw new ClassCastException(
425 "The right argument must be a List, found " +
426 right.getClass().getName());
427 }
428 }
429 }
430
431
432
433
434
435
436
437 private Function<Object, Object> head = new ListFunction() {
438 @Override
439 public Object evaluate(List<?> list) {
440 return list.get(0);
441 }
442 };
443
444
445
446
447 private Function<Object, Object> tail = new ListFunction() {
448 @Override
449 public Object evaluate(List<?> list) {
450 return list.size() < 2 ?
451 Collections.EMPTY_LIST :
452 list.subList(1, list.size());
453 }
454 };
455
456
457
458
459
460
461
462 @SuppressWarnings("rawtypes")
463 private BinaryFunction<Object, Object, Object> lesserTail = new ObjectListFunction() {
464 @Override
465 public Object evaluate(Object head, List<?> tail) {
466 return new FilteredGenerator(
467 IteratorToGeneratorAdapter.adapt(tail.iterator()),
468 IsLessThan.instance((Comparable<?>) head)).toCollection();
469 }
470 };
471
472
473
474
475
476
477 @SuppressWarnings("rawtypes")
478 private BinaryFunction greaterTail = new ObjectListFunction() {
479 @Override
480 public Object evaluate(Object head, List<?> tail) {
481 return new FilteredGenerator(
482 IteratorToGeneratorAdapter.adapt(tail.iterator()),
483 IsGreaterThanOrEqual.instance((Comparable<?>) head)).toCollection();
484 }
485 };
486
487
488
489
490
491
492 public void testHeadFunction() {
493 List<String> list = new ArrayList<String>();
494 try {
495 head.evaluate(list);
496 fail("Expected IndexOutOfBoundsException when evaluating head of an empty list");
497 } catch(IndexOutOfBoundsException e) {
498
499 }
500
501 list.add("First");
502 assertEquals("First",head.evaluate(list));
503
504 list.add("Second");
505 assertEquals("First",head.evaluate(list));
506
507 }
508
509 public void testTailFunction() {
510 List<String> list = new ArrayList<String>();
511 {
512 List<String> result = (List<String>)(tail.evaluate(list));
513 assertTrue("Tail of an empty list is empty.",result.isEmpty());
514 }
515 list.add("First");
516 {
517 List<String> result = (List<String>)(tail.evaluate(list));
518 assertTrue("Tail of a one element list is empty.",result.isEmpty());
519 }
520 list.add("Second");
521 {
522 List<String> result = (List<String>)(tail.evaluate(list));
523 assertEquals("Tail of a two element list has one element.",1,result.size());
524 assertEquals("Second",result.get(0));
525 }
526 list.add("Third");
527 {
528 List<String> result = (List<String>)(tail.evaluate(list));
529 assertEquals("Tail of a three element list has two elements.",2,result.size());
530 assertEquals("Second",result.get(0));
531 assertEquals("Third",result.get(1));
532 }
533 }
534
535 public void testLesserTail() {
536 List<Integer> list = new ArrayList<Integer>();
537 for (int i=0;i<10;i++) {
538 list.add(Integer.valueOf(i));
539 }
540 for (int i=0;i<10;i++) {
541 Integer val = Integer.valueOf(i);
542 List<Integer> lesser = (List<Integer>) lesserTail.evaluate(val,list);
543 assertEquals(i,lesser.size());
544 for (int j=0;j<i;j++) {
545 assertEquals(Integer.valueOf(j),lesser.get(j));
546 }
547 }
548 }
549
550 public void testGreaterTail() {
551 List<Integer> list = new ArrayList<Integer>();
552 for (int i=0;i<10;i++) {
553 list.add(Integer.valueOf(i));
554 }
555 for (int i=0;i<10;i++) {
556 Integer val = Integer.valueOf(i);
557 List<Integer> greater = (List<Integer>) greaterTail.evaluate(val,list);
558 assertEquals(10-i,greater.size());
559 for (int j=i;j<10;j++) {
560 assertEquals(Integer.valueOf(j),greater.get(j-i));
561 }
562 }
563 }
564 }