2
;;;; Author: Paul Dietz
3
;;;; Created: Fri Jun 6 18:15:50 2003
4
;;;; Contains: Generator class and associated generic function definitions
8
(compile-and-load "rctest-util.lsp")
10
(defvar *prototype-class-table* (make-hash-table)
11
"Contains a map from names of classes to prototype instances
14
(defgeneric prototype (class)
15
;; Map a class to a prototype instance of the class. Cache using
16
;; *prototype-class-table*.
17
(:method ((class standard-class) &aux (name (class-name class)))
18
(or (gethash name *prototype-class-table*)
19
(setf (gethash name *prototype-class-table*)
20
(make-instance class))))
21
(:method ((class symbol))
22
(prototype (find-class class))))
24
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
26
;;; Generators are objects that are used to create random instances.
28
(defclass generator () ())
30
(defclass composite-generator (generator)
31
((subgenerators :type array :initform (make-array '(10)
34
(cumulative-weights :type array
35
:initform (make-array '(10)
38
:element-type 'single-float
39
:initial-element 0.0f0))
42
(defclass simple-generator (generator) ())
44
(defgeneric generate (gen size &rest ctxt &key &allow-other-keys)
46
((gen composite-generator) (size real) &rest ctxt)
47
(let* ((subgens (slot-value gen 'subgenerators))
48
(n (fill-pointer subgens)))
49
(when (<= n 0) (return-from generate (values nil nil)))
50
(let* ((cum-weights (slot-value gen 'cumulative-weights))
51
(total-weight (aref cum-weights (1- n)))
52
(random-weight (random total-weight))
53
;; Replace POSITION call with a binary search if necessary
54
(index (position random-weight cum-weights :test #'>=)))
55
(loop for i from 1 to 10
56
do (multiple-value-bind (val success?)
57
(apply #'generate (aref subgens index) size ctxt)
58
(when success? (return (values val t))))
59
finally (return (values nil nil))))))
62
(defmethod generate ((gen symbol) size &rest ctxt &key &allow-other-keys)
63
(apply #'generate (prototype gen) size ctxt))
65
(defgeneric add-subgenerator (gen subgen weight)
67
((gen composite-generator) (subgen generator) weight)
68
(let* ((subgens (slot-value gen 'subgenerators))
69
(n (fill-pointer subgens))
70
(cum-weights (slot-value gen 'cumulative-weights))
71
(total-weight (if (> n 0) (aref cum-weights (1- n)) 0.0f0)))
72
(vector-push-extend gen subgens n)
73
(vector-push-extend (+ total-weight weight) cum-weights n)
76
(defclass iterative-generator (generator)
77
((subgenerator :initarg :sub)))
79
(defclass random-iterative-generator (iterative-generator) ())
81
(defmethod generate ((gen random-iterative-generator) size &rest ctxt)
84
(let ((subgen (slot-value gen 'subgenerator))
85
(subsizes (randomly-partition (1- size) (min (isqrt size) 10))))
86
(loop for subsize in subsizes
87
for (element success) = (multiple-value-list
88
(apply #'generate subgen subsize ctxt))
89
when success collect element))))
91
;;; Macro for defining simple generator objects
92
;;; BODY is the body of the method with arguments (gen ctxt size)
93
;;; for computing the result. Inside the body the function FAIL causes
94
;;; the generator to return (nil nil).
96
(defmacro defgenerator (name &key
99
(superclass 'simple-generator)
101
(let ((rtag (gensym)))
102
(unless (listp keys) (setf keys (list keys)))
104
(defclass ,name (,superclass) ,slots)
105
(defmethod generate ((gen ,name) (size real) &rest ctxt &key ,@keys)
106
(declare (ignorable gen size ctxt))
108
(flet ((fail () (return-from ,rtag (values nil nil))))