1; QuickCheck clone
2
3(define-record-type :generator
4  (make-generator proc)
5  generator?
6  ;; int(size) random-generator -> val
7  (proc generator-proc))
8
9; for transliteration from Haskell
10(define (return val)
11  (make-generator
12   (lambda (size rgen)
13     val)))
14
15(define (>>= m1 k)
16  (let ((proc1 (generator-proc m1)))
17    (make-generator
18     (lambda (size rgen)
19       (call-with-values
20	   (lambda ()
21	     (random-generator-split rgen))
22	 (lambda (rgen1 rgen2)
23	   (let ((gen (k (proc1 size rgen1))))
24	     ((generator-proc gen) size rgen2))))))))
25
26(define (sequence gens)
27  (if (null? gens)
28      (return '())
29      (>>= (car gens)
30	   (lambda (val)
31	     (>>= (sequence (cdr gens))
32		  (lambda (rest)
33		    (return (cons val rest))))))))
34
35; for export
36(define generator-unit return)
37(define generator-bind >>=)
38(define generator-sequence sequence)
39
40(define (lift->generator proc . gens)
41  (>>= (sequence gens)
42       (lambda (vals)
43	 (return (apply proc vals)))))
44
45; [lower, upper]
46(define (choose-integer lower upper)
47  (make-generator
48   (lambda (size rgen)
49     (call-with-values
50	 (lambda ()
51	   (random-integer rgen lower upper))
52       (lambda (n _)
53	 n)))))
54
55(define (choose-real lower upper)
56  (make-generator
57   (lambda (size rgen)
58     (call-with-values
59	 (lambda ()
60	   (random-real rgen lower upper))
61       (lambda (n _)
62	 n)))))
63
64(define choose-ascii-char
65  (lift->generator integer->char (choose-integer 0 127)))
66
67(define choose-ascii-letter
68  (lift->generator (lambda (i)
69		     (string-ref
70		      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" i))
71		   (choose-integer 0 51)))
72
73(define choose-printable-ascii-char
74  (lift->generator integer->char (choose-integer 32 127)))
75
76(define max-scalar-value #x10FFFF)
77(define gap-start #xD800)
78(define gap-end #xE000)
79(define gap-size (- gap-end gap-start))
80
81(define (choose-char lower upper)
82  (make-generator
83   (lambda (size rgen)
84     (call-with-values
85	 (lambda ()
86	   (random-integer rgen (char->integer lower)
87			   (min (char->integer upper)
88				(- max-scalar-value gap-size))))
89       (lambda (n _)
90	 (integer->char
91	  (if (< n gap-start)
92	      n
93	      (+ n gap-size))))))))
94
95; int (generator a) -> (generator a)
96(define (variant v gen)
97  (let ((proc (generator-proc gen)))
98    (make-generator
99     (lambda (size rgen)
100       (let loop ((v (+ 1 v))
101		  (rgen rgen))
102	 (if (zero? v)
103	     (proc size rgen)
104	     (call-with-values
105		 (lambda ()
106		   (random-generator-split rgen))
107	       (lambda (rgen1 rgen2)
108		 (loop (- v 1) rgen2)))))))))
109
110; int random-gen (generator a) -> a
111(define (generate n rgen gen)
112  (call-with-values
113      (lambda ()
114	(random-integer rgen 0 n))
115    (lambda (size nrgen)
116      ((generator-proc gen) size nrgen))))
117
118; (vals -> (generator b)) -> (generator (vals -> b))
119(define (promote proc)
120  (make-generator
121   (lambda (size rgen)
122     (lambda vals
123       (let ((g (apply proc vals)))
124	 ((generator-proc g) size rgen))))))
125
126; (int -> (generator a)) -> (generator a)
127(define (sized proc)
128  (make-generator
129   (lambda (size rgen)
130     (let ((g (proc size)))
131       ((generator-proc g) size rgen)))))
132
133; (list a) -> (generator a)
134(define (choose-one-of lis)
135  (lift->generator (lambda (n)
136		     (list-ref lis n))
137		   (choose-integer 0 (- (length lis) 1))))
138
139; vector from the paper
140; (generator a) int -> (generator (list a))
141(define (choose-list el-gen n)
142  (let recur ((n n))
143    (if (zero? n)
144	(return '())
145	(>>= el-gen
146	     (lambda (val)
147	       (>>= (recur (- n 1))
148		    (lambda (rest)
149		      (return (cons val rest)))))))))
150
151; (generator char) int -> (generator string)
152(define (choose-string char-gen n)
153  (lift->generator list->string (choose-list char-gen n)))
154
155(define (choose-symbol char-gen n)
156  (>>= (choose-string char-gen n)
157       (lambda (s)
158	 (return (string->symbol s)))))
159
160(define (choose-vector el-gen n)
161  (lift->generator list->vector (choose-list el-gen n)))
162
163; (list (promise (generator a))) -> (generator a)
164(define (choose-mixed gens)
165  (>>= (choose-one-of gens)
166       force))
167
168; (list (pair int (generator a))) -> (generator a)
169(define (choose-with-frequencies lis)
170  (>>= (choose-integer 1 (apply + (map car lis)))
171       (lambda (n)
172	 (pick n lis))))
173
174(define (pick n lis)
175  (let ((k (caar lis)))
176    (if (<= n k)
177	(cdar lis)
178	(pick (- n k) (cdr lis)))))
179
180(define-record-type :arbitrary
181  (make-arbitrary generator transformer)
182  arbitrary?
183  ;; (generator a)
184  (generator arbitrary-generator)
185  ;; a (generator b) -> (generator b)
186  (transformer arbitrary-transformer))
187
188; class Arbitrary a where
189;    arbitrary   :: Gen a
190;    coarbitrary :: a -> Gen b -> Gen b
191
192(define (coarbitrary arb val gen)
193  ((arbitrary-transformer arb) val gen))
194
195(define arbitrary-boolean
196  (make-arbitrary (choose-one-of '(#t #f))
197		  (lambda (a gen)
198		    (variant (if a 0 1) gen))))
199
200(define arbitrary-integer
201  (make-arbitrary (sized
202		   (lambda (n)
203		     (choose-integer (- n) n)))
204		  (lambda (n gen)
205		    (variant (if (>= n 0)
206				 (* 2 n)
207				 (+ (* 2 (- n)) 1))
208			     gen))))
209
210(define (arbitrary-integer-from-to lower upper)
211  (make-arbitrary (choose-integer lower upper)
212		  (lambda (n gen)
213		    (variant (if (>= n 0)
214				 (* 2 n)
215				 (+ (* 2 (- n)) 1))
216			     gen))))
217
218(define arbitrary-natural
219  (make-arbitrary (sized
220		   (lambda (n)
221		     (choose-integer 0 n)))
222		  (lambda (n gen)
223		    (variant n gen))))
224
225(define arbitrary-ascii-char
226  (make-arbitrary choose-ascii-char
227		  (lambda (ch gen)
228		    (variant (char->integer ch) gen))))
229
230(define arbitrary-ascii-letter
231  (make-arbitrary choose-ascii-letter
232		  (lambda (ch gen)
233		    (variant (char->integer ch) gen))))
234
235(define arbitrary-printable-ascii-char
236  (make-arbitrary choose-printable-ascii-char
237		  (lambda (ch gen)
238		    (variant (char->integer ch) gen))))
239
240(define arbitrary-char
241  (make-arbitrary (sized
242		   (lambda (n)
243		     (choose-char (integer->char 0)
244				  (integer->char n))))
245		  (lambda (ch gen)
246		    (variant (char->integer ch) gen))))
247
248(define (make-rational a b c)
249  (+ a
250     (/ a
251	(+ (abs c) 1))))
252
253(define arbitrary-rational
254  (make-arbitrary (lift->generator make-rational
255				   (arbitrary-generator arbitrary-integer)
256				   (arbitrary-generator arbitrary-integer)
257				   (arbitrary-generator arbitrary-integer))
258		  (lambda (r gen)
259		    (coarbitrary arbitrary-integer
260				 (numerator r)
261				 (coarbitrary arbitrary-integer
262					      (denominator r) gen)))))
263(define (fraction a b c)
264  (+ a
265     (exact->inexact (/ b
266			(+ (abs c) 1)))))
267
268(define arbitrary-real
269  (make-arbitrary (choose-with-frequencies
270		   (list
271		    (cons 5 (sized
272			     (lambda (n)
273			       (choose-integer (- n) n))))
274		    (cons 4 (lift->generator make-rational
275					     (arbitrary-generator arbitrary-integer)
276					     (arbitrary-generator arbitrary-integer)
277					     (arbitrary-generator arbitrary-integer)))
278		    (cons 1 (lift->generator fraction
279					     (arbitrary-generator arbitrary-integer)
280					     (arbitrary-generator arbitrary-integer)
281					     (arbitrary-generator arbitrary-integer)))))
282		  (lambda (r gen)
283		    (let ((fr (rationalize (inexact->exact r) 1/1000)))
284		      (coarbitrary arbitrary-integer
285				   (numerator fr)
286				   (coarbitrary arbitrary-integer
287						(denominator fr) gen))))))
288
289
290(define (arbitrary-mixed pred+arbitrary-promise-list)
291  (make-arbitrary (choose-mixed (map (lambda (p)
292				       (delay (arbitrary-generator (force (cdr p)))))
293				     pred+arbitrary-promise-list))
294		  (lambda (val gen)
295		    (let loop ((lis pred+arbitrary-promise-list) (n 0))
296		      (cond
297		       ((null? lis)
298			(assertion-violation 'arbitrary-mixed
299					     "value matches none of the predicates"
300					     val pred+arbitrary-promise-list))
301		       (((caar lis) val)
302			(variant n gen))
303		       (else
304			(loop (cdr lis) (+ 1 n))))))))
305
306(define (arbitrary-one-of eql? . vals)
307  (make-arbitrary (choose-one-of vals)
308		  (lambda (val gen)
309		    (let loop ((lis vals) (n 0))
310		      (cond
311		       ((null? lis)
312			(assertion-violation 'arbitrary-one-of
313					     "value is not in the list"
314					     val vals))
315		       ((eql? (car lis) val)
316			(variant n gen))
317		       (else
318			(loop (cdr lis) (+ 1 n))))))))
319
320(define (arbitrary-pair arbitrary-car arbitrary-cdr)
321  (make-arbitrary (lift->generator cons
322				   (arbitrary-generator arbitrary-car)
323				   (arbitrary-generator arbitrary-cdr))
324		  (lambda (p gen)
325		    (coarbitrary arbitrary-car
326				 (car p)
327				 (coarbitrary arbitrary-cdr
328					      (cdr p) gen)))))
329
330; a tuple is just a non-uniform list
331(define (arbitrary-tuple . arbitrary-els)
332  (make-arbitrary (apply lift->generator
333			 list
334			 (map arbitrary-generator arbitrary-els))
335		  (lambda (lis gen)
336		    (let recur ((arbitrary-els arbitrary-els)
337				(lis lis))
338		      (if (null? arbitrary-els)
339			  gen
340			  ((arbitrary-transformer (car arbitrary-els))
341			   (car lis)
342			   (recur (cdr arbitrary-els)
343				  (cdr lis))))))))
344
345(define (arbitrary-record construct accessors . arbitrary-els)
346  (make-arbitrary (apply lift->generator
347			 construct
348			 (map arbitrary-generator arbitrary-els))
349		  (lambda (rec gen)
350		    (let recur ((arbitrary-els arbitrary-els)
351				(lis (map (lambda (accessor) (accessor rec)) accessors)))
352		      (if (null? arbitrary-els)
353			  gen
354			  ((arbitrary-transformer (car arbitrary-els))
355			   (car lis)
356			   (recur (cdr arbitrary-els)
357				  (cdr lis))))))))
358
359(define (arbitrary-sequence min-length choose-sequence sequence->list arbitrary-el)
360  (make-arbitrary (sized
361		   (lambda (n)
362		     (>>= (choose-integer min-length (+ n min-length))
363			  (lambda (length)
364			    (choose-sequence (arbitrary-generator arbitrary-el) length)))))
365		  (lambda (seq gen)
366		    (let recur ((lis (sequence->list seq)))
367		      (if (null? lis)
368			  (variant 0 gen)
369			  ((arbitrary-transformer arbitrary-el)
370			   (car lis)
371			   (variant 1 (recur (cdr lis)))))))))
372
373(define (arbitrary-list arbitrary-el)
374  (arbitrary-sequence 0 choose-list values arbitrary-el))
375
376(define (arbitrary-nonempty-list arbitrary-el)
377  (arbitrary-sequence 1 choose-list values arbitrary-el))
378
379(define (arbitrary-vector arbitrary-el)
380  (arbitrary-sequence 0 choose-vector vector->list arbitrary-el))
381
382(define arbitrary-ascii-string
383  (arbitrary-sequence 0 choose-string string->list arbitrary-ascii-char))
384
385(define arbitrary-printable-ascii-string
386  (arbitrary-sequence 0 choose-string string->list arbitrary-printable-ascii-char))
387
388(define arbitrary-string
389  (arbitrary-sequence 0 choose-string string->list arbitrary-char))
390
391(define arbitrary-symbol
392  (arbitrary-sequence 0 choose-symbol
393		      (lambda (symbol)
394			(string->list (symbol->string symbol)))
395		      arbitrary-ascii-letter))
396
397(define (arbitrary-procedure arbitrary-result . arbitrary-args)
398  (let ((arbitrary-arg-tuple (apply arbitrary-tuple arbitrary-args)))
399    (make-arbitrary (promote
400		     (lambda args
401		       ((arbitrary-transformer arbitrary-arg-tuple)
402			args
403			(arbitrary-generator arbitrary-result))))
404		    (lambda (proc gen)
405		      (>>= (arbitrary-generator arbitrary-arg-tuple)
406			   (lambda (args)
407			     ((arbitrary-transformer arbitrary-result)
408			      (apply proc args)
409			      gen)))))))
410
411
412(define-record-type :property
413  (make-property proc arg-names args)
414  property?
415  (proc property-proc)
416  (arg-names property-arg-names)
417  ;; (list (union arbitrary generator))
418  (args property-args))
419
420(define-syntax property
421  (syntax-rules ()
422    ((property ((?id ?gen) ...) ?body0 ?body1 ...)
423     (make-property (lambda (?id ...)
424		      ?body0 ?body1 ...)
425		    '(?id ...)
426		    (list ?gen ...)))))
427
428(define-record-type :result
429  (make-result ok stamp arguments-list)
430  check-result?
431  ;; () = unknown, #t, #f
432  (ok result-ok)
433  (stamp result-stamp)
434  ;; (list (list (pair (union #f symbol) value)))
435  (arguments-list result-arguments-list))
436
437(define (result-with-ok res ok)
438  (make-result ok
439	       (result-stamp res)
440	       (result-arguments-list res)))
441
442(define (result-add-stamp res stamp)
443  (make-result (result-ok res)
444	       (cons stamp (result-stamp res))
445	       (result-arguments-list res)))
446
447; result (list (pair (union #f symbol) value)) -> result
448(define (result-add-arguments res args)
449  (make-result (result-ok res)
450	       (result-stamp res)
451	       (cons args (result-arguments-list res))))
452
453(define nothing
454  (make-result '() '() '()))
455
456(define exception-result
457  (make-result #f '() '()))
458
459; A testable value is one of the following:
460; - a :property object
461; - a boolean
462; - a result record
463; - a generator of a result record
464
465(define (coerce->result-generator thing)
466  (cond
467   ((property? thing)
468    (for-all/names (property-proc thing)
469		   (property-arg-names thing)
470		   (property-args thing)))
471   ((boolean? thing) (return (result-with-ok nothing thing)))
472   ((check-result? thing) (return thing))
473   ((generator? thing) thing)
474   (else
475    (assertion-violation 'coerce->result-generator
476			 "cannot be coerced to a result generator"
477			 thing))))
478
479(define (coerce->generator thing)
480  (cond
481   ((generator? thing) thing)
482   ((arbitrary? thing) (arbitrary-generator thing))
483   (else
484    (assertion-violation 'coerce->generator
485			 "cannot be coerced to a generator" thing))))
486
487(define (for-all proc . args)
488  (>>= (sequence (map coerce->generator args))
489       (lambda (args)
490	 (>>= (with-handlers ((exn:fail?
491			       (lambda (_)
492				 (return exception-result))))
493                (coerce->result-generator (apply proc args)))
494	      (lambda (res)
495		(return (result-add-arguments res
496					      (map (lambda (arg) (cons #f arg)) args))))))))
497
498(define (for-all/names proc arg-names args)
499  (>>= (sequence (map coerce->generator args))
500       (lambda (args)
501	 (>>= (with-handlers ((exn:fail?
502			       (lambda (_)
503				 (return exception-result))))
504                (coerce->result-generator (apply proc args)))
505	      (lambda (res)
506		(return (result-add-arguments res (map cons arg-names args))))))))
507
508(define-syntax ==>
509  (syntax-rules ()
510    ((==> ?bool ?prop)
511     (if ?bool
512	 ?prop
513	 (return nothing)))))
514
515(define (label str testable)
516  (>>= (coerce->result-generator testable)
517       (lambda (res)
518	 (return (result-add-stamp res str)))))
519
520(define-syntax classify
521  (syntax-rules ()
522    ((classify ?really? ?str ?testable)
523     (let ((testable ?testable))
524       (if ?really?
525	   (label ?str testable)
526	   testable)))))
527
528(define-syntax trivial
529  (syntax-rules ()
530    ((trivial ?really? ?testable)
531     (classify ?really? "trivial" ?testable))))
532
533(define (collect lbl testable)
534  (label (external-representation lbl) testable))
535
536(define (external-representation obj)
537  (let ((port (make-string-output-port)))
538    (write obj port)
539    (string-output-port-output port)))
540
541; Running the whole shebang
542
543(define-record-type :config
544  (make-config max-test max-fail size print-every)
545  config?
546  (max-test config-max-test)
547  (max-fail config-max-fail)
548  (size config-size)
549  (print-every config-print-every))
550
551(define quick
552  (make-config 100
553	       1000
554	       (lambda (n)
555		 (+ 3 (quotient n 2)))
556	       values))
557
558(define verbose
559  (make-config 100
560	       1000
561	       (lambda (n)
562		 (+ 3 (quotient n 2)))
563	       (lambda (n args)
564		 (display n)
565		 (display ":")
566		 (newline)
567		 (for-each (lambda (arg)
568			     (display arg)
569			     (newline))
570			   args))))
571
572(define (check-results config prop)
573  (let ((rgen (make-random-generator 0)))
574    (tests config (coerce->result-generator prop) rgen 0 0 '())))
575
576(define (check config prop)
577  (call-with-values
578      (lambda ()
579	(check-results config prop))
580    report-result))
581
582(define (quickcheck-results prop)
583  (check-results quick prop))
584
585(define (quickcheck prop)
586  (check quick prop))
587
588; returns three values:
589; - ntest
590; - stamps
591; - #t for success, #f for exhausted, result for failure
592
593(define (tests config gen rgen ntest nfail stamps)
594  (let loop ((rgen rgen)
595	     (ntest ntest)
596	     (nfail nfail)
597	     (stamps stamps))
598    (cond
599     ((= ntest (config-max-test config))
600      (values ntest stamps #t))
601     ((= ntest (config-max-fail config))
602      (values ntest stamps #f))
603     (else
604      (call-with-values
605	  (lambda ()
606	    (random-generator-split rgen))
607	(lambda (rgen1 rgen2)
608	  (let ((result (generate ((config-size config) ntest) rgen2 gen)))
609	    ((config-print-every config) ntest (result-arguments-list result))
610	    (case (result-ok result)
611	      ((()) (loop rgen1 ntest (+ 1 nfail) stamps))
612	      ((#t) (loop rgen1 (+ 1 ntest) nfail (cons (result-stamp result) stamps)))
613	      ((#f)
614	       (values ntest stamps result))))))))))
615
616(define (report-result ntest stamps maybe-result)
617  (case maybe-result
618    ((#t)
619     (done "OK, passed" ntest stamps))
620    ((#f)
621     (done "Arguments exhausted after" ntest stamps))
622    (else
623     (display "Falsifiable, after ")
624     (display ntest)
625     (display " tests:")
626     (newline)
627     (for-each write-arguments
628	       (result-arguments-list maybe-result)))))
629
630; (pair (union #f symbol) value)
631(define (write-argument arg)
632  (if (car arg)
633      (begin
634	(display (car arg))
635	(display " = "))
636      (values))
637  (write (cdr arg)))
638
639; (list (pair (union #f symbol) value))
640(define (write-arguments args)
641  (if (pair? args)
642      (begin
643	(write-argument (car args))
644	(for-each (lambda (arg)
645		    (display " ")
646		    (write-argument arg))
647		  (cdr args))
648	(newline))
649      (values)))
650
651(define (done mesg ntest stamps)
652  (display mesg)
653  (display " ")
654  (display ntest)
655  (display " tests")
656  (let* ((sorted (list-sort stamp<? (filter pair? stamps)))
657	 (grouped (group-sizes sorted))
658	 (sorted (list-sort (lambda (p1 p2)
659			      (< (car p1) (car p2)))
660			    grouped))
661	 (entries (map (lambda (p)
662			 (let ((n (car p))
663			       (lis (cdr p)))
664			 (string-append (number->string (quotient (* 100 n) ntest))
665					"% "
666					(intersperse ", " lis))))
667		       (reverse sorted))))
668    (cond
669     ((null? entries)
670      (display ".")
671      (newline))
672     ((null? (cdr entries))
673      (display " (")
674      (display (car entries))
675      (display ").")
676      (newline))
677     (else
678      (display ".") (newline)
679      (for-each (lambda (entry)
680		  (display entry)
681		  (display ".")
682		  (newline))
683		entries)))))
684
685(define (group-sizes lis)
686  (if (null? lis)
687      '()
688      (let loop ((current (car lis))
689		 (size 1)
690		 (lis (cdr lis))
691		 (rev '()))
692	(cond
693	 ((null? lis)
694	  (reverse (cons (cons size current) rev)))
695	 ((equal? current (car lis))
696	  (loop current (+ 1 size) (cdr lis) rev))
697	 (else
698	  (loop (car lis) 1 (cdr lis) (cons (cons size current) rev)))))))
699
700(define (stamp<? s1 s2)
701  (cond
702   ((null? s1)
703    (pair? s1))
704   ((null? s2)
705    #t)
706   ((string<? (car s1) (car s2))
707    #t)
708   ((string=? (car s1) (car s2))
709    (stamp<? (cdr s1) (cdr s2)))
710   (else #f)))
711
712
713(define (intersperse del lis)
714  (if (null? lis)
715      ""
716      (string-append (car lis)
717		     (let recur ((lis (cdr lis)))
718		       (if (null? lis)
719			   ""
720			   (string-append del
721					  (recur (cdr lis))))))))
722