-(defmacro defgenerator (name arguments &body body)
- `(defun ,name ,arguments
- (lambda () ,@body)))
-
-(defgenerator gen-integer (&key (max (1+ most-positive-fixnum))
- (min (1- most-negative-fixnum)))
- (+ min (random (1+ (- max min)))))
-
-(defgenerator gen-character (&key (code (gen-integer :min 0 :max (1- char-code-limit)))
- (alphanumericp nil))
- (if alphanumericp
- (code-char (funcall code))
- (loop
- for char = (code-char (funcall code))
- until (alphanumericp char)
- finally (return char))))
-
-(defun gen-string (&key
- (length (gen-integer :min 0 :max 80))
- (elements (gen-character))
- (element-type 'character))
+(defun gen-integer (&key (max (1+ most-positive-fixnum))
+ (min (1- most-negative-fixnum)))
+ "Returns a generator which produces random integers greater
+than or equal to MIN and less than or equal to MIN."
+ (lambda ()
+ (+ min (random (1+ (- max min))))))
+
+(defun type-most-negative (floating-point-type)
+ (ecase floating-point-type
+ (short-float most-negative-short-float)
+ (single-float most-negative-single-float)
+ (double-float most-negative-double-float)
+ (long-float most-negative-long-float)))
+
+(defun type-most-positive (floating-point-type)
+ (ecase floating-point-type
+ (short-float most-positive-short-float)
+ (single-float most-positive-single-float)
+ (double-float most-positive-double-float)
+ (long-float most-positive-long-float)) )
+
+(defun gen-float (&key bound (type 'short-float) min max)
+ "Returns a generator which producs floats of type TYPE.
+
+BOUND, which defaults to the most-positive value of TYPE, constrains
+the results to be in the range (-BOUND, BOUND).
+
+MIN and MAX, if supplied, cause the returned float to be within the
+floating point interval (MIN, MAX). It is the caller's responsibility
+to ensure that the range between MIN and MAX is less than the
+requested type's maximum interval. MIN defaults to 0.0 (when only MAX
+is supplied), MAX defaults to MOST-POSITIVE-<TYPE> (when only MIN is
+supplied). This peculiar calling convention is designed for the common
+case of generating positive values below a known limit.
+
+NOTE: Since GEN-FLOAT is built on CL:RANDOM the distribution of
+returned values will be continuous, not discrete. In other words: the
+values will be evenly distributed across the specified numeric range,
+the distribution of possible floating point values, when seen as a
+sequence of bits, will not be even."
+ (lambda ()
+ (flet ((rand (limit) (random (coerce limit type))))
+ (when (and bound (or min max))
+ (error "GET-FLOAT does not support specifying :BOUND and :MAX/:MIN."))
+ (if (or min max)
+ (handler-bind ((arithmetic-error (lambda (c)
+ (error "ERROR ~S occured when attempting to generate a random value between ~S and ~S." c min max))))
+ (setf min (or min 0)
+ max (or max (type-most-positive type)))
+ (+ min (rand (- max min))))
+ (let ((min (if bound bound (- (type-most-negative type))))
+ (max (if bound bound (type-most-positive type))))
+ (ecase (random 2)
+ (0 ;; generate a positive number
+ (rand max))
+ (1 ;; generate a negative number NB: min is actually
+ ;; positive. see the if statement above.
+ (- (rand min)))))))))
+
+(defun gen-character (&key (code-limit char-code-limit)
+ (code (gen-integer :min 0 :max (1- code-limit)))
+ (alphanumericp nil))
+ "Returns a generator of characters.
+
+CODE must be a generator of random integers. ALPHANUMERICP, if
+non-NIL, limits the returned chars to those which pass
+alphanumericp."
+ (lambda ()
+ (loop
+ for count upfrom 0
+ for char = (code-char (funcall code))
+ until (and char
+ (or (not alphanumericp)
+ (alphanumericp char)))
+ when (= 1000 count)
+ do (error "After 1000 iterations ~S has still not generated ~:[a valid~;an alphanumeric~] character :(."
+ code alphanumericp)
+ finally (return char))))
+
+(defun gen-string (&key (length (gen-integer :min 0 :max 80))
+ (elements (gen-character))
+ (element-type 'character))
+ "Returns a generator which producs random strings. LENGTH must
+be a generator which producs integers, ELEMENTS must be a
+generator which produces characters of type ELEMENT-TYPE."