| ;;; |
| ;;; Name: imath-test.scm |
| ;;; Purpose: Code to generate random rational number test cases. |
| ;;; Notes: Written for DrRacket (nee PLT Scheme) |
| ;;; |
| (require (lib "27.ss" "srfi")) |
| |
| ;; Generate a random natural number with the specified number of digits. |
| (define (random-big-natural digits) |
| (let loop ((d "") (digits digits)) |
| (if (zero? digits) |
| (string->number d 10) |
| (let ((rnd (random 10))) |
| (loop (string-append d (list->string |
| (list |
| (integer->char |
| (+ rnd |
| (char->integer #\0)))))) |
| (- digits 1)))))) |
| |
| ;; Generate a random integer with the specified number of digits and |
| ;; probability (0..1) of being negative. |
| (define (random-big-integer digits pneg) |
| (let ((base (random-big-natural digits))) |
| (if (< (random-real) pneg) |
| (* base -1) |
| base))) |
| |
| ;; Generate a random rational number with the specified number of numerator and |
| ;; denominator digits, and probability pneg (0..1) of being negative. |
| (define (random-big-rational n-digits d-digits pneg) |
| (let ((num (random-big-natural n-digits)) |
| (den (random-big-natural d-digits))) |
| (if (zero? den) |
| (random-big-rational n-digits d-digits pneg) |
| (if (< (random-real) pneg) |
| (- (/ num den)) |
| (/ num den))))) |
| |
| ;; Create a rational generator with a fixed negative probability. |
| ;; Always generates rationals. |
| (define (make-rat-generator prob-neg) |
| (lambda (n-digits d-digits num) |
| (random-big-rational n-digits d-digits prob-neg))) |
| |
| ;; Create a rational generator with a fixed negative probability. With |
| ;; probability prob-backref, generates a back-reference to an earlier input |
| ;; value, rather than a new value. This is used to make sure argument |
| ;; overlapping works the way it should. |
| (define (make-backref-generator prob-neg prob-backref) |
| (lambda (n-digits d-digits num) |
| (if (and (> num 1) |
| (< (random-real) prob-backref)) |
| (let ((ref (+ (random (- num 1)) 1))) |
| (string-append "=" (number->string ref))) |
| (random-big-rational n-digits d-digits prob-neg)))) |
| |
| ;; Just like make-backref-generator, except the second argument is always an |
| ;; integer, and the backreference can only be to the first argument. |
| (define (make-backref-generator-2 prob-neg prob-backref) |
| (lambda (n-digits d-digits num) |
| (case num |
| ((1) (random-big-rational n-digits d-digits prob-neg)) |
| ((2) (random-big-integer n-digits prob-neg)) |
| (else |
| (if (< (random-real) prob-backref) |
| "=1" |
| (random-big-rational n-digits d-digits prob-neg)))))) |
| |
| (define (make-output-test-generator prob-neg max-dig) |
| (lambda (n-digits d-digits num) |
| (cond ((= num 1) |
| (random-big-rational n-digits d-digits prob-neg)) |
| ((= num 2) |
| (let loop ((radishes '(10 16 8 4 2))) |
| (cond ((null? radishes) |
| (+ (random 34) 2)) |
| ((< (random-real) 0.3) |
| (car radishes)) |
| (else |
| (loop (cdr radishes)))))) |
| (else |
| (random max-dig)) |
| ))) |
| |
| ;; Given a test name, an argument generator, and an operation to compute the |
| ;; desired solution, return a function that generates a random test case for a |
| ;; given number of digits of precision in the numerator and denominator. |
| (define (make-test-case-generator name arg-gen op) |
| (lambda (n-digits d-digits) |
| (let ((args (list (arg-gen n-digits d-digits 1) |
| (arg-gen n-digits d-digits 2) |
| (arg-gen n-digits d-digits 3)))) |
| (let* ((arg1 (car args)) |
| (arg2 (if (equal? (cadr args) "=1") |
| arg1 (cadr args))) |
| (soln (if (and (eq? op /) |
| (zero? arg2)) |
| "$MP_UNDEF" |
| (op arg1 arg2)))) |
| (list |
| name |
| args |
| (list soln)))))) |
| |
| ;; Glue strings together with the specified joiner. |
| (define (join-strings joiner lst) |
| (cond ((null? lst) "") |
| ((null? (cdr lst)) (car lst)) |
| (else |
| (string-append (car lst) joiner |
| (join-strings joiner (cdr lst)))))) |
| |
| ;; Convert a test case generated by a test case generator function into a |
| ;; writable string, in the format used by imtest.c |
| (define (test-case->string tcase) |
| (let ((s (open-output-string)) |
| (stringify (lambda (v) |
| (let ((s (open-output-string))) |
| (display v s) |
| (get-output-string s))))) |
| (display (car tcase) s) |
| (display ":" s) |
| (display (join-strings "," (map stringify (cadr tcase))) |
| s) |
| (display ":" s) |
| (display (join-strings "," (map stringify (caddr tcase))) |
| s) |
| (get-output-string s))) |
| |
| (define qadd (make-test-case-generator |
| 'qadd (make-backref-generator 0.3 0.2) +)) |
| (define qsub (make-test-case-generator |
| 'qsub (make-backref-generator 0.3 0.2) -)) |
| (define qmul (make-test-case-generator |
| 'qmul (make-backref-generator 0.3 0.2) *)) |
| (define qdiv (make-test-case-generator |
| 'qdiv (make-backref-generator 0.3 0.2) /)) |
| (define qtodec (make-test-case-generator |
| 'qtodec (make-output-test-generator 0.3 25) |
| (lambda (a b) '?))) |
| (define qaddz (make-test-case-generator |
| 'qaddz (make-backref-generator-2 0.3 0.2) +)) |
| (define qsubz (make-test-case-generator |
| 'qsubz (make-backref-generator-2 0.3 0.2) -)) |
| (define qmulz (make-test-case-generator |
| 'qmulz (make-backref-generator-2 0.3 0.2) *)) |
| (define qdivz (make-test-case-generator |
| 'qdivz (make-backref-generator-2 0.3 0.2) /)) |
| |
| (define (write-test-cases test-fn lo-size hi-size num-each fname) |
| (let ((out (open-output-file fname))) |
| (do ((num lo-size (+ num 1))) |
| ((> num hi-size) (void)) |
| (do ((den hi-size (- den 1))) |
| ((< den lo-size) (void)) |
| (do ((ctr 1 (+ ctr 1))) |
| ((> ctr num-each) (void)) |
| (display (test-case->string (test-fn num den)) out) |
| (newline out)))) |
| (close-output-port out))) |
| |
| (define (write-lots-of-tests) |
| (write-test-cases qadd 1 20 2 "qadd.tc") |
| (write-test-cases qsub 1 20 2 "qsub.tc") |
| (write-test-cases qmul 1 20 2 "qmul.tc") |
| (write-test-cases qdiv 1 20 2 "qdiv.tc") |
| (write-test-cases qtodec 1 20 2 "qtodec.tc") |
| (write-test-cases qaddz 1 20 2 "qaddz.tc") |
| (write-test-cases qsubz 1 20 2 "qsubz.tc") |
| (write-test-cases qmulz 1 20 2 "qmulz.tc") |
| (write-test-cases qdivz 1 20 2 "qdivz.tc")) |