;;; -*- Mode: LISP; Syntax: Common-Lisp; Package: gmp; Base: 10 -*- ;; Author: Richard Fateman, Jan, 2006 ;; This file is based in part on lisp/loadgmp2.cl ?2002? ;; which also has polynomial stuff in it. ;; Working univariate polynomial stuff is in lisp/polymultx.cl though. (eval-when '(load) (require "ga")(provide "gmp")) ;; move this elsewhere, later (in-package :gmp) (eval-when (compile load) (declaim (optimize (speed 3) (safety 0) (space 0) (compilation-speed 0))) ) ;; find a some working gmp library. ;; the next section's syntax is particular for Allegro CL. ;; If I learned about UFFI I suppose it could be done in a uniform way ;; for any of the participating Common Lisp implementations. ;;(load "h:/lisp/gmp-4.1-win32/libgmp-3.dll") ;;(load "libgmp-3.dll") ;;(load "gmp.dll") ;; one should work.. (eval-when (compile load eval) (ff:def-foreign-type mpz (cl::* :int)) (ff:def-foreign-call (mpz_init "__gmpz_init") ((x (cl::* :int))) :returning :int :arg-checking nil :call-direct t) (ff:def-foreign-call (mpz_set_str "__gmpz_set_str") ;changes value of mpz x. ((x mpz) (s (cl::* :char)) (base :int)) :strings-convert t :returning :int :arg-checking nil) ;; :call-direct t (ff:def-foreign-call (mpz_get_str "__gmpz_get_str") ((s :int) (base :int) (op mpz)) ;;I think it is ok to let GMP allocate the space for this string. :returning ((cl::* :char) ) :arg-checking nil :call-direct t) (ff:def-foreign-call (mpz_get_d "__gmpz_get_d") ((x mpz)) :returning :double :arg-checking nil :call-direct t) (ff:def-foreign-call;; remove gmp number from memory (mpz_clear "__gmpz_clear") ((x mpz)) :returning :int :arg-checking nil :call-direct t) (ff:def-foreign-call;; compare (mpz_cmp "__gmpz_cmp") ((x mpz) (y mpz) ); returns pos if x>y, zero if x=y, neg if x0. If b=0, what do we do? (defmethod qr((a gmpz)(b gmpz)) (let ((r (create_mpz_zero)) (q (create_mpz_zero))) (mpz_fdiv_qr (gmpz-z q)(gmpz-z r)(gmpz-z a)(gmpz-z b)) (values r q)) ) ;;(defarithmetic expt) ;; We should implement links for (expt GMP integer) and (expt GMP GMP) (defmethod ga::1+((z gmpz))(+ 1 z)) (defmethod ga::1-((z gmpz))(- z 1)) (defmacro defcomparison (op) (let ((two-arg (intern (concatenate 'string "two-arg-" (symbol-name op)) :ga )) (cl-op (tocl op))) `(progn ;; only extra methods not in ga are defined here. (defmethod ,two-arg ((arg1 gmpz) (arg2 gmpz)) (,cl-op (mpz_cmp(gmpz-z arg1)(gmpz-z arg2)) 0)) (defmethod ,two-arg ((arg1 real) (arg2 gmpz)) (let ((x (lisp2gmpz arg1))) (,cl-op (mpz_cmp(gmpz-z x)(gmpz-z arg2)) 0))) (defmethod ,two-arg ((arg1 gmpz) (arg2 real)) (let ((y (lisp2gmpz arg2))) (,cl-op (mpz_cmp(gmpz-z arg2) (gmpz-z y)) 0))) (compile ',two-arg) (compile ',op) ',op))) (defcomparison >) (defcomparison =) (defcomparison <) (defcomparison <=) (defcomparison >=) ;; programs that decompose an Allegro CL number into an ;; array of 16 bit quantities suitable for piling into GMP are ;; in the appendix to papers/polysbyGMP.tex. Copied here, too. (defmethod lisp2gmpz ((x integer)) ;; but not fixnum (let* ((out (create_mpz_zero)) ;the wrapper (r (gmpz-z out)) ;the inside (a (btoa x));; bignum to array. (out2(create_mpz_zero)) (h (gmpz-z out2))) (do ((i (1- (length a))(1- i))) ((< i 0) r) (declare (fixnum i)) (mpz_set_si h (aref a i)) (mpz_mul_2exp r r 16) (mpz_add r r h)) (if (< x 0) (setf (aref r 1)(- (aref r 1)))) out)) ;;B to A: Bignum or fixnum to Array of bigits, 16 bits (defun btoa(x) (if (excl::fixnump x) (vector (abs x)) (let* ((size (bignum-size x)) (ans (make-array size :element-type '(unsigned-byte 16)))) (declare (optimize (speed 3)(safety 0)(debug 0))(fixnum size)) (do ((i 0 (1+ i))) ((= i size) ans) (declare (fixnum i)) (setf (aref ans i)(nth-bigit x i)))))) (eval-when (compile load)(require :llstructs)) (defun nth-bigit (x n) (sys:memref x -14 (* 2 n) :unsigned-word)) (defun bignum-size(x) ;; in 16-bit quantities (excl::bm_size x)) ;; this program is not used... (defun atob(a) ; array to POSITIVE lisp (bignum) (let ((num 0)) (do ((i (1- (length a)) (1- i))) ((< i 0) num) (setf num (+ (aref a i) (ash num 16)))))) #| The model for generic arithmetic and overloading here is not congruent with the gmp design in the following way, which mostly affects efficiency. We are superimposing a "functional" model on a "state-based" one, and in the transition we are creating temporary objects too often. With GC finalizations, these extra objects will go away without additional programmer effort, but they are still created. A closer adherence to the computation model would probably be better. Example: in our model we can compute (setf a (+ a (* b c))) but before we complete the operation we must compute separately, t1=b*c, t2=a+t1, and then pointing a to t2, and then clearing the old value of a. This can be done without any intermediate (Lisp) storage by this idiom: (mpz_addmul (gmpz-z a) (gmpz-z b) (gmpz-z c)) This assumes that no other access route is associated with a's value. ............. A digression on Factorial functions, benchmarks, timing, etc. ;; ;; factorial gmp version, vs usual (defun f(x)(if (zerop x) (cs 1) (* x (f (1- x))))) ;functional version (defun g(x)(if (zerop x) 1 (* x (g (1- x))))) ; ordinary bignum version (defun h(x) ;; closer to the real way to do this using gmp numbers (let ((ans (cs 1)) (m (cs 0))) (dotimes (i x ans) (mpz_add_ui (gmpz-z m)(gmpz-z m) 1) ;; add integer 1 to gmp m (mpz_mul (gmpz-z ans)(gmpz-z ans) (gmpz-z m))))) ;; set ans <- m*ans ;; hacked up version of h. actually m isn't likely to be ;; larger than a fixnum. Who can compute factorial of (2^29) ? (defun h7(x);; compute factorial with gmpz (assert (and (excl::fixnump x) (> x 0))) (let* ((ans (cs 1)) ;; make space for the answer. (g (gmpz-z ans))) ;; g is the inside of gmpz number ans. (declare (fixnum m x)) (do ((i 1 (cl::1+ i))) ((cl::= i x) ans) (declare (fixnum i)) (mpz_mul_si g g i) ;; compute g <- g*i ))) ;;; an observation: we are not really timing Bignum X Bignum, since ;;; if you look at how we are computing factorial, we really are ;;; doing Bignum X signed-32bit-integer. This may or may not be ;;; what you really want to test. ;;; Important note on times. The time taken by some of these tests ;;; relies heavily on which version of the gmp dll is used. The ;;; generic C (no assembler? or at least no assembler that depends ;;; on which implementation of i86 architecture you have) is definitely ;;; slower. For example, running on Windsor, a pentium3, the time ;;; to compute (h7 20000) is 2.16 seconds using gc-gmp.dll. ;;; If we use instead the pentium-2 specific version, the time is .48 seconds, ;;; an improvement of 5.4X. ;; for factorial of 20,000, f uses .6 sec, ;; sony vaio time 40,013 ;; cons cells, 303,494,640 other bytes, as reported by lisp on ;; windsor, 1.62+0.07 sec and ;;; 60,025 cons cells, 1,600,104 other bytes, 0 static bytes ; newer lib-gmp3.dll from trinity ;; for factorial of 20,000, g uses 1.6 sec + 0.92 sec in GC as well as ;; 40,013 cons cells, 303,494,640 other bytes. ;; on windsor, 2.98+1.31 ;; for factorial of 20,000, h uses .5 sec, 3 cons cells, 160 other bytes ;; on windsor, 0.481 sec ;; 3 cons cells, 160 other bytes, 0 static bytes ;;; This massive massive reduction in cons cells from 40,000 to 3 ;;; and "other bytes" from 3.0e8 to 1.6e2 of six orders of magnitude ;;; is partly illusory: some space is being allocated outside the ;;; view of the GC, in GMP's C-allocated "foreign" space. ;; at least as reported by lisp. So using functional interface ;; does not seem to slow it down in this test, but see another ;; test later in this file -- same programs-- different gmp ;; where the result is different. More times, this comparing a pentium-4 specific version of GMP on Windsome, a 2.5GHz pentium to the C-only version. factorial of 20,000. f: 0.49+0.14 sec 60,000 +1.6million ;; vs 1.6+0.04sec with non assembler g: 1.1 +0.63 sec 40,000 +303 million ;; ordinary lisp h: 0.188sec 3 cons cells 160 other bytes ;; vs 1.3+0 with non assembler Mathematica 5.1, which probably uses GMP for this, reports that it computes factorial of 20,000 in 0.047 sec on Windsome Maple 7 reports a time of 0.297 sec. which is probably also from GMP. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Why is Mathematica 5.1 so fast at computing factorial? Simple: it does not use the naive factorial algorithm, but a variant using recursive-splitting for computing n!. A fairer Mathematica calculation to compare to our naive factorial would be Product[i,{i,1,20000}] which takes twice as long as 20000!, but is still rather fast. Here's a recursive factorial that is especially nice for large numbers if it is efficient to multiply two numbers of about equal size, and probably what Mathematica uses. (defun k (n m) ;; (k n 1) is n! (if (<= n m) (cs n) (* (k n (* 2 m)) (k (- n m)(* 2 m))))) ;; or declared better and using shift instead of mult by 2 ... (defun k2 (n m) ;; (k n 1) is n! (declare (fixnum n m)) (if (cl::<= n m) (cs n) (* (k2 (cl::- n m)(setf m (ash m 1))) (k2 n m) ))) ;; Now here's a mysterious factorial which uses no extra storage, but its ;; not faster because it multiplies small X big. (defun kfact(n)(kfact1 n 1 1)) ;; version for integers (defun kfact1 (n m ans) ;; (kfact n 1 1) is n! (declare (fixnum n m)) (if (<= n m) (* n ans) (let ((q (ash m 1))) (kfact1 n q (kfact1 (- n m) q ans))))) ;; ;; Here's a nice version showing how a minor piece of ;; representation hacking in GMPZ could be done (defun k3top(n) ;;(k3top n) is n! pretty fast (let ((a (alloc-gmpz))) (k3 n 1 (gmpz-z a)) a)) (defun k3 (n m ans) ;; ans is a gmp number "insides" (declare (fixnum n m)) (if(cl::<= n m)(mpz_set_si ans n) (let ((b (gmpz-z(alloc-gmpz)))) (k3 (- n m) (setf m(ash m 1)) b) (k3 n m ans) (mpz_mul ans ans b)))) ;; the problem with k3 above is that it uses lots of storage, O(n), ;; by use of (alloc-gmpz), most of it briefly. ;; Actually only a few storage locations O(log_2(n)) are needed ;; at any one time. The next program pre-allocates sufficient ;; cells and then manages them explicitly. ;; I also used labels and local environment to capture the resource res. (defun k4(n);; using resource instead of allocation each time. Faster!! (declare (fixnum n)(optimize (speed 3)(safety 0))) (let ((res nil)) (labels ((k4i (n m ans) ; ans is a gmp number. (declare (fixnum n m) (type (simple-array (signed-byte 32) (3)) ans)) (if(cl::<= n m)(mpz_set_si ans n) (let ((b (vector-pop res))) (declare (type (simple-array (signed-byte 32) (3)) b)) (k4i (cl::- n m) (setf m(ash m 1)) b) (k4i n m ans) (mpz_mul ans ans b) (incf (fill-pointer res)) ; (vector-push b gmpres) )))) (let* ((L (ceiling (cl::log n 2))) (a (alloc-gmpz))) (declare (fixnum L)) (setf res (make-array L :fill-pointer 0)) ;; allocate enough space for temps. ;; fill the temps with gmp numbers, zeros. Just the insides though. (dotimes (i L)(vector-push (gmpz-z (alloc-gmpz)) res)) (k4i n 1 (gmpz-z a)) a)))) ;; (k 20000 1) takes .241 sec on windsor. ;; (k2 20000 1) takes .190 sec on windsor. ;; (k3top 20000)takes .130 sec on windsor using p2-libgmp-3.dll ;; (k4 20000) takes .071 sec on windsor using ditto ;; Mathematica takes .07 sec on windsor. ;; Here's another variant. This version also works pretty hard.. ;; but is not faster. (defun k4x(n);; (declare (fixnum n)) (let ((res nil)) (labels ((k4i (n m ans) ; ans is a gmp number. (declare (fixnum n m)) (cond((cl::<= n m)(mpz_set_si ans n)) ;; try to factor out 2^q from factorial ((and (cl::= m 2)(evenp n)) (let ((q (ash n -1))) (declare (fixnum q)) (k4i q 1 ans) (mpz_mul_2exp ans ans q))) ((and (cl::> m 1)(cl::= 0 (mod n m))) (let ((b (vector-pop res)) (q (cl:/ n m))) (declare (fixnum q)) ;; try to factor out n^q from factorial (mpz_ui_pow_ui b m q) (k4i q 1 ans) (mpz_mul ans ans b) (incf (fill-pointer res)))) (t(let ((b (vector-pop res))) (k4i (cl::- n m) (setf m(ash m 1)) b) (k4i n m ans) (mpz_mul ans ans b) (incf (fill-pointer res)) ; (vector-push b gmpres) ))))) (let* ((L (* 2 (ceiling (cl::log n 2)))) (a (alloc-gmpz))) (declare (fixnum L)) (setf res (make-array L :fill-pointer 0)) ;; allocate enough space for temps. ;; fill the temps with gmp numbers, zeros. Just the insides though. (dotimes (i L)(vector-push (gmpz-z (alloc-gmpz)) res)) (k4i n 1 (gmpz-z a)) a)))) (defun k4x (n m ans) ; ans is a gmp number. (declare (fixnum n m)) (cond ((cl::<= n m)(mpz_set_si (gmpz-z ans) n) ans) ;; this next clause factors out 2^n. but doesn't speed up answer! ((and (cl::= m 2)(evenp n)) (let ((q (ash n -1))) (declare (fixnum q m)) (k4x q 1 ans) (mpz_mul_2exp (gmpz-z ans)(gmpz-z ans) q) ans )) (t (let ((b (alloc-gmpz))) (k4x (- n m) (setf m(ash m 1)) b) (k4x n m ans) (mpz_mul (gmpz-z ans)(gmpz-z ans)(gmpz-z b)) ans)))) There are many more methods, some involving factoring: to compute 100! all we need to compute is 2^97 * 3^48 * 5^24 * 7^16 * 11^9 * 13^7 * 17^5 * 19^5 * 23^4 * 29^3 * 31^3 * 37^2 * 41^2 * 43^2 * 47^2 * 53 * 59 * 61 * 67 * 71 * 73 * 79 * 83 * 89 * 97. see http://www.luschny.de/math/factorial/index.html This bit of discussion here goes to show that this benchmark, as is the case for other benchmarks, does not represent the most efficient way of computing the answer, but just a way of using computer resources in some systematic fashion, watching how some resource utilization varies as we modify some relatively simple parameter such as language implementation, language usage, choice of computer, etc. ... end of digression on Factorial computation. Further notes: after writing the QD interface, and revising the MPFR interface to look like the QD interface, the question arises as to whether we should revise GMP interface to look the same. Since the GMP and MPFR arithmetic are likely to be much slower in their actual computing, inefficiency in the linkage would be much more tolerable. Do we need to write with-temps and dsetv for GMP? I think not. We could do a wrapper for gmp rationals, I guess. Or make interfaces for the number-theoretic stuff in gmp. Trying to clear the air about different versions of GMP 4.1.4 for different architectures. Consider the computation of 20,000! by the program k4, above. This needs access to the GMP library. Which library? We tested this all on a pentium 4. (time (progn (dotimes (i 10)(k4 20000)) nil)) with generic code for any i86, written in C: 1,219 ms. with some code specifically for pentium 0: 1,250 ms. with some code specifically for pentium 3: 641 ms. with some code specifically for pentium 4: 437 ms. |# ;; maxfactx can be done nicely in gmpz, and is ;; about 5X faster on windsor with generic gmp library; ;; with pentium-2 version, it is 14X to 16X faster. ;; cutting short the iteration by memoization would also ;; help. 20,000! in 70ms. (setf *print-circle* t) (defun gmaxfactx (n) (declare (fixnum n)(optimize (speed 3)(safety 0))) (let* ((z (if (< n 100) 5 100)) (aloop (gcloop z))) ; heuristic (loop for i from (1+ z) to n do ;;(setf (car aloop) (* (car aloop) i)) (mpz_mul_si (car aloop) (car aloop) i) (setf aloop (cdr aloop))) (gloopprod aloop))) (defun gcloop(n) ;; (cloop 5) is #1=(5 4 3 2 1 . #1#) with gmpz insides (let*((start (list (gmp::gmpz-z (gmp::into 1)))) (end start)) (dotimes (i (1- n) (setf (cdr end) start)) (setq start (cons (gmpz-z (into (+ 2 i))) start))))) (defun gloopprod (l) ;; efficient product of all the approximately equal ;; numbers in the loop trying to keep the sizes of inputs ;; approximately balanced. the circular loop l is destroyed in the ;; process. (declare (optimize (speed 3)(safety 0))) (cond ((eq(cdr l) l)(make-gmpz :z (car l))) (t ;;(setf (car l)(* (car l)(cadr l))) (mpz_mul (car l)(car l)(cadr l)) (setf (cdr l)(cddr l)) (gloopprod (cdr l))))) ;;;; here is a lisp-only version of the above, for comparison. (defun cloop(n) ;; (cloop 5) is #1=(5 4 3 2 1 . #1#) (let*((start (list 1)) (end start)) (dotimes (i (1- n) (setf (cdr end) start)) (setq start (cons (+ 2 i) start))))) (defun loopprod (l) ;; efficient product of all the approximately equal ;; numbers in the loop trying to keep the sizes of inputs ;; approximately balanced. the circular loop l is destroyed in the ;; process. (declare (optimize (speed 3)(safety 0))) (cond ((eq(cdr l) l)(car l)) (t (setf (car l)(* (car l)(cadr l))) (setf (cdr l)(cddr l)) (loopprod (cdr l))))) (defun maxfactx (n) (declare (fixnum n)(optimize (speed 3)(safety 0))) (let* ((z (if (< n 100) 5 100)) (aloop (cloop z))) ; heuristic (loop for i from (1+ z) to n do (setf (car aloop) (* (car aloop) i)) (setf aloop (cdr aloop))) (loopprod aloop))) ;;;; trying to keep powers of 2 out of the loop... ;; broken ;;; do only the ODD numbers, i.e. #| (defun cloop(n begin step) ;; (begin, begin+step , ... wrap around, with n items) (let*((start (list begin)) (end start) (count (+ begin (* step n)))) (do((i (+ step begin) (+ i step))) ((>= i count) (setf (cdr end) start)) (setq start (cons i start))))) (defun loopprod (l) ;; efficient product of all the approximately equal ;; numbers in the loop trying to keep the sizes of inputs ;; approximately balanced. the circular loop l is destroyed in the ;; process. (declare (optimize (speed 3)(safety 0))) (cond ((eq(cdr l) l)(car l)) (t (setf (car l)(* (car l)(cadr l))) (setf (cdr l)(cddr l)) (loopprod (cdr l))))) (defun maxfactx (n) (declare (fixnum n)(optimize (speed 3)(safety 0))) (let* ((z (if (< n 100) 1 20)) (aloop (cloop z 3 2)) (shift (floor z 2)) ) (loop for i from (+ 3 (* 2 z)) to n do (setf (car aloop) (* (car aloop) i)) (setf aloop (cdr aloop))) (print shift) (ash (loopprod aloop) (+ shift (floor n 2))))) |# ;;play games with kg (defun kg (n m) (declare (fixnum n m)) ;; tell lisp that n, m are not very big. (cond ((and (= m 2)(evenp n)) (ash (kg (ash n -1) 1) (ash n -1))) ((<= n m) n) (t (* (kg n (ash m 1)) (kg (- n m)(ash m 1)))))) (defun kg (n m) (declare (fixnum n m)) ;; tell lisp that n, m are not very big. (cond ((and (= m 2)(evenp n)) (ash (kg (ash n -1) 1) (ash n -1))) ((<= n m) n) (t (* (kg n (ash m 1)) (kg (- n m)(ash m 1)))))) (in-package :user) (defun kgtop (n) (let ((shift 0)) (declare(special shift)) (ash (kg1 n 1) shift))) (defun kg1 (n m) (declare (fixnum n m) (special shift)) (cond ((and (= m 2)(evenp n)) (incf shift (ash n -1)) (kg1 (ash n -1) 1)) ((<= n m) n) (t (* (kg1 n (ash m 1)) (kg1 (- n m)(ash m 1)))))) (defun gkg(n &optional (aa (alloc-gmpz)));put answer in 2nd arg, if there (declare (fixnum n)(optimize (speed 3)(safety 0)(debug 0))) (let ((res nil) (shift 0) (L (ceiling (cl::log n 2))) (a (gmpz-z aa))) (declare (fixnum L shift n)) (labels ((k (n m ans res) (declare (fixnum n m)) (cond ((and (cl::evenp n)(cl::> m 1)) (cl::incf shift (cl::ash n -1)) (k (cl::ash n -1) (cl::ash m -1) ans res)) ((cl::<= n m)(mpz_set_si ans n)) (t(let ((b (car res))) (k (cl::- n m) (setq m (cl::ash m 1)) b (cdr res)) (k n m ans (cdr res)) (mpz_mul ans ans b) ans))))) (dotimes (i L)(push (gmpz-z (alloc-gmpz)) res));set up resource (k n 1 a res) (mpz_mul_2exp a a shift) aa))) ;;;;;;;;;;;;;; ;;;; combining gmp, memoization, split-recursive, (defun kgmin (n m min) ;; (k n 1 1) is n! (declare (fixnum n m min)) (cond ((< n min) 1) ((<= n m) n) ((and (evenp m)(evenp n)(evenp min)) (cl::ash (kgmin (cl::ash n -1) (cl::ash m -1) (cl::ash min -1)) (1+ (cl::ash (- n min) -1)))) (t (* (kgmin n (cl::ash m 1) min) (kgmin (- n m)(cl::ash m 1) min))))) ;; (* (gkgmin 100 1 50)(kgmin 49 1 1)) is the same as (gkg 100 1 1) (defun gkgmin(n min &aux (aa (alloc-gmpz)));; (declare (fixnum n)(optimize (speed 3)(safety 0)(debug 0))) (let ((res nil) (shift 0) (L (ceiling (cl::log n 2))) (a (gmpz-z aa))) (declare (fixnum L shift n)) (labels ((k (n m ans res min) (declare (fixnum n m)) (cond ((cl::< n min) (mpz_set_si ans 1)) ((cl::<= n m)(mpz_set_si ans n)) ((and (evenp n)(evenp m)(evenp min)) (cl::incf shift (1+ (cl::ash (- n min) -1))) (k (cl::ash n -1) (cl::ash m -1) ans res (cl::ash min -1))) (t(let ((b (car res))) (k (cl::- n m) (setq m (cl::ash m 1)) b (cdr res) min) (k n m ans (cdr res) min) (mpz_mul ans ans b) ans))))) (dotimes (i L)(push (gmpz-z (alloc-gmpz)) res));set up resource (k n 1 a res min) (mpz_mul_2exp a a shift) aa))) ;;main program. (defun gkmemfac(n) (let ((z (lookupfact n))) ;; find z, the largest integer <= n such that we know z! (if (= (car z) n)(cdr z) (rememberfact n (* (gkgmin n (1+ (car z))) (cdr z)))))) (defun lookupfact(n) (loop for i from 0 to (length oldfacts) do (if (<= (car (aref oldfacts i)) n) (return (aref oldfacts i))))) (defun rememberfact(n f) (vector-push-extend (cons n f) oldfacts) (setf oldfacts (sort oldfacts #'> :key #'car)) f) (defparameter oldfacts (make-array 0 :adjustable t :fill-pointer t)) (defun clearfact()(setf oldfacts (make-array 0 :adjustable t :fill-pointer t)) (vector-push-extend (cons 0 (into 1)) oldfacts)) (clearfact) ; clear it now. ;;vs