| 1 | ;; Using GSL bulk data (vectors, matrices, etc.) storage. |
|---|
| 2 | ;; Liam Healy, Sun Mar 26 2006 - 16:32 |
|---|
| 3 | ;; Time-stamp: <2008-03-18 21:19:05EDT data.lisp> |
|---|
| 4 | ;; $Id$ |
|---|
| 5 | |
|---|
| 6 | (in-package :gsl) |
|---|
| 7 | |
|---|
| 8 | ;;; To do: |
|---|
| 9 | ;;; - create a CL object of class gsl-data with the right GSL |
|---|
| 10 | ;;; pointer just from a raw CL object |
|---|
| 11 | ;;; - recreate GSL object should be possible to recreate the C object |
|---|
| 12 | ;;; (need this?) |
|---|
| 13 | ;;; - master list of objects, for manual memory management? |
|---|
| 14 | |
|---|
| 15 | ;;;;**************************************************************************** |
|---|
| 16 | ;;;; Class gsl-data and generic functions |
|---|
| 17 | ;;;;**************************************************************************** |
|---|
| 18 | |
|---|
| 19 | (defclass gsl-data () |
|---|
| 20 | ((pointer :initarg :pointer :accessor pointer |
|---|
| 21 | :documentation "A C pointer to the GSL representation of the data.") |
|---|
| 22 | (storage-size :initarg :storage-size :reader storage-size) |
|---|
| 23 | (data :accessor data-cache |
|---|
| 24 | :documentation "The Lisp object corresponding to the GSL data.") |
|---|
| 25 | (cl-invalid |
|---|
| 26 | :initform t :accessor cl-invalid |
|---|
| 27 | :documentation |
|---|
| 28 | "An indication of whether the Lisp object (slot 'data) agrees with the |
|---|
| 29 | GSL C data. If NIL, they agree. If T, they disagree in an unspecified |
|---|
| 30 | way. If a list of index sets, those indices disagree and the remainder |
|---|
| 31 | are correct.")) |
|---|
| 32 | (:documentation |
|---|
| 33 | "A superclass for all GSL data storage structures, such as vector, matrix, |
|---|
| 34 | etc.")) |
|---|
| 35 | |
|---|
| 36 | (defmethod print-object ((object gsl-data) stream) |
|---|
| 37 | (print-data-object object *print-array* stream)) |
|---|
| 38 | |
|---|
| 39 | (defparameter *print-data-contents* t) |
|---|
| 40 | |
|---|
| 41 | (defun print-data-object (object contents stream) |
|---|
| 42 | "Print the data object to the stream, possibly showing contents." |
|---|
| 43 | (print-unreadable-object (object stream :type t :identity t) |
|---|
| 44 | (when (and contents *print-data-contents*) |
|---|
| 45 | (princ (data object) stream)))) |
|---|
| 46 | |
|---|
| 47 | (defgeneric gsl-array (object) |
|---|
| 48 | (:documentation "A pointer to the GSL array with the data contents.")) |
|---|
| 49 | |
|---|
| 50 | (defun dim0 (object) |
|---|
| 51 | "The first dimension of the object." |
|---|
| 52 | (first (storage-size object))) |
|---|
| 53 | |
|---|
| 54 | (defun dim1 (object) |
|---|
| 55 | "The second dimension of the object." |
|---|
| 56 | (second (storage-size object))) |
|---|
| 57 | |
|---|
| 58 | (defgeneric cl-elt-type (object) |
|---|
| 59 | (:documentation "The CL type of an element.")) |
|---|
| 60 | |
|---|
| 61 | ;;; Accessing elements |
|---|
| 62 | (export 'maref) |
|---|
| 63 | (defgeneric maref (object &rest indices) |
|---|
| 64 | (:documentation "An element of the data.")) |
|---|
| 65 | |
|---|
| 66 | (defgeneric (setf maref) (value object &rest indices) |
|---|
| 67 | (:method :after (value (object gsl-data) &rest indices) |
|---|
| 68 | (push indices (cl-invalid object))) |
|---|
| 69 | (:documentation "Set an element of the data.")) |
|---|
| 70 | |
|---|
| 71 | (export '(write-binary read-binary write-formatted read-formatted)) |
|---|
| 72 | (defgeneric write-binary (object stream) |
|---|
| 73 | (:documentation "Write the binary GSL data.")) |
|---|
| 74 | |
|---|
| 75 | (defgeneric read-binary (object stream) |
|---|
| 76 | (:documentation "Read the binary GSL data.")) |
|---|
| 77 | |
|---|
| 78 | (defgeneric write-formatted (object stream format) |
|---|
| 79 | (:documentation "Write the formatted GSL data.")) |
|---|
| 80 | |
|---|
| 81 | (defgeneric read-formatted (object stream format) |
|---|
| 82 | (:documentation "Read the formatted GSL data.")) |
|---|
| 83 | |
|---|
| 84 | ;;;;**************************************************************************** |
|---|
| 85 | ;;;; Macro defdata to define class etc. |
|---|
| 86 | ;;;;**************************************************************************** |
|---|
| 87 | |
|---|
| 88 | (defun assign-pointer (object pointer) |
|---|
| 89 | "Check that a GSL data pointer is not null, then assign it to the object." |
|---|
| 90 | (check-null-pointer |
|---|
| 91 | pointer |
|---|
| 92 | :ENOMEM |
|---|
| 93 | (format nil "for ~a." |
|---|
| 94 | (with-output-to-string (stream) |
|---|
| 95 | (print-data-object object nil stream)))) |
|---|
| 96 | (setf (pointer object) pointer)) |
|---|
| 97 | |
|---|
| 98 | ;;; (args (loop for i below dimensions collect (intern (format nil "I~d" i)))) |
|---|
| 99 | ;;; (mapcar (lambda (v) `(,v size)) args) |
|---|
| 100 | |
|---|
| 101 | (defmacro data-go (type matrixp) |
|---|
| 102 | "Define the letm function for data types." |
|---|
| 103 | (if matrixp ; Matrix (two indices) |
|---|
| 104 | `(defgo ,type (size-or-initial &optional size-or-zero zero) |
|---|
| 105 | (cond ((and (numberp size-or-initial) (numberp size-or-zero)) |
|---|
| 106 | ;; Array dimensions are given as literal numbers for matrix |
|---|
| 107 | (list |
|---|
| 108 | `(make-data ',',type ,zero ,size-or-initial ,size-or-zero) |
|---|
| 109 | 'free)) |
|---|
| 110 | ((typep size-or-initial 'array) |
|---|
| 111 | (let ((argsymb (gensym "ARG"))) |
|---|
| 112 | (list |
|---|
| 113 | `(apply #'make-data |
|---|
| 114 | ',',type ,size-or-zero (array-dimensions ,argsymb)) |
|---|
| 115 | 'free |
|---|
| 116 | (lambda (symb) |
|---|
| 117 | `(setf (data ,symb) ,argsymb)) |
|---|
| 118 | (lambda () (list argsymb size-or-initial))))) |
|---|
| 119 | (t |
|---|
| 120 | ;; Determine at runtime whether first arg is initial or dimension |
|---|
| 121 | (let ((argsymb (gensym "ARG"))) |
|---|
| 122 | (list |
|---|
| 123 | `(apply #'make-data ',',type nil |
|---|
| 124 | (if (and (numberp ,argsymb) (numberp ,size-or-zero)) |
|---|
| 125 | (list ,argsymb ,size-or-zero) |
|---|
| 126 | (array-dimensions ,argsymb))) |
|---|
| 127 | 'free |
|---|
| 128 | (lambda (symb) |
|---|
| 129 | `(unless (and (numberp ,argsymb) (numberp ,size-or-zero)) |
|---|
| 130 | (setf (data ,symb) ,argsymb))) |
|---|
| 131 | (lambda () (list argsymb size-or-initial))))))) |
|---|
| 132 | ;; Vector or other one-index object |
|---|
| 133 | `(defgo ,type (size-or-initial &optional zero) |
|---|
| 134 | (typecase size-or-initial |
|---|
| 135 | (number |
|---|
| 136 | ;; Size is given as literal number |
|---|
| 137 | (list |
|---|
| 138 | `(make-data ',',type ,zero ,size-or-initial) |
|---|
| 139 | 'free)) |
|---|
| 140 | (vector |
|---|
| 141 | ;; Initial value supplied as literal CL vector) |
|---|
| 142 | (let ((argsymb (gensym "ARG"))) |
|---|
| 143 | (list |
|---|
| 144 | `(make-data ',',type ,zero |
|---|
| 145 | (length ,argsymb)) |
|---|
| 146 | 'free |
|---|
| 147 | (lambda (symb) `(setf (data ,symb) ,argsymb)) |
|---|
| 148 | (lambda () (list argsymb size-or-initial))))) |
|---|
| 149 | (t |
|---|
| 150 | ;; Determine at runtime whether first arg is initial or size |
|---|
| 151 | (let ((argsymb (gensym "ARG"))) |
|---|
| 152 | (list |
|---|
| 153 | `(make-data ',',type ,zero |
|---|
| 154 | (if (numberp ,argsymb) ,argsymb (length ,argsymb))) |
|---|
| 155 | 'free |
|---|
| 156 | (lambda (symb) `(unless (numberp ,argsymb) (setf (data ,symb) ,argsymb))) |
|---|
| 157 | (lambda () (list argsymb size-or-initial))))))))) |
|---|
| 158 | |
|---|
| 159 | (defparameter *data-class-name* nil |
|---|
| 160 | "A list classes, each consisting of a list |
|---|
| 161 | superclass, CL element type, class, GSL splice name.") |
|---|
| 162 | (defmacro add-data-class (category element-type class superclass GSL-string) |
|---|
| 163 | `(eval-when (:compile-toplevel :load-toplevel :execute) |
|---|
| 164 | (pushnew (list ',category ',element-type ',class ',superclass ',GSL-string) |
|---|
| 165 | *data-class-name* |
|---|
| 166 | :test #'equal))) |
|---|
| 167 | (defun data-type-lookup (category element-type) |
|---|
| 168 | (find (list category element-type) |
|---|
| 169 | *data-class-name* |
|---|
| 170 | :key (lambda (l) (subseq l 0 2)) |
|---|
| 171 | :test #'equal)) |
|---|
| 172 | (defun data-class-name (category element-type) |
|---|
| 173 | (third (data-type-lookup category element-type))) |
|---|
| 174 | (defun data-superclass-name (category element-type) |
|---|
| 175 | (fourth (data-type-lookup category element-type))) |
|---|
| 176 | (defun data-gsl-string (category element-type) |
|---|
| 177 | (fifth (data-type-lookup category element-type))) |
|---|
| 178 | |
|---|
| 179 | (defmacro defdata (category cl-elt-type &optional (dimensions 1) splice-name) |
|---|
| 180 | "For the type named in the string, |
|---|
| 181 | define the allocator (gsl-*-alloc), zero allocator (gsl-*-calloc), |
|---|
| 182 | freeing (gsl-*-free), binary writing (binary-*-write) and |
|---|
| 183 | reading (binary-*-read), formatted writing (write-*-formatted) |
|---|
| 184 | and reading (read-*-formatted) functions." |
|---|
| 185 | (flet ((gsl-name (function-name) |
|---|
| 186 | (format nil "gsl_~a~a_~a" |
|---|
| 187 | (data-gsl-string category cl-elt-type) |
|---|
| 188 | (or splice-name |
|---|
| 189 | (lookup-splice-name cl-elt-type)) function-name))) |
|---|
| 190 | (let* ((cargs (loop for i below dimensions |
|---|
| 191 | collect `((nth ,i (storage-size object)) size))) |
|---|
| 192 | (class-name (data-class-name category cl-elt-type))) |
|---|
| 193 | `(progn |
|---|
| 194 | (defclass ,class-name (,(data-superclass-name category cl-elt-type)) |
|---|
| 195 | ((cl-elt-type :initform ',cl-elt-type :reader cl-elt-type |
|---|
| 196 | :allocation :class))) |
|---|
| 197 | (data-go ,class-name |
|---|
| 198 | ,(or (member category '(matrix combination)))) |
|---|
| 199 | (defmfun alloc ((object ,class-name)) |
|---|
| 200 | ,(gsl-name "alloc") ,cargs |
|---|
| 201 | :type :method |
|---|
| 202 | :c-return (cr :pointer) |
|---|
| 203 | :return ((assign-pointer object cr))) |
|---|
| 204 | (defmfun calloc ((object ,class-name)) |
|---|
| 205 | ,(gsl-name "calloc") ,cargs |
|---|
| 206 | :type :method |
|---|
| 207 | :c-return (cr :pointer) |
|---|
| 208 | :return ((assign-pointer object cr))) |
|---|
| 209 | (defmfun free ((object ,class-name)) |
|---|
| 210 | ,(gsl-name "free") (((pointer object) :pointer)) |
|---|
| 211 | :type :method |
|---|
| 212 | :c-return :void) |
|---|
| 213 | (defmfun write-binary ((object ,class-name) stream) |
|---|
| 214 | ,(gsl-name "fwrite") ((stream :pointer) ((pointer object) :pointer)) |
|---|
| 215 | :type :method) |
|---|
| 216 | (defmfun read-binary ((object ,class-name) stream) |
|---|
| 217 | ,(gsl-name "fread") ((stream :pointer) ((pointer object) :pointer)) |
|---|
| 218 | :type :method) |
|---|
| 219 | (defmfun write-formatted ((object ,class-name) stream format) |
|---|
| 220 | ,(gsl-name "fprintf") |
|---|
| 221 | ((stream :pointer) ((pointer object) :pointer) (format :string)) |
|---|
| 222 | :type :method) |
|---|
| 223 | (defmfun read-formatted ((object ,class-name) stream format) |
|---|
| 224 | ,(gsl-name "fscanf") |
|---|
| 225 | ((stream :pointer) ((pointer object) :pointer) (format :string)) |
|---|
| 226 | :type :method))))) |
|---|
| 227 | |
|---|
| 228 | (defun defmfun-all (category cl-types args &optional key-string) |
|---|
| 229 | "A defmfun for each of the declared data types." |
|---|
| 230 | (let ((categories (if (listp category) category (list category)))) |
|---|
| 231 | `(progn |
|---|
| 232 | ,@(loop for type in cl-types |
|---|
| 233 | for ctype = (lookup-C-type type) |
|---|
| 234 | collect |
|---|
| 235 | `(defmfun ,(first args) |
|---|
| 236 | ;; Set the class name for the arglist |
|---|
| 237 | ,(mapcar |
|---|
| 238 | (lambda (x) |
|---|
| 239 | (if (and (listp x) (member (second x) categories)) |
|---|
| 240 | (list (first x) |
|---|
| 241 | (data-class-name (second x) type)) |
|---|
| 242 | x)) |
|---|
| 243 | (second args)) |
|---|
| 244 | ;; Create the correct GSL C library function name |
|---|
| 245 | ,(splice-name |
|---|
| 246 | (third args) |
|---|
| 247 | ;; Use key-string to override the lookup of the category. |
|---|
| 248 | (or key-string (data-gsl-string (first categories) type)) |
|---|
| 249 | type) |
|---|
| 250 | ,(mapcar |
|---|
| 251 | (lambda (x) |
|---|
| 252 | (if (eq (st-type x) :c-base-type) |
|---|
| 253 | (list (st-symbol x) ctype) |
|---|
| 254 | x)) |
|---|
| 255 | (fourth args)) |
|---|
| 256 | ,@(let ((restargs (copy-list (nthcdr 4 args)))) |
|---|
| 257 | (when (eq (getf restargs :c-return) :c-base-type) |
|---|
| 258 | (setf (getf restargs :c-return) ctype)) |
|---|
| 259 | (setf (getf restargs :type) :method) |
|---|
| 260 | restargs)))))) |
|---|
| 261 | |
|---|
| 262 | ;;;;**************************************************************************** |
|---|
| 263 | ;;;; Making data objects and initializing storage |
|---|
| 264 | ;;;;**************************************************************************** |
|---|
| 265 | |
|---|
| 266 | (export '(make-data)) |
|---|
| 267 | |
|---|
| 268 | (defgeneric alloc (object) |
|---|
| 269 | (:documentation "Allocate GSL data; used internally.")) |
|---|
| 270 | |
|---|
| 271 | (defgeneric calloc (object) |
|---|
| 272 | (:documentation "Allocate GSL data and clear; used internally.")) |
|---|
| 273 | |
|---|
| 274 | (export 'free) |
|---|
| 275 | (defgeneric free (object) |
|---|
| 276 | (:documentation "Free GSL data.") |
|---|
| 277 | (:method :after ((object gsl-data)) (setf (pointer object) nil))) |
|---|
| 278 | |
|---|
| 279 | (defun make-data (type zero &rest size) |
|---|
| 280 | "Make the GSL data object, including the allocation of space. |
|---|
| 281 | The user is responsible for calling #'free to free the foreign |
|---|
| 282 | memory when done." |
|---|
| 283 | (let ((obj (make-instance type :storage-size size))) |
|---|
| 284 | (if zero (calloc obj) (alloc obj)) |
|---|
| 285 | obj)) |
|---|
| 286 | |
|---|
| 287 | ;;;;**************************************************************************** |
|---|
| 288 | ;;;; Getting values into CL |
|---|
| 289 | ;;;;**************************************************************************** |
|---|
| 290 | |
|---|
| 291 | (defun cl-invalidate (&rest objects) |
|---|
| 292 | "Mark the CL image of the GSL array as invalid." |
|---|
| 293 | (mapc (lambda (obj) (setf (cl-invalid obj) t)) |
|---|
| 294 | objects)) |
|---|
| 295 | |
|---|
| 296 | (export 'data) |
|---|
| 297 | (defgeneric data (object &optional destination) |
|---|
| 298 | (:documentation "Extract the values in the object to a CL object. |
|---|
| 299 | The destination may be a sequence, 'vector, 'list. |
|---|
| 300 | If it is a sequence, that object is filled with the values. |
|---|
| 301 | If it is any other object, a new sequence is made of the type |
|---|
| 302 | specified.") |
|---|
| 303 | ;; Default method is to make a sequence |
|---|
| 304 | (:method ((object gsl-data) &optional (sequence 'vector)) |
|---|
| 305 | (if (eq (cl-invalid object) t) |
|---|
| 306 | ;; set everything |
|---|
| 307 | (let* ((total-size (apply #'* (storage-size object))) |
|---|
| 308 | (seq |
|---|
| 309 | (case sequence |
|---|
| 310 | (list (make-list total-size)) |
|---|
| 311 | ((nil vector) |
|---|
| 312 | (make-array (list total-size) |
|---|
| 313 | :element-type (cl-elt-type object))) |
|---|
| 314 | (t sequence)))) |
|---|
| 315 | (loop for i from 0 |
|---|
| 316 | below (min (length seq) total-size) |
|---|
| 317 | do (setf (elt seq i) (maref object i))) |
|---|
| 318 | seq) |
|---|
| 319 | ;; set selected |
|---|
| 320 | (let ((seq (data-cache object))) |
|---|
| 321 | (mapc (lambda (is) |
|---|
| 322 | (let ((i (first is))) |
|---|
| 323 | (setf (elt seq i) (maref object i)))) |
|---|
| 324 | (cl-invalid object)) |
|---|
| 325 | seq))) |
|---|
| 326 | ;; Around method looks for cached value and returns it if valid; |
|---|
| 327 | ;; otherwise computes CL element(s). |
|---|
| 328 | (:method :around ((object gsl-data) &optional (sequence 'vector)) |
|---|
| 329 | (declare (ignore sequence)) |
|---|
| 330 | (when (cl-invalid object) |
|---|
| 331 | (setf (data-cache object) |
|---|
| 332 | (call-next-method) |
|---|
| 333 | (cl-invalid object) |
|---|
| 334 | nil)) |
|---|
| 335 | (data-cache object))) |
|---|
| 336 | |
|---|
| 337 | ;;;;**************************************************************************** |
|---|
| 338 | ;;;; Setting values from CL |
|---|
| 339 | ;;;;**************************************************************************** |
|---|
| 340 | |
|---|
| 341 | (defgeneric (setf data) (cl-array object) |
|---|
| 342 | (:documentation "Set the values in the object from a CL array.") |
|---|
| 343 | ;; Default method is to read from a sequence |
|---|
| 344 | (:method (sequence (object gsl-data)) |
|---|
| 345 | (loop for i from 0 |
|---|
| 346 | below (min (length sequence) (apply #'* (storage-size object))) |
|---|
| 347 | do (setf (maref object i) (elt sequence i)))) |
|---|
| 348 | (:method :after (source (object gsl-data)) |
|---|
| 349 | (setf (cl-invalid object) t))) |
|---|
| 350 | |
|---|
| 351 | (export 'set-all) |
|---|
| 352 | (defgeneric set-all (object value) |
|---|
| 353 | (:documentation "Set all elements to the value.") |
|---|
| 354 | (:method :after ((object gsl-data) value) (cl-invalidate object))) |
|---|
| 355 | |
|---|
| 356 | (export 'set-zero) |
|---|
| 357 | (defgeneric set-zero (object) |
|---|
| 358 | (:documentation "Set all elements to 0.") |
|---|
| 359 | (:method :after ((object gsl-data)) (cl-invalidate object))) |
|---|
| 360 | |
|---|
| 361 | (export 'set-identity) |
|---|
| 362 | (defgeneric set-identity (object) |
|---|
| 363 | (:documentation "Set elements to represent the identity.") |
|---|
| 364 | (:method :after ((object gsl-data)) (cl-invalidate object))) |
|---|
| 365 | |
|---|
| 366 | (export 'set-basis) |
|---|
| 367 | (defgeneric set-basis (object index) |
|---|
| 368 | (:documentation "Set indexth basis vector.")) |
|---|
| 369 | |
|---|
| 370 | (export 'data-valid) |
|---|
| 371 | (defgeneric data-valid (object) |
|---|
| 372 | (:documentation "Validate the values in the object.")) |
|---|
| 373 | |
|---|
| 374 | ;;;;**************************************************************************** |
|---|
| 375 | ;;;; Copying |
|---|
| 376 | ;;;;**************************************************************************** |
|---|
| 377 | |
|---|
| 378 | (export '(copy swap)) |
|---|
| 379 | (defgeneric copy (destination source) |
|---|
| 380 | (:documentation "Copy from source to destination.")) |
|---|
| 381 | |
|---|
| 382 | (defgeneric swap (obj1 obj2) |
|---|
| 383 | (:documentation "Swap contents of obj1 and obj2.")) |
|---|
| 384 | |
|---|
| 385 | ;;;;**************************************************************************** |
|---|
| 386 | ;;;; Arithmetic operations |
|---|
| 387 | ;;;;**************************************************************************** |
|---|
| 388 | |
|---|
| 389 | (export '(m+ m- m* m/ m*c m+c)) |
|---|
| 390 | |
|---|
| 391 | (defgeneric m+ (a b) |
|---|
| 392 | (:documentation "Add.")) |
|---|
| 393 | |
|---|
| 394 | (defgeneric m- (a b) |
|---|
| 395 | (:documentation "Subtract.")) |
|---|
| 396 | |
|---|
| 397 | (defgeneric m* (a b) |
|---|
| 398 | (:documentation "Multiply.")) |
|---|
| 399 | |
|---|
| 400 | (defgeneric m/ (a b) |
|---|
| 401 | (:documentation "Divide.")) |
|---|
| 402 | |
|---|
| 403 | (defgeneric m+c (a x) |
|---|
| 404 | (:documentation "Add scalar.")) |
|---|
| 405 | |
|---|
| 406 | (defgeneric m*c (a x) |
|---|
| 407 | (:documentation "Multiply by scalar.")) |
|---|
| 408 | |
|---|
| 409 | ;;;;**************************************************************************** |
|---|
| 410 | ;;;; Maximum and minimum elements |
|---|
| 411 | ;;;;**************************************************************************** |
|---|
| 412 | |
|---|
| 413 | (export |
|---|
| 414 | '(gsl-max gsl-min gsl-minmax gsl-max-index gsl-min-index gsl-minmax-index)) |
|---|
| 415 | |
|---|
| 416 | (defgeneric gsl-min (a) |
|---|
| 417 | (:documentation "Minimum.")) |
|---|
| 418 | |
|---|
| 419 | (defgeneric gsl-max (a) |
|---|
| 420 | (:documentation "Maximum.")) |
|---|
| 421 | |
|---|
| 422 | (defgeneric gsl-minmax (a) |
|---|
| 423 | (:documentation "Minimum and maximum.")) |
|---|
| 424 | |
|---|
| 425 | (defgeneric gsl-min-index (a) |
|---|
| 426 | (:documentation "Index of minimum.")) |
|---|
| 427 | |
|---|
| 428 | (defgeneric gsl-max-index (a) |
|---|
| 429 | (:documentation "Index of maximum.")) |
|---|
| 430 | |
|---|
| 431 | (defgeneric gsl-minmax-index (a) |
|---|
| 432 | (:documentation "Indices of minimum and maximum.")) |
|---|
| 433 | |
|---|
| 434 | ;;;;**************************************************************************** |
|---|
| 435 | ;;;; Properties |
|---|
| 436 | ;;;;**************************************************************************** |
|---|
| 437 | |
|---|
| 438 | (export '(gsl-zerop)) |
|---|
| 439 | |
|---|
| 440 | (defgeneric gsl-zerop (a) |
|---|
| 441 | (:documentation "Object is zero.")) |
|---|