From: David Steuber
Subject: The Joy of Pi
Date: 
Message-ID: <874qjvg2sz.fsf@david-steuber.com>
Just for a lark, I implemented an infinite series for calculating Pi
from David Blatner's "The Joy of Pi" (the title actually uses the
Greek letter).  I used one of Newton's series on page 42.  This is the
first time I've written a Lisp program that runs faster in CLisp than
in SBCL.  OpenMCL runs it very slowly.  It might be interesting to
make this program fast.  Mostly that would involve using a faster
computing method.  I also took advantage of closures because they
simplified the code logic for me.  I don't know what sort of
performance hit I take using closures.  I doubt it is a big deal in
the face of bignum arithmetic.

Here is the code followed by three runs on a 12" PowerBook G4 with
867Mhz processor and 640MB RAM:

;;; Calculate pi using one of Newton's series:
;;;
;;; pi/6 = 1/2 + 1/2(1/3x2^3) + ((1x3)/(2x4))(1/5x2^2) +
;;; ((1x3x5)/(2x4x6))(1/7x2^7) + ...
;;;

(defun make-alternating-number-generator (first-number)
  (let ((num (- first-number 2)))
    (lambda () (incf num 2))))

(defun make-term-generator ()
  (let ((numerator-gen (make-alternating-number-generator 1))
        (denominator-gen (make-alternating-number-generator 2))
        (coeff-gen (make-alternating-number-generator 3)))
    (let ((n 1) (d 1))
      (lambda ()
        (setf n (* n (funcall numerator-gen))
              d (* d (funcall denominator-gen)))
        (let ((c (funcall coeff-gen)))
          (* (/ n d) (/ 1 (* c (expt 2 c)))))))))

(defun newton-pi (terms)
  (let ((accum 1/2)
        (term (make-term-generator)))
    (dotimes (i terms (* 6 accum))
      (incf accum (funcall term)))))

