;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;; ;;;;;;;; ;;;;;; All files in this directory or any subdirectories are ;;;;;;;; ;;;;;; copyright 1997, 1998, 1999, 2000, 2002, 2003. ;;;;;;;; ;;;;;; by Rafael D. Sorkin. All rights reserved. ;;;;;;;; ;;;;;; ;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; bibliotek.elisp.coda.el Time-stamp:<2003-May-13 21:32:31 16065.40111> ;; Here we keep macros and functions for elisp that upset some TCL compilers ;; (They are here rather than in `bibliotek.elisp' so they can have access to ;; the macros defined in `bibliotek.macros') ;: Roster ;| Time and friends ;| ;| Time (prints timing and memory use info) ;| time-occupied (returns time) [in bibliotek.macros.el] ;| time-evaluation (returns elapsed time itself) ;| get-time (get current time in seconds) ;| t_0 t_1 (names before "localization")(global vbles for `time-evaluation') ;| ;| time [commented out] ;| Time-alt [commented out] ;| ;| Random ;| random-from-elisp ;| el:random (prefixed to distinguish it from one in CL-package) ;| random (now just a dummy function) ;| ;| sgn ;| ;| ratio (converts its args to float) ;| reciprocal (alias 1/) ;| ;| sinh cosh tanh ;| ;| ! (factorial of integer argument -- elisp version only, ;| TCL version is in bibliotek.general) ;| print-to-file ;| ;: Check we really are in elisp (unless *elisp* (error " Use { bibliotek.macros.elisp } only with elisp")) ;: Timing macros (localize-using-name "timer-family" (t_0 t_1 time-triple form body mem-use dt dmem z) (defvar t_0) (defvar t_1) (defun get-time () "\ Returns current time in seconds from some meaningless reference time circa 1970 Jan 1. \ " (let ((time-triple (current-time))) (+ (car time-triple) (nth 1 time-triple) (* 0.000001 (nth 2 time-triple))))) ; ; NOTE the function `nth' is a subr in elisp, hence presumably faster than ; `cadr' and `caddr' (defun time-evaluation (form) "\ SYNOPSIS (time-evaluation FORM) Timing is crude because only elapsed real time is measured -- not CPU time. Evals FORM and returns the elapsed time in seconds. (Does not collect garbage first) This is basically an auxiliary macro for `time-occupied' BEWARE On bananoid this behaved differently when enclosed in `progn'! " (setq t_0 (get-time)) (eval form) (- (get-time) t_0)) (defmacro Time (&rest body) "\ SYNOPSIS (Time FORM) A crude timer for elisp that only measures elapsed real time, not CPU time. Returns a string giving (i) the elapsed time in seconds (ii) the total number of objects (not bytes) consed as determined from `memory-use-counts' (qv). Deposits in *mrv* the full result, including a breakdown of which types of objects were consed: (conses floats vector-cells symbols string-chars miscs intervals). Collects garbage first to get a clean start. " `(let ((mem-use nil) (dt nil) (dmem nil)) (setq mem-use (memory-use-counts)) (setq dt (time-occupied ,@body)) (setq dmem (map 'list (function -) (memory-use-counts) mem-use)) (setq *mrv* (list dt (sum dmem) dmem)) (format "%0.2f [%d]" dt (sum dmem)))) ; (defmacro Time-alt (&rest body) ; " Same as `time' but collects garbage first" ; `(progn ; (garbage-collect) ; (time ; ,@body))) ; ; (defun pp-elapsed-time (z) ; " Auxiliary function for `time'. Argument should be in seconds." ; (cond ; ((< z 0) (message "Negative elapsed time of %f seconds!" z)) ; (t (princ (format "%0.3g sec elapsed (NOT CPU) tim" z)) 'e))) ; ; ; ; returning `e' is a kluge. ; ; (defmacro time (&rest body) ; "\ ; A crude timer, which measures elapsed real time (not CPU time) ; and prints the result, which it obtains from the function `time-evaluation'. ; The argument can be a single form or a series of them. ; We do NOT collect garbage first. ; (The actual return value is useless: the symbol `e'). ; " ; `(pp-elapsed-time ; (time-evaluation ; '(progn ,@body)))) ; ; ; ; The use of `progn' somehow stabilized the timing (on bananoid) ) ;: Various functions (deff sgn (x) "\ The signum function, returning -1 0 1 The argument should be a real number (including +-Infinity but EXCLUDING NaN). (For elisp, `sgn' turns out to coincide with the builtin function `signum', for TCL they differ because `signum' admits complex arguments.) \ " (cond ((> x 0) 1) ((< x 0) -1) ((= x 0) 0) (otherwise (error "Invalid argument to `sgn': %s" x)))) (defun ratio (x y) "\ Floats its arguments and then takes their ratio. This is necessary with elisp since division of integers truncates the quotient to an integer! \ " (/ (float x) (float y))) (defalias 'ratio* 'ratio) (defun reciprocal (x) " First converts to float, since elisp truncates integer division" (/ 1 (float x))) (defalias '1/ 'reciprocal) (defun sinh(x) (let ((y (exp x))) (/ (- y (reciprocal y)) 2))) (defun cosh(x) (let ((y (exp x))) (/ (+ y (reciprocal y)) 2))) (defun tanh(x) (let* ((y (exp x)) (z (reciprocal y))) (/ (- y z) (+ y z)))) (defun ! (n) "\ Factorial function for integers -- from 0 to 170 only! Converts argument to float to avoid large integer problem in elisp. \ " (assert (integerp n) nil "The function `!' takes integers only") (cond ((< n 0) Infinity%) ((= 0 n) 1.0) (t (* n (! (1- n)))))) ; ; we make the result float to avoid the large integer problem in elisp ; The TCL version is better and is in bibliotek.general.gcl (deff print-to-file (x file &optional overwrite) "\ Prints any object to a file as if by `princ', following it with a newline. If `overwrite' is nonnil it will overwrite the file, rather than appending to it. The object itself is returned. \ " ;----------------------------------------------------- ;/turn off any inhibitors to printing the whole object ;----------------------------------------------------- (varbind print-length nil print-level nil) (declare (special print-length print-level)) ;----------------------------------------------------- ;/write to file using kludgy feature of `write-region' ;----------------------------------------------------- (write-region (format "%s\n" x) nil file (not overwrite) 0) ;---------------------- ;/return object printed ;---------------------- x) ; ; In principle we want this for TCL too, someday. ; The "special" declaration stops compiler warnings. ;: Rearranging the names of random number generators for consistency with TCL ; The function `random' exists in both elisp and TCL, but they accept different ; args and return different values. In addition elisp's cl-package provides ; the function `random*'. After adjusting the names below, we have two random ; number generators within elisp, both of which follow TCL conventions: ; Random == random* (provided by cl-package) ; random-from-elisp (made from el:random) ; We also preserve the original elisp function under a new name: ; el:random = the new name for elisp's `random' ; (To avoid confusion, we don't use the name `random' at all. We just make it ; give an error. However, for the sake of other users, we do this only ; "locally".) (defalias 'Random 'random*) (unless (fboundp 'el:random) (defalias 'el:random (symbol-function 'random))) (when (and (boundp '*user=rds*) *user=rds*) (deff random (&optional dummy) "\ This is now just a dummy function. Use `Random' or `random-from-elisp' instead. Or use `el:random' if you want literally the original elisp version. " (error "Don't use `random', use `Random' or `random-from-elisp' instead."))) (deff random-from-elisp (bound &optional (64bit *carefully*)) "\ SYNOPSIS (random-from-elisp bound &optional 64bit) If `bound' is of type integer then we return a random integer in the range [0 bound). If it a floating point then the range is [0 bound] and the possible values are discrete with a spacing depending on the machine. If the optional argument `64bit' is true [defaults to *carefully*] then we demand the spacing to be smaller than 1e-12. To initialize the seed do (el:random t). CAUTION If you compile this on one machine and run it on another then you might get unpredictable results (eg compiling on umoja and running on phonon). " ;------------------- ;/check bound is > 0 ;------------------- (o assert plusp bound) ;------------------------------------------------ ;/invoke el:random and process result as required ;------------------------------------------------ (cond ((integerp bound) (el:random bound)) ((floatp bound) ;--------------------------------------------- ;/does this machine support the integer 10^12? ;--------------------------------------------- (when 64bit (assert (< 1e12 most-positive-fixnum) nil "The integer 10^12 is too big for this machine!")) (varbind fneg (float most-negative-fixnum) fpos (float most-positive-fixnum)) (* bound (ratio (- (el:random) fneg) (- fpos fneg)))))) ; ; NOTES ; ; This is concocted from elisp's function `random', which accepts either no ; argument or a strictly positive integer. In the former case it returns a ; (positive or negative) integer, in the latter a natural number in the ; specified range. It gives garbage if its argument is zero or a float! ; Therefore we include check that arg > 0. ;: END