(defun pi-decimal (digits &optional (terms 100))
  (let ((pi-rational (newton-pi terms)))
    (dotimes (i digits)
      (multiple-value-bind (digit remainder)
          (floor pi-rational)
        (princ digit)
        (when (and (> i 0) (= 0 (mod i 4)))
          (princ #\Space))
        (when (and (> i 0) (= 0 (mod i 64)))
          (terpri))
        (when (= i 0) (princ ".") (terpri))
        (setf pi-rational (* 10 remainder))))))

·····@interloper:~/usr/src/pi
$ clisp
  i i i i i i i       ooooo    o        ooooooo   ooooo   ooooo
  I I I I I I I      8     8   8           8     8     o  8    8
  I  \ `+' /  I      8         8           8     8        8    8
   \  `-+-'  /       8         8           8      ooooo   8oooo
    `-__|__-'        8         8           8           8  8
        |            8     o   8           8     o     8  8
  ------+------       ooooo    8oooooo  ooo8ooo   ooooo   8

Copyright (c) Bruno Haible, Michael Stoll 1992, 1993
Copyright (c) Bruno Haible, Marcus Daniels 1994-1997
Copyright (c) Bruno Haible, Pierpaolo Bernardi, Sam Steingold 1998
Copyright (c) Bruno Haible, Sam Steingold 1999-2000
Copyright (c) Sam Steingold, Bruno Haible 2001-2004


[1]> (load (compile-file "pi.lisp"))

Compiling file /Users/david/usr/src/pi/pi.lisp ...

Wrote file /Users/david/usr/src/pi/pi.fas
0 errors, 0 warnings
;; Loading file /Users/david/usr/src/pi/pi.fas ...
;; Loaded file /Users/david/usr/src/pi/pi.fas
T
[2]> (time (pi-decimal 1025 2000))
3.
1415 9265 3589 7932 3846 2643 3832 7950 2884 1971 6939 9375 1058 2097 4944 5923 
0781 6406 2862 0899 8628 0348 2534 2117 0679 8214 8086 5132 8230 6647 0938 4460 
9550 5822 3172 5359 4081 2848 1117 4502 8410 2701 9385 2110 5559 6446 2294 8954 
9303 8196 4428 8109 7566 5933 4461 2847 5648 2337 8678 3165 2712 0190 9145 6485 
6692 3460 3486 1045 4326 6482 1339 3607 2602 4914 1273 7245 8700 6606 3155 8817 
4881 5209 2096 2829 2540 9171 5364 3678 9259 0360 0113 3053 0548 8204 6652 1384 
1469 5194 1511 6094 3305 7270 3657 5959 1953 0921 8611 7381 9326 1179 3105 1185 
4807 4462 3799 6274 9567 3518 8575 2724 8912 2793 8183 0119 4912 9833 6733 6244 
0656 6430 8602 1394 9463 9522 4737 1907 0217 9860 9437 0277 0539 2171 7629 3176 
7523 8467 4818 4676 6940 5132 0005 6812 7145 2635 6082 7785 7713 4275 7789 6091 
7363 7178 7214 6844 0901 2249 5343 0146 5495 8537 1050 7922 7968 9258 9235 4201 
9956 1121 2902 1960 8640 3441 8159 8136 2977 4771 3099 6051 8707 2113 4999 9998 
3729 7804 9951 0597 3173 2816 0963 1859 5024 4594 5534 6908 3026 4252 2308 2533 
4468 5035 2619 3118 8171 0100 0313 7838 7528 8658 7533 2083 8142 0617 1776 6914 
7303 5982 5349 0428 7554 6873 1159 5628 6388 2353 7875 9375 1957 7818 5778 0532 
1712 2680 6613 0019 2787 6611 1959 0921 6420 1989 3809 5257 2010 6548 5863 2788 

Real time: 15.590597 sec.
Run time: 12.75 sec.
Space: 26240780 Bytes
GC: 45, GC time: 1.26 sec.
NIL
[3]> (quit)
Bye.
·····@interloper:~/usr/src/pi
$ sbcl
This is SBCL 0.8.16.24, an implementation of ANSI Common Lisp.
More information about SBCL is available at <http://www.sbcl.org/>.

SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses.  See the CREDITS and COPYING files in the
distribution for more information.
* (load (compile-file "pi.lisp"))
; compiling file "/Users/david/usr/src/pi/pi.lisp" (written 11 NOV 2004 09:32:06 PM):
; recognizing DEFUN MAKE-ALTERNATING-NUMBER-GENERATOR
; compiling DEFUN MAKE-ALTERNATING-NUMBER-GENERATOR: 
; compiling top level form: 
; recognizing DEFUN MAKE-TERM-GENERATOR
; compiling DEFUN MAKE-TERM-GENERATOR: 
; compiling top level form: 
; recognizing DEFUN NEWTON-PI
; compiling DEFUN NEWTON-PI: 
; compiling top level form: 
; recognizing DEFUN PI-DECIMAL
; compiling DEFUN PI-DECIMAL: 
; compiling top level form: 

; /Users/david/usr/src/pi/pi.fasl written
; compilation finished in 0:00:00

T
* (time (pi-decimal 1025 2000))
3.
1415 9265 3589 7932 3846 2643 3832 7950 2884 1971 6939 9375 1058 2097 4944 5923 
0781 6406 2862 0899 8628 0348 2534 2117 0679 8214 8086 5132 8230 6647 0938 4460 
9550 5822 3172 5359 4081 2848 1117 4502 8410 2701 9385 2110 5559 6446 2294 8954 
9303 8196 4428 8109 7566 5933 4461 2847 5648 2337 8678 3165 2712 0190 9145 6485 
6692 3460 3486 1045 4326 6482 1339 3607 2602 4914 1273 7245 8700 6606 3155 8817 
4881 5209 2096 2829 2540 9171 5364 3678 9259 0360 0113 3053 0548 8204 6652 1384 
1469 5194 1511 6094 3305 7270 3657 5959 1953 0921 8611 7381 9326 1179 3105 1185 
4807 4462 3799 6274 9567 3518 8575 2724 8912 2793 8183 0119 4912 9833 6733 6244 
0656 6430 8602 1394 9463 9522 4737 1907 0217 9860 9437 0277 0539 2171 7629 3176 
7523 8467 4818 4676 6940 5132 0005 6812 7145 2635 6082 7785 7713 4275 7789 6091 
7363 7178 7214 6844 0901 2249 5343 0146 5495 8537 1050 7922 7968 9258 9235 4201 
9956 1121 2902 1960 8640 3441 8159 8136 2977 4771 3099 6051 8707 2113 4999 9998 
3729 7804 9951 0597 3173 2816 0963 1859 5024 4594 5534 6908 3026 4252 2308 2533 
4468 5035 2619 3118 8171 0100 0313 7838 7528 8658 7533 2083 8142 0617 1776 6914 
7303 5982 5349 0428 7554 6873 1159 5628 6388 2353 7875 9375 1957 7818 5778 0532 
1712 2680 6613 0019 2787 6611 1959 0921 6420 1989 3809 5257 2010 6548 5863 2788 
Evaluation took:
  17.833 seconds of real time
  15.32 seconds of user run time
  0.74 seconds of system run time
  0 page faults and
  126,003,784 bytes consed.
NIL
* (quit)
·····@interloper:~/usr/src/pi
$ openmcl
Welcome to OpenMCL Version (Beta: Darwin) 0.14.2-p1!
? (load (compile-file "pi.lisp"))
#P"/Users/david/usr/src/pi/pi.dfsl"
? (time (pi-decimal 1025 2000))
3.
1415 9265 3589 7932 3846 2643 3832 7950 2884 1971 6939 9375 1058 2097 4944 5923 
0781 6406 2862 0899 8628 0348 2534 2117 0679 8214 8086 5132 8230 6647 0938 4460 
9550 5822 3172 5359 4081 2848 1117 4502 8410 2701 9385 2110 5559 6446 2294 8954 
9303 8196 4428 8109 7566 5933 4461 2847 5648 2337 8678 3165 2712 0190 9145 6485 
6692 3460 3486 1045 4326 6482 1339 3607 2602 4914 1273 7245 8700 6606 3155 8817 
4881 5209 2096 2829 2540 9171 5364 3678 9259 0360 0113 3053 0548 8204 6652 1384 
1469 5194 1511 6094 3305 7270 3657 5959 1953 0921 8611 7381 9326 1179 3105 1185 
4807 4462 3799 6274 9567 3518 8575 2724 8912 2793 8183 0119 4912 9833 6733 6244 
0656 6430 8602 1394 9463 9522 4737 1907 0217 9860 9437 0277 0539 2171 7629 3176 
7523 8467 4818 4676 6940 5132 0005 6812 7145 2635 6082 7785 7713 4275 7789 6091 
7363 7178 7214 6844 0901 2249 5343 0146 5495 8537 1050 7922 7968 9258 9235 4201 
9956 1121 2902 1960 8640 3441 8159 8136 2977 4771 3099 6051 8707 2113 4999 9998 
3729 7804 9951 0597 3173 2816 0963 1859 5024 4594 5534 6908 3026 4252 2308 2533 
4468 5035 2619 3118 8171 0100 0313 7838 7528 8658 7533 2083 8142 0617 1776 6914 
7303 5982 5349 0428 7554 6873 1159 5628 6388 2353 7875 9375 1957 7818 5778 0532 
1712 2680 6613 0019 2787 6611 1959 0921 6420 1989 3809 5257 2010 6548 5863 2788 
(PI-DECIMAL 1025 2000) took 109,685 milliseconds (109.685 seconds) to run.
Of that, 103,560 milliseconds (103.560 seconds) were spent in user mode
         960 milliseconds (0.960 seconds) were spent in system mode
         5,165 milliseconds (5.165 seconds) were spent executing other OS processes.
125 milliseconds (0.125 seconds) was spent in GC.
 30,467,512 bytes of memory allocated.
NIL
? (quit)

-- 
An ideal world is left as an excercise to the reader.
   --- Paul Graham, On Lisp 8.1

From: Paul F. Dietz
Subject: Re: The Joy of Pi
Date: 
Message-ID: <ppmdnSUO27wGJAncRVn-1w@dls.net>
David Steuber wrote:
> Just for a lark, I implemented an infinite series for calculating Pi
> from David Blatner's "The Joy of Pi" (the title actually uses the
> Greek letter).  I used one of Newton's series on page 42.  This is the
> first time I've written a Lisp program that runs faster in CLisp than
> in SBCL.

CLISP does very well on programs that spend most of their time
in bignum arithmetic.  Since most of that time is in a library, which
in CLISP is written in C and is highly optimized, the fact that CLISP
doesn't compile to machine code is not as important.

	Paul
From: Bradley J Lucier
Subject: Re: The Joy of Pi
Date: 
Message-ID: <cn332a$bsa@arthur.cs.purdue.edu>
In article <··············@david-steuber.com>,
David Steuber  <·····@david-steuber.com> wrote:
>Just for a lark, I implemented an infinite series for calculating Pi
>from David Blatner's "The Joy of Pi" (the title actually uses the
>Greek letter).  I used one of Newton's series on page 42.  This is the
>first time I've written a Lisp program that runs faster in CLisp than
>in SBCL.  OpenMCL runs it very slowly.  It might be interesting to
>make this program fast.  Mostly that would involve using a faster
>computing method.  I also took advantage of closures because they
>simplified the code logic for me.  I don't know what sort of
>performance hit I take using closures.  I doubt it is a big deal in
>the face of bignum arithmetic.
>
>Here is the code followed by three runs on a 12" PowerBook G4 with
>867Mhz processor and 640MB RAM:
>
>;;; Calculate pi using one of Newton's series:
>;;;
>;;; pi/6 = 1/2 + 1/2(1/3x2^3) + ((1x3)/(2x4))(1/5x2^2) +
>;;; ((1x3x5)/(2x4x6))(1/7x2^7) + ...
>;;;

OK, this stuff is fun.  I translated your code to Scheme, ran it in Gambit-C
4.0 beta on a 500MHz G4 PowerBook and got a time of 20130 ms, so that's
comparable to the CLISP time.  (I couldn't get the CLISP distribution to
build on MacOS X for some reason.)

Summing this type of series is what so-called "binary splitting"

http://numbers.computation.free.fr/Constants/Algorithms/splitting.html

is designed for.  (If I remember correctly, binary splitting might be
mentioned in the last chapter of the Joy of Pi.)  You split the sum
roughly into two equal halves, factor out the common terms of the second
half, sum each half, multiply the second half by the common terms, and
add the two halves together.

I wrote some code to do this with your series, the usual series for e, and
the best-known Maclaurin atan formula for pi.  The code and results (on a
2GHz G5) are given below.  Basically, the binary-splitting method is almost
10 times as fast as your method in summing 2000 terms with Gambit-C's
bignum library.

For a lark, I timed the original pi-newton code and the related
binary splitting code with 20,000 terms on the G5.  I got

(time (display-decimal newton-pi digits terms))
    1749105 ms real time
    1229220 ms cpu time (998140 user, 231080 system)
    196485 collections accounting for 426063 ms real time (226070 user, 73680 system)
    155452902920 bytes allocated
    no minor faults
    no major faults
(time (display-decimal binary-splitting-newton-pi digits terms))
    24481 ms real time
    17280 ms cpu time (16530 user, 750 system)
    5744 collections accounting for 8466 ms real time (5990 user, 210 system)
    2524772176 bytes allocated
    no minor faults
    no major faults

so, as you can see, there is a complexity benefit with the binary splitting
method (as long has you have fast methods (faster than O(N^2)) for
multiplication, division, gcd, etc., in your bignum library).

Brad

PS:  If you want to take issue with this code or rewrite it to be more clear,
great, since I'm not exactly sure why it works with the pi-newton series, but
I don't have any more time to play with it today.

;;; Calculate pi using one of Newton's series:
;;;
;;; pi/6 = 1/2 + 1/2(1/3x2^3) + ((1x3)/(2x4))(1/5x2^2) +
;;; ((1x3x5)/(2x4x6))(1/7x2^7) + ...
;;;

(define terms 2000)
(define digits 1025)

(define-macro (dotimes vars . body)
  `(do ((,(car vars) 0 (+ ,(car vars) 1)))
       ((= ,(car vars) ,(cadr vars)) ,(if (not (null? (cddr vars))) (caddr vars) '(void)))
     ,@body))

(define (make-alternating-number-generator first-number)
  (let ((num (- first-number 2)))
    (lambda ()
      (set! num (+ num 2))
      num)))

(define (make-term-generator )
  (let ((numerator-gen (make-alternating-number-generator 1))
        (denominator-gen (make-alternating-number-generator 2))
        (coeff-gen (make-alternating-number-generator 3)))
    (let ((n 1) (d 1))
      (lambda ()
        (set! n (* n (numerator-gen)))
	(set! d (* d (denominator-gen)))
        (let ((c (coeff-gen)))
          (* (/ n d) (/ 1 (* c (expt 2 c)))))))))

(define (newton-pi terms)
  (let ((accum 1/2)
        (term (make-term-generator)))
    (dotimes (i terms (* 6 accum))
	     (set! accum (+ accum (term))))))

(define (display-decimal method digits #!optional (terms 100))
  (let ((pi-rational (method terms)))
    (dotimes (i digits)
	     (let* ((digit (floor pi-rational))
		    (remainder (- pi-rational digit)))
        (display digit)
        (if (and (> i 0) (= 0 (modulo i 4)))
          (display #\space))
        (if (and (> i 0) (= 0 (modulo i 64)))
          (newline))
        (if (= i 0) (begin (display ".") (newline)))
        (set! pi-rational (* 10 remainder))))))

(time (display-decimal newton-pi digits terms))

(define (binary-splitting-partial-product m n factor)
  ;; computes the product (* (factor (+ m 1)) (factor (+ m 2)) ... (factor n))
  (if (< (- n m) 10)
      (do ((i (+ m 1) (+ i 1))
	   (result 1 (* result (factor i))))
	  ((> i n) result))
      (* (binary-splitting-partial-product m
					   (quotient (+ m n) 2)
					   factor)
	 (binary-splitting-partial-product (quotient (+ m n) 2)
					   n 
					   factor))))

(define (partial-factorial m n)
  (binary-splitting-partial-product m n (lambda (i) i)))

(define (partial-odd-factorial m n)
  (binary-splitting-partial-product m n (lambda (i) (- (* 2 i) 1))))

(define (partial-even-factorial m n)
  (binary-splitting-partial-product m n (lambda (i) (* 2 i))))

(define (binary-splitting-partial-sum m n
                                      partial-term
                                      common-factor-ratio)
  ;; sums (partial) terms from m to n-1
  ;; (partial-term n m) is the term at n with the common factors of terms >= m removed
  ;; (common-factor-ratio m n) is the ratio of the common factor of terms >= n divided by
  ;; the common factors of terms >= m
  (if (< (- n m) 10)
      (do ((i m (+ i 1))
           (result 0 (+ result (partial-term m i))))
          ((= i n) result))
      (+ (binary-splitting-partial-sum m
                                       (quotient (+ m n) 2)
                                       partial-term
                                       common-factor-ratio)
         (* (common-factor-ratio m (quotient (+ m n) 2))
            (binary-splitting-partial-sum (quotient (+ m n) 2)
                                          n
                                          partial-term
                                          common-factor-ratio)))))

(define (binary-splitting-sum n partial-term common-factor)
  (binary-splitting-partial-sum 0 n partial-term common-factor))

(define (binary-splitting-e n)
  (binary-splitting-sum n
                       (lambda (m n) (/ (partial-factorial m n)))
                       (lambda (m n) (/ (partial-factorial m n)))))

(time (display-decimal binary-splitting-e digits terms))

(define (binary-splitting-compute-atan n x)
  ;; here we just consider the common factor to be x^(2n+1)
  (* x    ;; common factor for all terms
     (binary-splitting-sum n
                           (lambda (m n) (/ (expt x (* 2 (- n m)))
                                            (* (if (odd? n) -1 1) ( + (* 2 n) 1))))
                           (lambda (m n) (expt x (* 2 (- n m)))))))

(define (binary-splitting-atan-pi n)
  (* 4 (- (* 4 (binary-splitting-compute-atan n 1/5))
          (binary-splitting-compute-atan (quotient (* n 10) 34) 1/239))))

(time (display-decimal binary-splitting-atan-pi digits terms))

;(define a (time (binary-splitting-atan-pi size)))

(define (binary-splitting-newton-pi n)
  (* 3 (binary-splitting-sum (+ n 1)
			     (lambda (m n) (/ (partial-odd-factorial m n)
					      (partial-even-factorial m n) (* (+ (* 2 n) 1) (expt 2 (+ (* 2 n 1))))))
			     (lambda (m n) (/ (partial-odd-factorial m n)
					      (partial-even-factorial m n))))))

(time (display-decimal binary-splitting-newton-pi digits terms))

(pp (= (binary-splitting-newton-pi terms) (newton-pi terms)))

[descartes:~] lucier% gsi
loading ~lucier/local/gambit/gambcext
Gambit Version 4.0 beta 9
> (load "pi-newton.scm")
3.
1415 9265 3589 7932 3846 2643 3832 7950 2884 1971 6939 9375 1058 2097 4944 5923 
0781 6406 2862 0899 8628 0348 2534 2117 0679 8214 8086 5132 8230 6647 0938 4460 
9550 5822 3172 5359 4081 2848 1117 4502 8410 2701 9385 2110 5559 6446 2294 8954 
9303 8196 4428 8109 7566 5933 4461 2847 5648 2337 8678 3165 2712 0190 9145 6485 
6692 3460 3486 1045 4326 6482 1339 3607 2602 4914 1273 7245 8700 6606 3155 8817 
4881 5209 2096 2829 2540 9171 5364 3678 9259 0360 0113 3053 0548 8204 6652 1384 
1469 5194 1511 6094 3305 7270 3657 5959 1953 0921 8611 7381 9326 1179 3105 1185 
4807 4462 3799 6274 9567 3518 8575 2724 8912 2793 8183 0119 4912 9833 6733 6244 
0656 6430 8602 1394 9463 9522 4737 1907 0217 9860 9437 0277 0539 2171 7629 3176 
7523 8467 4818 4676 6940 5132 0005 6812 7145 2635 6082 7785 7713 4275 7789 6091 
7363 7178 7214 6844 0901 2249 5343 0146 5495 8537 1050 7922 7968 9258 9235 4201 
9956 1121 2902 1960 8640 3441 8159 8136 2977 4771 3099 6051 8707 2113 4999 9998 
3729 7804 9951 0597 3173 2816 0963 1859 5024 4594 5534 6908 3026 4252 2308 2533 
4468 5035 2619 3118 8171 0100 0313 7838 7528 8658 7533 2083 8142 0617 1776 6914 
7303 5982 5349 0428 7554 6873 1159 5628 6388 2353 7875 9375 1957 7818 5778 0532 
1712 2680 6613 0019 2787 6611 1959 0921 6420 1989 3809 5257 2010 6548 5863 2788 
(time (display-decimal newton-pi digits terms))
    8749 ms real time
    6280 ms cpu time (6260 user, 20 system)
    546 collections accounting for 789 ms real time (580 user, 10 system)
    208919200 bytes allocated
    no minor faults
    no major faults
2.
7182 8182 8459 0452 3536 0287 4713 5266 2497 7572 4709 3699 9595 7496 6967 6277 
2407 6630 3535 4759 4571 3821 7852 5166 4274 2746 6391 9320 0305 9921 8174 1359 
6629 0435 7290 0334 2952 6059 5630 7381 3232 8627 9434 9076 3233 8298 8075 3195 
2510 1901 1573 8341 8793 0702 1540 8914 9934 8841 6750 9244 7614 6066 8082 2648 
0016 8477 4118 5374 2345 4424 3710 7539 0777 4499 2069 5517 0276 1838 6062 6133 
1384 5830 0075 2044 9338 2656 0297 6067 3711 3200 7093 2870 9127 4437 4704 7230 
6969 7720 9310 1416 9283 6819 0255 1510 8657 4637 7211 1252 3897 8442 5056 9536 
9677 0785 4499 6996 7946 8644 5490 5987 9316 3688 9230 0987 9312 7736 1782 1542 
4999 2295 7635 1482 2082 6989 5193 6680 3318 2528 8693 9849 6465 1058 2093 9239 
8294 8879 3320 3625 0944 3117 3012 3819 7068 4161 4039 7019 8376 7932 0683 2823 
7646 4804 2953 1180 2328 7825 0981 9455 8153 0175 6717 3613 3206 9811 2509 9618 
1881 5930 4169 0351 5988 8851 9345 8072 7386 6738 5894 2287 9228 4998 9208 6805 
8257 4927 9610 4841 9844 4363 4632 4496 8487 5602 3362 4827 0419 7862 3209 0021 
6099 0235 3043 6994 1849 1463 1409 3431 7381 4364 0546 2531 5209 6183 6908 8870 
7016 7683 9642 4378 1405 9271 4563 5490 6130 3107 2085 1038 3750 5101 1574 7704 
1718 9861 0687 3969 6552 1267 1546 8895 7035 0354 0212 3407 8498 1933 4321 0681 
(time (display-decimal binary-splitting-e digits terms))
    1040 ms real time
    660 ms cpu time (630 user, 30 system)
    193 collections accounting for 308 ms real time (150 user, 0 system)
    74508896 bytes allocated
    no minor faults
    no major faults
3.
1415 9265 3589 7932 3846 2643 3832 7950 2884 1971 6939 9375 1058 2097 4944 5923 
0781 6406 2862 0899 8628 0348 2534 2117 0679 8214 8086 5132 8230 6647 0938 4460 
9550 5822 3172 5359 4081 2848 1117 4502 8410 2701 9385 2110 5559 6446 2294 8954 
9303 8196 4428 8109 7566 5933 4461 2847 5648 2337 8678 3165 2712 0190 9145 6485 
6692 3460 3486 1045 4326 6482 1339 3607 2602 4914 1273 7245 8700 6606 3155 8817 
4881 5209 2096 2829 2540 9171 5364 3678 9259 0360 0113 3053 0548 8204 6652 1384 
1469 5194 1511 6094 3305 7270 3657 5959 1953 0921 8611 7381 9326 1179 3105 1185 
4807 4462 3799 6274 9567 3518 8575 2724 8912 2793 8183 0119 4912 9833 6733 6244 
0656 6430 8602 1394 9463 9522 4737 1907 0217 9860 9437 0277 0539 2171 7629 3176 
7523 8467 4818 4676 6940 5132 0005 6812 7145 2635 6082 7785 7713 4275 7789 6091 
7363 7178 7214 6844 0901 2249 5343 0146 5495 8537 1050 7922 7968 9258 9235 4201 
9956 1121 2902 1960 8640 3441 8159 8136 2977 4771 3099 6051 8707 2113 4999 9998 
3729 7804 9951 0597 3173 2816 0963 1859 5024 4594 5534 6908 3026 4252 2308 2533 
4468 5035 2619 3118 8171 0100 0313 7838 7528 8658 7533 2083 8142 0617 1776 6914 
7303 5982 5349 0428 7554 6873 1159 5628 6388 2353 7875 9375 1957 7818 5778 0532 
1712 2680 6613 0019 2787 6611 1959 0921 6420 1989 3809 5257 2010 6548 5863 2788 
(time (display-decimal binary-splitting-atan-pi digits terms))
    1405 ms real time
    820 ms cpu time (820 user, 0 system)
    273 collections accounting for 404 ms real time (340 user, 0 system)
    104547336 bytes allocated
    no minor faults
    no major faults
3.
1415 9265 3589 7932 3846 2643 3832 7950 2884 1971 6939 9375 1058 2097 4944 5923 
0781 6406 2862 0899 8628 0348 2534 2117 0679 8214 8086 5132 8230 6647 0938 4460 
9550 5822 3172 5359 4081 2848 1117 4502 8410 2701 9385 2110 5559 6446 2294 8954 
9303 8196 4428 8109 7566 5933 4461 2847 5648 2337 8678 3165 2712 0190 9145 6485 
6692 3460 3486 1045 4326 6482 1339 3607 2602 4914 1273 7245 8700 6606 3155 8817 
4881 5209 2096 2829 2540 9171 5364 3678 9259 0360 0113 3053 0548 8204 6652 1384 
1469 5194 1511 6094 3305 7270 3657 5959 1953 0921 8611 7381 9326 1179 3105 1185 
4807 4462 3799 6274 9567 3518 8575 2724 8912 2793 8183 0119 4912 9833 6733 6244 
0656 6430 8602 1394 9463 9522 4737 1907 0217 9860 9437 0277 0539 2171 7629 3176 
7523 8467 4818 4676 6940 5132 0005 6812 7145 2635 6082 7785 7713 4275 7789 6091 
7363 7178 7214 6844 0901 2249 5343 0146 5495 8537 1050 7922 7968 9258 9235 4201 
9956 1121 2902 1960 8640 3441 8159 8136 2977 4771 3099 6051 8707 2113 4999 9998 
3729 7804 9951 0597 3173 2816 0963 1859 5024 4594 5534 6908 3026 4252 2308 2533 
4468 5035 2619 3118 8171 0100 0313 7838 7528 8658 7533 2083 8142 0617 1776 6914 
7303 5982 5349 0428 7554 6873 1159 5628 6388 2353 7875 9375 1957 7818 5778 0532 
1712 2680 6613 0019 2787 6611 1959 0921 6420 1989 3809 5257 2010 6548 5863 2788 
(time (display-decimal binary-splitting-newton-pi digits terms))
    1133 ms real time
    680 ms cpu time (660 user, 20 system)
    200 collections accounting for 245 ms real time (120 user, 0 system)
    85432840 bytes allocated
    no minor faults
    no major faults
#t
"/Users/lucier/pi-newton.scm"
From: Frank Buss
Subject: Re: The Joy of Pi
Date: 
Message-ID: <cn3e55$367$1@newsreader2.netcologne.de>
David Steuber <·····@david-steuber.com> wrote:

> Just for a lark, I implemented an infinite series for calculating Pi
> from David Blatner's "The Joy of Pi" (the title actually uses the
> Greek letter).  I used one of Newton's series on page 42.  This is the
> first time I've written a Lisp program that runs faster in CLisp than
> in SBCL.  OpenMCL runs it very slowly.

First I've tested it with LispWorks on my Pentium 1.5 GHz on Windows, 
which is VERY slow:

CL-USER > (time (pi-decimal 1025 2000))
Timing the evaluation of (PI-DECIMAL 1025 2000)
; Loading fasl file C:\Program Files\Xanalys\LispWorks\lib\4-3-0-0
\modules\util\callcoun.fsl

[...]

user time    =    269.557
system time  =      0.360
Elapsed time =   0:04:44
Allocation   = 11562961928 bytes standard / 573507 bytes conses
0 Page faults
Calls to %EVAL    35
NIL

With CLISP it is nearly 20 times faster (compiled):

Real time: 15.902867 sec.
Run time: 15.302003 sec.
Space: 26241288 Bytes
GC: 50, GC time: 0.0801152 sec.
NIL

> It might be interesting to
> make this program fast.  Mostly that would involve using a faster
> computing method.

you are right. Using a spigot algorithm (translated from tcl: 
http://wiki.tcl.tk/8401 ) you can write it like this:

(defun pi-2000 ()
  (let ((e 0)
        (f (make-array 3501 :initial-element 2000)))
    (loop for c from 3500 above 0 by 14 do
          (let ((d 0))
            (loop for b from c above 0 do
                  (let ((g (1- (* 2 b))))
                    (setf d (+ (* d b) (* (aref f b) 10000)))
                    (setf (aref f b) (mod d g))
                    (setf d (floor d g))))
            (multiple-value-bind (div mod) (floor d 10000)
              (format t "~4,'0D" (+ e div))
              (setf e mod))))))

In LispWorks:

CL-USER > (time (pi-2000))
Timing the evaluation of (PI-2000)
[...]
user time    =      0.881
system time  =      0.000
Elapsed time =   0:00:01
Allocation   = 12470624 bytes standard / 85030 bytes conses
0 Page faults
Calls to %EVAL    33
NIL

and in CLISP:

Real time: 4.987171 sec.
Run time: 4.897042 sec.
Space: 5802196 Bytes
GC: 11, GC time: 0.0100144 sec.
NIL

-- 
Frank Bu�, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Frank Buss
Subject: Re: The Joy of Pi
Date: 
Message-ID: <cn3g18$81g$1@newsreader2.netcologne.de>
Frank Buss <··@frank-buss.de> wrote:

> (defun pi-2000 ()

sorry, wrong name, it calculates only 1000 digits, like the other 
algorithm, to compare the time. Use 7001 and 7000 to calculate 2000 digits, 
which is fast, too.

-- 
Frank Bu�, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: David Steuber
Subject: Re: The Joy of Pi
Date: 
Message-ID: <87is8apnx2.fsf@david-steuber.com>
I'm going to have to look at some of those faster methods.  I did find
a series in the book that converges faster even though it looks like
it should take more work.  But this is as nothing compared to the
speedups mentioned elsewhere in this topic.  Anyway, here is my
revised code.  It may be a bit confusing that the print-bellard-pi
function takes the number of terms in the series rather than the
desired number of digits.  I also doubt that the tail digits are
correct.  I simply used the first way I thought of to terminate the
output that actually worked reasonably well.  Closures rock, btw.

·····@interloper:~/usr/src/pi
$ cat pi.lisp
;;; Calculate pi using one of Newton's series:
;;;
;;; pi/6 = 1/2 + 1/2(1/3x2^3) + ((1x3)/(2x4))(1/5x2^2) + 
;;; ((1x3x5)/(2x4x6))(1/7x2^7) + ...
;;;

(defun make-alternating-number-generator (first-number)
  (let ((num (- first-number 2)))
    (lambda () (incf num 2))))

(defun make-term-generator ()
  (let ((numerator-gen (make-alternating-number-generator 1))
        (denominator-gen (make-alternating-number-generator 2))
        (coeff-gen (make-alternating-number-generator 3)))
    (let ((n 1) (d 1))
      (lambda ()
        (setf n (* n (funcall numerator-gen))
              d (* d (funcall denominator-gen)))
        (let ((c (funcall coeff-gen)))
          (* (/ n d) (/ 1 (* c (expt 2 c)))))))))

(defun newton-pi (terms)
  (let ((accum 1/2)
        (term (make-term-generator)))
    (dotimes (i terms (* 6 accum))
      (incf accum (funcall term)))))

;;; Calculate Pi using the formula on page 118 of Joy of Pi.

(defun make-term-generator2 (&optional (nth-term 0))
  (let ((n (1- nth-term)))
    (lambda ()
      (incf n)
      (let ((8n (* 8 n)))
        (* (expt 1/16 n)
           (- (/ 4 (+ 8n 1))
              (/ 2 (+ 8n 4))
              (/ 1 (+ 8n 5))
              (/ 1 (+ 8n 6))))))))

(defun bellard-pi (terms)
  (let ((a 0)
        (termgen (make-term-generator2)))
    (dotimes (i terms a)
      (incf a (funcall termgen)))
    (values a (+ a (funcall termgen)))))

(defun make-digit-generator (r)
  (lambda ()
    (multiple-value-bind (digit rem) (floor r)
      (setf r (* 10 rem))
      digit)))

(defun print-bellard-pi (terms)
  (multiple-value-bind (p1 p2) (bellard-pi terms)
    (let ((g1 (make-digit-generator p1))
          (g2 (make-digit-generator p2)))
      (loop for d1 = (funcall g1) then (funcall g1)
            for d2 = (funcall g2) then (funcall g2)
            for i from 0
            while (equal d1 d2)
            do (progn
                 (princ d1)
                 (when (and (> i 0) (= 0 (mod i 5)))
                   (princ #\Space))
                 (when (and (> i 0) (= 0 (mod i 60)))
                   (terpri))
                 (when (= i 0) (princ ".") (terpri)))))))

·····@interloper:~/usr/src/pi
$ clisp
  i i i i i i i       ooooo    o        ooooooo   ooooo   ooooo
  I I I I I I I      8     8   8           8     8     o  8    8
  I  \ `+' /  I      8         8           8     8        8    8
   \  `-+-'  /       8         8           8      ooooo   8oooo
    `-__|__-'        8         8           8           8  8
        |            8     o   8           8     o     8  8
  ------+------       ooooo    8oooooo  ooo8ooo   ooooo   8

Copyright (c) Bruno Haible, Michael Stoll 1992, 1993
Copyright (c) Bruno Haible, Marcus Daniels 1994-1997
Copyright (c) Bruno Haible, Pierpaolo Bernardi, Sam Steingold 1998
Copyright (c) Bruno Haible, Sam Steingold 1999-2000
Copyright (c) Sam Steingold, Bruno Haible 2001-2004


[1]> (load (compile-file "pi.lisp"))

Compiling file /Users/david/usr/src/pi/pi.lisp ...

Wrote file /Users/david/usr/src/pi/pi.fas
0 errors, 0 warnings
;; Loading file /Users/david/usr/src/pi/pi.fas ...
;; Loaded file /Users/david/usr/src/pi/pi.fas
T
[2]> (time (print-bellard-pi 2000))
3.
14159 26535 89793 23846 26433 83279 50288 41971 69399 37510 58209 74944 
59230 78164 06286 20899 86280 34825 34211 70679 82148 08651 32823 06647 
09384 46095 50582 23172 53594 08128 48111 74502 84102 70193 85211 05559 
64462 29489 54930 38196 44288 10975 66593 34461 28475 64823 37867 83165 
27120 19091 45648 56692 34603 48610 45432 66482 13393 60726 02491 41273 
72458 70066 06315 58817 48815 20920 96282 92540 91715 36436 78925 90360 
01133 05305 48820 46652 13841 46951 94151 16094 33057 27036 57595 91953 
09218 61173 81932 61179 31051 18548 07446 23799 62749 56735 18857 52724 
89122 79381 83011 94912 98336 73362 44065 66430 86021 39494 63952 24737 
19070 21798 60943 70277 05392 17176 29317 67523 84674 81846 76694 05132 
00056 81271 45263 56082 77857 71342 75778 96091 73637 17872 14684 40901 
22495 34301 46549 58537 10507 92279 68925 89235 42019 95611 21290 21960 
86403 44181 59813 62977 47713 09960 51870 72113 49999 99837 29780 49951 
05973 17328 16096 31859 50244 59455 34690 83026 42522 30825 33446 85035 
26193 11881 71010 00313 78387 52886 58753 32083 81420 61717 76691 47303 
59825 34904 28755 46873 11595 62863 88235 37875 93751 95778 18577 80532 
17122 68066 13001 92787 66111 95909 21642 01989 38095 25720 10654 85863 
27886 59361 53381 82796 82303 01952 03530 18529 68995 77362 25994 13891 
24972 17752 83479 13151 55748 57242 45415 06959 50829 53311 68617 27855 
88907 50983 81754 63746 49393 19255 06040 09277 01671 13900 98488 24012 
85836 16035 63707 66010 47101 81942 95559 61989 46767 83744 94482 55379 
77472 68471 04047 53464 62080 46684 25906 94912 93313 67702 89891 52104 
75216 20569 66024 05803 81501 93511 25338 24300 35587 64024 74964 73263 
91419 92726 04269 92279 67823 54781 63600 93417 21641 21992 45863 15030 
28618 29745 55706 74983 85054 94588 58692 69956 90927 21079 75093 02955 
32116 53449 87202 75596 02364 80665 49911 98818 34797 75356 63698 07426 
54252 78625 51818 41757 46728 90977 77279 38000 81647 06001 61452 49192 
17321 72147 72350 14144 19735 68548 16136 11573 52552 13347 57418 49468 
43852 33239 07394 14333 45477 62416 86251 89835 69485 56209 92192 22184 
27255 02542 56887 67179 04946 01653 46680 49886 27232 79178 60857 84383 
82796 79766 81454 10095 38837 86360 95068 00642 25125 20511 73929 84896 
08412 84886 26945 60424 19652 85022 21066 11863 06744 27862 20391 94945 
04712 37137 86960 95636 43719 17287 46776 46575 73962 41389 08658 32645 
99581 33904 78027 59009 94657 64078 95126 94683 98352 59570 98258 22620 
52248 94077 26719 47826 84826 01476 99090 26401 36394 43745 53050 68203 
49625 24517 49399 65143 14298 09190 65925 09372 21696 46151 57098 58387 
41059 78859 59772 97549 89301 61753 92846 81382 68683 86894 27741 55991 
85592 52459 53959 43104 99725 24680 84598 72736 44695 84865 38367 36222 
62609 91246 08051 24388 43904 51244 13654 97627 80797 71569 14359 97700 
12961 60894 41694 86855 58484 06353 42207 22258 28488 64815 84560 28506 
01684 27394 52267 
Real time: 23.431875 sec.
Run time: 11.47 sec.
Space: 65206600 Bytes
GC: 111, GC time: 3.12 sec.
NIL
[3]> 

-- 
An ideal world is left as an excercise to the reader.
   --- Paul Graham, On Lisp 8.1
From: Greg Menke
Subject: Re: The Joy of Pi
Date: 
Message-ID: <m38y95q3re.fsf@europa.pienet>
Frank Buss <··@frank-buss.de> writes:


> David Steuber <·····@david-steuber.com> wrote:
> 
> > Just for a lark, I implemented an infinite series for calculating Pi
> > from David Blatner's "The Joy of Pi" (the title actually uses the
> > Greek letter).  I used one of Newton's series on page 42.  This is the
> > first time I've written a Lisp program that runs faster in CLisp than
> > in SBCL.  OpenMCL runs it very slowly.
> 
> First I've tested it with LispWorks on my Pentium 1.5 GHz on Windows, 
> which is VERY slow:


Did you compile pi-decimal?


Gregm
From: Frank Buss
Subject: Re: The Joy of Pi
Date: 
Message-ID: <cn537b$r48$1@newsreader2.netcologne.de>
Greg Menke <··········@toadmail.com> wrote:

> Did you compile pi-decimal?

yes. Perhaps the bignum support is slow in LispWorks or I did something 
other wrong.

-- 
Frank Bu�, ··@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
From: Greg Menke
Subject: Re: The Joy of Pi
Date: 
Message-ID: <m3y8h5crvf.fsf@europa.pienet>
Frank Buss <··@frank-buss.de> writes:


> Greg Menke <··········@toadmail.com> wrote:
> 
> > Did you compile pi-decimal?
> 
> yes. Perhaps the bignum support is slow in LispWorks or I did something 
> other wrong.
> 

Did adding (declare ... ) for the types have any effect?  Interesting
you found such a big performance difference though.  I've found
Lispworks vastly faster than clisp in other ways though.  OTOH,
different implementations have different strengths...

Gregm
From: Fred Gilham
Subject: Re: The Joy of Pi
Date: 
Message-ID: <u7fz3bf0w7.fsf@snapdragon.csl.sri.com>
Here's a Series version of the "pi-decimal" program.  It is just about
identical in performance to the non-series version.

Running under CMUCL on my machine (1.4GHz Athlon) it takes about 10
seconds.  The CLISP version takes about 5 seconds.  I couldn't get it
to compile under SBCL.  Does the Series package compile under SBCL?

I also tried this with ACL 6.2 but it's about 2 orders of magnitude
slower.  I guess bignum performance wasn't a priority in that
implementation.

Disclaimer: I'm not a Series expert.  Possibly someone else could do
this in a clever way.  Mine is more a brute-force translation of the
original.  I do like the way the Series version cleanly unfolds the
pieces of the computation without compromising efficiency.

Anyway here it is:

;;; Calculate pi using one of Newton's series:
;;;
;;; pi/6 = 1/2 + 1/2(1/3x2^3) + ((1x3)/(2x4))(1/5x2^2) +
;;; ((1x3x5)/(2x4x6))(1/7x2^7) + ...
;;;
;;; Series version.
;;;

(eval-when (:compile-toplevel :load-toplevel)
  ;; This is if you have installed Series as a CMUCL subsystem.
  #+:CMU
  (require :series)
  #-:CMU
  (progn
    (load "s-package")
    (load "s-code")))

(eval-when (:compile-toplevel :load-toplevel)
  (series::install))

(defun alternating-number-series (first-number)
  (declare (optimizable-series-function 1))
  (scan-range :from first-number :by 2 :type integer))

(defun numerator-series ()
  (declare (optimizable-series-function 1))  
  (collecting-fn 'integer
		 #'(lambda () 1)
		 #'*
		 (alternating-number-series 1)))

(defun denominator-series ()
  (declare (optimizable-series-function 1))
  (collecting-fn 'integer
		 #'(lambda () 1)
		 #'*
		 (alternating-number-series 2)))

(defun coefficient-series ()
  (declare (optimizable-series-function 1))
  (alternating-number-series 3))


(defun term-series ()
  (declare (optimizable-series-function 1))
  (mapping ((n (numerator-series))
	    (d (denominator-series))
	    (c (coefficient-series)))
     (* (/ n d) (/ 1 (* c (expt 2 c))))))


(defun newton-pi (terms)
  (let ((term-series (term-series)))
    (* 6 (+ 1/2 (collect-sum (subseries term-series 0 terms) 'rational)))))


(defun digit-series (number)
  (declare (optimizable-series-function 1))
  (scan-fn '(values fixnum rational)
	   #'(lambda () (floor number))
	   #'(lambda (number remainder)
	       (declare (ignore number))
	       (floor (* 10 remainder)))))


(defun pi-decimal (digits &optional (terms 100))
  (let* ((pi-rational (newton-pi terms)))
    (iterate ((digit (digit-series pi-rational))
	      (i (scan-range :from 0 :by 1 :below digits)))
      (princ digit)
      (when (and (> i 0) (= 0 (mod i 4)))
	(princ #\Space))
      (when (and (> i 0) (= 0 (mod i 64)))
	(terpri))
      (when (= i 0) (princ ".") (terpri)))))


-- 
Fred Gilham                     ······@csl.sri.com
"In the 20th century, more citizens were killed by their own
governments than by foreign enemies....totalitarianism first of all
regards its own people as the enemy." --- Arnold Beichman
From: Fred Gilham
Subject: Re: The Joy of Pi
Date: 
Message-ID: <u77jom9b3x.fsf@snapdragon.csl.sri.com>
I wrote:
> (defun newton-pi (terms)
>   (let ((term-series (term-series)))
>     (* 6 (+ 1/2 (collect-sum (subseries term-series 0 terms) 'rational)))))

which has a little left-over cruft from the development process.

(defun newton-pi (terms)
   (* 6 (+ 1/2 (collect-sum (subseries (term-series) 0 terms) 'rational)))


Similarly,

> (defun pi-decimal (digits &optional (terms 100))
>   (let* ((pi-rational (newton-pi terms)))
>     (iterate ((digit (digit-series pi-rational))
> 	      (i (scan-range :from 0 :by 1 :below digits)))
>       (princ digit)
>       (when (and (> i 0) (= 0 (mod i 4)))
> 	(princ #\Space))
>       (when (and (> i 0) (= 0 (mod i 64)))
> 	(terpri))
>       (when (= i 0) (princ ".") (terpri)))))

should be

(defun pi-decimal (digits &optional (terms 100))
  (iterate ((digit (digit-series (newton-pi terms)))
            (i (scan-range :from 0 :by 1 :below digits)))
    (princ digit)
    (when (and (> i 0) (= 0 (mod i 4)))
      (princ #\Space))
    (when (and (> i 0) (= 0 (mod i 64)))
      (terpri))
    (when (= i 0) (princ ".") (terpri)))))


-- 
Fred Gilham   ······@csl.sri.com | If sophists such as Richard Rorty
are correct, and the West has moved from a post-religious age to a
post-metaphysical age, then there is literally nothing Western left
about the West to defend.                           --David Dieteman
From: David Steuber
Subject: Re: The Joy of Pi
Date: 
Message-ID: <87ekiof2xi.fsf@david-steuber.com>
I messed about with this some more today.

Frank Buss <··@frank-buss.de> writes:

> David Steuber <·····@david-steuber.com> wrote:
> 
> > It might be interesting to make this program fast.  Mostly that
> > would involve using a faster computing method.
> 
> you are right. Using a spigot algorithm (translated from tcl: 
> http://wiki.tcl.tk/8401 ) you can write it like this:
> 
> (defun pi-2000 ()
>   (let ((e 0)
>         (f (make-array 3501 :initial-element 2000)))
>     (loop for c from 3500 above 0 by 14 do
>           (let ((d 0))
>             (loop for b from c above 0 do
>                   (let ((g (1- (* 2 b))))
>                     (setf d (+ (* d b) (* (aref f b) 10000)))
>                     (setf (aref f b) (mod d g))
>                     (setf d (floor d g))))
>             (multiple-value-bind (div mod) (floor d 10000)
>               (format t "~4,'0D" (+ e div))
>               (setf e mod))))))

This is a very interesting algorithm and I can't figure out how it
works.  I may have to get the book, "Pi Unleashed" that is reviewed
here:

  http://www.maa.org/reviews/piunleashed.html

I did some hunting around for the article in AMM that was cited on the
Wiki page as well as here:

  http://home.att.net/~srschmitt/pi-spigot.html

But I've had no luck so far.

I also did some minor tweaking of the code:

(defun pi-spigot ()
  (let ((e 0)
        (f (make-array 3501 :initial-element 2000)))
    (symbol-macrolet ((f[b] (aref f b)))
      (loop for c from 3500 above 0 by 14 do
           (let ((d 0))
             (loop for b from c above 0 do
                  (let ((g (1- (* 2 b))))
                    (multiple-value-setq (d f[b])
                      (floor (+ (* d b) (* f[b] 10000)) g))))
             (multiple-value-bind (div mod) (floor d 10000)
               (format t "~4,'0D" (+ e div))
               (setf e mod)))))))

The symbol-macrolet allowed me to dump the redundant call to mod and
use multiple-value-setq.  I also dropped a setf to d.  It runs a shade
faster, but the tweaks didn't help me gain any insight into the
algorithm.  My hope was to find a way to do away with the large
array.  Say that three times fast.  The array holds state information
used for later digits.  I guess the array doesn't count as part of the
outputs in, "A spigot algorithm produces its outputs incrementally,
and does not reuse them after producing them."

Bruno Haible is mentioned in the book review for writing a cln library
that is included with Pi Unleashed.  I thought that was pretty neat.

-- 
An ideal world is left as an excercise to the reader.
   --- Paul Graham, On Lisp 8.1
From: David Steuber
Subject: Re: The Joy of Pi
Date: 
Message-ID: <87ekinksb6.fsf@david-steuber.com>
Following up on myself here, mostly to use Google as a back up for my
file :-)

I cleaned up some of the Pi code I have.  Fred Gilham's use of series
is pretty interesting, but I don't have that installed.  As I said
before, I also like Common Lisp's closures.  I don't know what
performance hit I take using them, but they make these sums easier to
think about.

In the code below, I have three different series.  I haven't done any
timings, but I suspect that the series that converge faster (ie,
produce more digits of Pi for n terms) are more efficient.  The best
series seem to use roots which I have not figured out how to code.

Here is pi.lisp as it exists now:

;;; Calculate Pi using one of Newton's series:
;;;
;;; Pi/6 = 1/2 + 1/2(1/3x2^3) + ((1x3)/(2x4))(1/5x2^2) + 
;;;        ((1x3x5)/(2x4x6))(1/7x2^7) + ...
;;;

(defun make-alternating-number-generator (first-number)
  (let ((num (- first-number 2)))
    (lambda () (incf num 2))))

(defun make-term-generator ()
  (let ((numerator-gen (make-alternating-number-generator 1))
        (denominator-gen (make-alternating-number-generator 2))
        (coeff-gen (make-alternating-number-generator 3)))
    (let ((n 1) (d 1))
      (lambda ()
        (setf n (* n (funcall numerator-gen))
              d (* d (funcall denominator-gen)))
        (let ((c (funcall coeff-gen)))
          (* (/ n d) (/ 1 (* c (expt 2 c)))))))))

(defun newton-pi (terms)
  (let ((a 1/2)
        (term (make-term-generator)))
    (dotimes (i terms)
      (incf a (funcall term)))
    (values (* 6 a) (* 6 (+ a (funcall term))))))

;;; Calculate Pi using the formula on page 118 of Joy of Pi.
;;; It is also at this URL:
;;;
;;;  http://mathworld.wolfram.com/BBPFormula.html
;;;

(defun make-bbp-term-generator (&optional (nth-term 0))
  (let ((n (1- nth-term)))
    (lambda ()
      (incf n)
      (let ((8n (* 8 n)))
        (* (expt 1/16 n)
           (- (/ 4 (+ 8n 1))
              (/ 2 (+ 8n 4))
              (/ 1 (+ 8n 5))
              (/ 1 (+ 8n 6))))))))

(defun bbp-pi (terms)
  (let ((a 0)
        (termgen (make-bbp-term-generator)))
    (dotimes (i terms)
      (incf a (funcall termgen)))
    (values a (+ a (funcall termgen)))))

;;; Calculate Pi using Rabinowitz and Wagon series:
;;;
;;;       inf  n!^2 x 2^(n+1)
;;; Pi = sigma --------------
;;;       n=0    (2n + 1)!
;;;

(let ((factorials (make-array 100 :adjustable t :initial-element nil)))
  (defun n! (n)
    "Returns n!.  Memoization via an array for speed is done."
    (when (< (length factorials) n)
      (adjust-array factorials (+ n 100) :initial-element nil))
    (if (< n 1)
        1
        (let ((f (aref factorials (1- n))))
          (if f
              f
              (setf (aref factorials (1- n))
                    (* n (n! (1- n)))))))))

(defun make-rabinowitz-wagon-term-generator (&optional (nth-term 0))
  (let ((n (1- nth-term)))
    (lambda ()
      (incf n)
      (/ (* (expt (n! n) 2)
            (expt 2 (1+ n)))
         (n! (1+ (* 2 n)))))))

(defun rabinowitz-wagon-pi (terms)
  (let ((a 0)
        (termgen (make-rabinowitz-wagon-term-generator)))
    (dotimes (i terms)
      (incf a (funcall termgen)))
    (values a (+ a (funcall termgen)))))

;;; Decimal expansion of above series.  Pi is returned as a rational
;;; approximation along with the next term so that
;;; print-pi-decimal-expansion can terminate when the digits diverge.

(defun make-digit-generator (r)
  (lambda ()
    (multiple-value-bind (digit rem) (floor r)
      (setf r (* 10 rem))
      digit)))

(defun print-pi-decimal-expansion (fn terms)
  (multiple-value-bind (p1 p2) (funcall fn terms)
    (let ((g1 (make-digit-generator p1))
          (g2 (make-digit-generator p2)))
      (loop for d1 = (funcall g1) then (funcall g1)
            for d2 = (funcall g2) then (funcall g2)
            for i from 0
            while (equal d1 d2)
            do (progn
                 (princ d1)
                 (when (and (> i 0) (= 0 (mod i 5)))
                   (princ #\Space))
                 (when (and (> i 0) (= 0 (mod i 60)))
                   (terpri))
                 (when (= i 0) (princ ".") (terpri)))
            finally (return i)))))

;;; Spigot algorithm

(defun pi-1000 ()
  (let ((e 0)
        (f (make-array 3501 :initial-element 2000)))
    (loop for c from 3500 above 0 by 14 do
          (let ((d 0))
            (loop for b from c above 0 do
                  (let ((g (1- (* 2 b))))
                    (setf d (+ (* d b) (* (aref f b) 10000)))
                    (setf (aref f b) (mod d g))
                    (setf d (floor d g))))
            (multiple-value-bind (div mod) (floor d 10000)
              (format t "~4,'0D" (+ e div))
              (setf e mod))))))

(defun pi-spigot ()
  (let ((e 0)
        (f (make-array 3501 :initial-element 2000)))
    (symbol-macrolet ((f[b] (aref f b)))
      (loop for c from 3500 above 0 by 14 do
           (let ((d 0))
             (loop for b from c above 0 do
                  (let ((g (1- (* 2 b))))
                    (multiple-value-setq (d f[b])
                      (floor (+ (* d b) (* f[b] 10000)) g))))
             (multiple-value-bind (div mod) (floor d 10000)
               (format t "~4,'0D" (+ e div))
               (setf e mod)))))))

-- 
An ideal world is left as an excercise to the reader.
   --- Paul Graham, On Lisp 8.1
From: Fred Gilham
Subject: Re: The Joy of Pi
Date: 
Message-ID: <u7sm72hqdh.fsf@snapdragon.csl.sri.com>
David Steuber wrote:
> I cleaned up some of the Pi code I have.  Fred Gilham's use of
> series is pretty interesting, but I don't have that installed.

Someone took the Series package and complexicated it.  So I used my
decomplexicator on it and put it on my ftp site.

   ftp.csl.sri.com:/pub/users/gilham/series.lisp

This is the 2.2.7 version from Sourceforge after running it through
the decomplexicator.  It runs under CMUCL and CLISP at least.

Just compile this file and load it.  Then if you want the series
symbols to be in your current package type (series::install).

Doesn't include documentation --- you'll have to snag that off the web
yourself.

If you have CMUCL installed in, say, /usr/local, and you want to use
Series as a library package, you can copy series.x86f (or whatever the
fasl file is called for your CMUCL) to
/usr/local/lib/cmucl/lib/subsystems/series-library.x86f.  Then you can
do

(require :series)

whenever you want to use the package.  Very convenient.  I do this for
a number of packages.

You can do this for pretty much any package by just cat-ing all the
fasl files together (in the right order, of course) and copying them
to the lib/subsystems directory, naming them <package>-library.x86f.

-- 
Fred Gilham                                      ······@csl.sri.com
Top five reasons why the God of Jesus Christ makes a better God than
`Caesar':
5. God only wants 10%.
4. God has fewer laws.
3. God has a clear understanding of the difference between destroying
   things and saving them.
2. God has a better retirement package.
1. Caesar wants you to send your sons to die for him.  God sent his
   Son to die for you.
From: Raymond Toy
Subject: Re: The Joy of Pi
Date: 
Message-ID: <sxdvfbx53bt.fsf@rtp.ericsson.se>
>>>>> "Fred" == Fred Gilham <······@snapdragon.csl.sri.com> writes:

    Fred> David Steuber wrote:
    >> I cleaned up some of the Pi code I have.  Fred Gilham's use of
    >> series is pretty interesting, but I don't have that installed.

    Fred> Someone took the Series package and complexicated it.  So I used my

What does complexicated mean?

Ray
From: Fred Gilham
Subject: Re: The Joy of Pi
Date: 
Message-ID: <u7mzx9isz7.fsf@snapdragon.csl.sri.com>
Ray Toy asked:
> What does complexicated mean?

Well, the Series package used to be a single file (not counting the
test code).  Someone split it up so you have to load at least 2 files
to get it to run.

This has apparently proved to be an obstacle to people.

My decomplexicator, aka emacs, allowed me to put series back into a
single file that can be compiled, loaded and run in a simple fashion.

-- 
Fred Gilham                                       ······@csl.sri.com
"Due to inclement weather and massive amounts of ice everywhere,
tonight's Healthy Environment Forum on Global Warming with Dr. Patz
has been postponed", wrote Sarah Doll of the Oregon Environmental
Council in an email sent to the press, "Sorry for any inconvenience
and hope you are staying warm."                       --- John Daly
From: Raymond Toy
Subject: Re: The Joy of Pi
Date: 
Message-ID: <sxdekil4pl4.fsf@rtp.ericsson.se>
>>>>> "Fred" == Fred Gilham <······@snapdragon.csl.sri.com> writes:

    Fred> Ray Toy asked:
    >> What does complexicated mean?

    Fred> Well, the Series package used to be a single file (not counting the
    Fred> test code).  Someone split it up so you have to load at least 2 files
    Fred> to get it to run.

Ah, I see.  It's 3 files now.  One for the package definitions, which
I think Tim Bradshaw said was a good idea; one for the series stuff;
and one to install it.  I think it's a bad idea to install series
automatically, so we wanted the user to explicitly install it.

Ray