Rename specvar integer-info to *integer-info*
[maxima.git] / share / contrib / binsplit / binsplit.lisp
blob8f83b5bb8bc03cc90db71884c31463764baa35fc
2 ;; exp, log, sin, asin and friends for bigfloats in Maxima
3 ;;
4 ;; An implementation of the binary splitting algorithms
5 ;;
6 ;; described in http://www.ginac.de/CLN/binsplit.pdf .
7 ;;
8 ;;
9 ;; Copyright (C) 2014 by Volker van Nek
10 ;;
11 ;; Released under the terms of the GNU General Public License
12 ;;
15 ;; Functions at Maxima level
16 ;;
17 ;; bs_asin(x), bs_acos(x), where 0 <= x <= 1
18 ;; bs_atan(x), bs_acot(x) 0 <= x
19 ;; bs_sin(x), bs_cos(x) 0 <= x <= pi/2
20 ;; bs_tan(x), bs_cot(x) 0 <= x < pi/2
21 ;; bs_exp(x) 0 <= x < 1
22 ;; bs_log(x) x > 0
23 ;; bs_expm1(x) 0 <= x < log(2)
24 ;; bs_log1p(eps) eps > -1
25 ;; x and eps are real valued bigfloats
28 ;;----------------------------------------------------------------------------;;
29 ;; Compute phi = asin(x) or acos(x), where 0 <= x <= 1
31 ;; or phi = atan(x) or acot(x), where 0 <= x.
33 (defun $bs_asin (x) (bs-asincos x 'asin)) ;; top level test functions
34 (defun $bs_acos (x) (bs-asincos x 'acos))
35 (defun $bs_atan (x) (bs-asincos x 'atan))
36 (defun $bs_acot (x) (bs-asincos x 'acot))
38 (defun bs-asincos (x fn)
39 (let ((phi ;; phi = carg(z), where ..
40 (let* ((fpprec (+ fpprec 12)) ;; needs testing
41 (x (cdr (bigfloatp x)))
42 (xx (fptimes* x x))
43 (one (fpone))
44 y re-z im-z )
45 (cond
46 ((or (eq fn 'asin) (eq fn 'acos))
47 (setq y (fproot (bcons (fpdifference one xx)) 2))
48 (if (eq fn 'asin)
49 ;; asin: .. z = sqrt(1-x^2) + %i * x
50 (setq re-z y im-z x)
51 ;; acos: .. z = x + %i * sqrt(1-x^2)
52 (setq re-z x im-z y) ))
54 (setq y (fpquotient one (fproot (bcons (fpplus one xx)) 2)))
55 (if (eq fn 'atan)
56 ;; atan: .. z = 1/sqrt(x^2+1) + %i * x/sqrt(x^2+1)
57 (setq re-z y im-z (fptimes* x y))
58 ;; acot: .. z = x/sqrt(x^2+1) + %i * 1/sqrt(x^2+1)
59 (setq re-z (fptimes* x y) im-z y) )))
60 (bcons (bs-carg1 re-z im-z fpprec)) )))
61 (bigfloatp phi) ))
63 ;;----------------------------------------------------------------------------;;
65 ;;----------------------------------------------------------------------------;;
66 ;; Compute phi = carg(z) in quadrant I, where |z| = 1.
68 ;; im(z) < 1/2: Algorithm bs-carg11 moves z along the unit circle down to the real axis
70 ;; while counting the angular movement (Haible & Papanikolaou, 2.2.8).
72 ;; im(z) < 1/sqrt(2): phi/2 = carg(sqrt(z)).
74 ;; im(z) = 1/sqrt(2): phi = pi/4.
76 ;; im(z) > 1/sqrt(2): pi/2 - phi = carg(imagpart(z) + %i*realpart(z)).
77 ;;
78 (defun bs-carg1 (re-z im-z prec) ;; assumes |z| = 1, quadrant 1
79 (cond
80 ((zerop (car (fpdifference re-z im-z)))
81 (let ((%pi (fppi))) (list (car %pi) (- (cadr %pi) 2))) ) ;; pi/4
82 ((fpgreaterp im-z re-z) ;; im-z > 1/sqrt(2) (i.e. phi > pi/4)
83 (let* ((%pi (fppi))
84 (%pi/2 (list (car %pi) (1- (cadr %pi)))) )
85 (fpdifference %pi/2 (bs-carg1 im-z re-z prec)) ))
87 (let ((e 0) phi)
88 ;; bs-carg11 needs im-z < 1/2 (i.e. phi < pi/6)
89 ;; when im-z >= 1/2 bisect phi by taking the complex sqrt
90 (when (not (fplessp im-z (cdr bfhalf)))
91 (multiple-value-setq (re-z im-z) (complex-sqrt (bcons re-z) (bcons im-z)))
92 (setq e 1) )
93 (setq phi (bs-carg11 (list re-z im-z) prec))
94 (list (car phi) (+ (cadr phi) e)) ))))
96 (defun bs-carg11 (z prec) ;; assumes |z| = 1, 0 < Re(z) <= 1, 0 <= Im(z) < 1/2
97 (let ((one (fpone))
98 k u e alpha )
99 (do ((phi (list 0 0))
100 (i 1 (1+ i))
101 (im-z (cadr z) (cadr z)) )
102 ((not (fpgreaterp (fpplus one im-z) one)) phi) ;; stop when im-z = 0 within current prec
103 (setq k (- (cadr im-z)) ;; im-z < 2^-k
104 u (ldb (byte k (- prec k)) (car im-z)) ;; first k significant mantissa bit
105 e (complex-exp-%i*u/2^2k (- u) k prec)
106 z (complex-fptimes* z e)
107 alpha (intofp u)
108 alpha (list (car alpha) (- (cadr alpha) (* 2 k)))
109 phi (fpplus phi alpha) ))))
111 (defun complex-exp-%i*u/2^2k (u k prec) ;; ;; compute exp(%i*u/2^2k) where u < 2^k, integer u,k
112 (if (= 0 u)
113 (list (fpone) (list 0 0))
114 (let ((z (* 2 k))
115 (kk k) ) ;; see (**) below why k is param for bs-exp-u/2^z-series-size
116 (complex-exp-%i*u/2^z u z kk prec) )))
118 (defun split-bs-exp-u/2^z (i j u z) ;; u is integer or complex with integer parts
119 (let (pp qq zz tt)
120 (if (= (- j i) 1)
121 (if (= i 0)
122 (setq pp 1 qq 1 zz 0 tt 1)
123 (setq pp u qq i zz z tt pp) )
124 (let ((m (ash (+ i j) -1)))
125 (multiple-value-bind (tl zl ql pl) (split-bs-exp-u/2^z i m u z)
126 (multiple-value-bind (tr zr qr pr) (split-bs-exp-u/2^z m j u z)
127 (setq pp (* pl pr)
128 qq (* ql qr)
129 zz (+ zl zr)
130 tt (* qr tl)
131 tt (if (complexp u)
132 (apply #'complex
133 (mapcar #'(lambda (n) (ash n zr))
134 (list (realpart tt) (imagpart tt)) ))
135 (ash tt zr) )
136 tt (+ tt (* pl tr)) )))))
137 (values tt zz qq pp) ))
139 (defun complex-fptimes* (a b)
140 (let ((x1 (car a)) (y1 (cadr a))
141 (x2 (car b)) (y2 (cadr b)) )
142 (list
143 (fpdifference (fptimes* x1 x2) (fptimes* y1 y2))
144 (fpplus (fptimes* x1 y2) (fptimes* x2 y1)) )))
146 (defun complex-fpsquare (a)
147 (let* ((x (car a)) (y (cadr a))
148 (xy (fptimes* x y)) )
149 (list
150 (fpdifference (fptimes* x x) (fptimes* y y))
151 (list (car xy) (1+ (cadr xy))) )))
153 ;;----------------------------------------------------------------------------;;
156 ;;----------------------------------------------------------------------------;;
157 ;; Compute sin(x), cos(x), tan(x), cot(x), where 0 <= x <= %pi/2
159 (defun $bs_sin (x) (bs-sincos x 'sin)) ;; top level test functions
160 (defun $bs_cos (x) (bs-sincos x 'cos))
161 (defun $bs_tan (x) (bs-sincos x 'tan))
162 (defun $bs_cot (x) (bs-sincos x 'cot))
164 (defun bs-sincos (x fn) ;; 0 <= x <= pi/2
165 (let ((xx (cdr x)) pi/1 pi/2 dx)
166 (cond
167 ((or (= 0 (car xx)) (< (cadr xx) (- fpprec))) ;; x is zero within current prec
168 (cond ((eql fn 'sin) xx)
169 ((eql fn 'cos) (intofp 1))
170 ((eql fn 'tan) xx)
171 ((eql fn 'cot) (merror (intl:gettext "attempted quotient by zero."))) ))
172 ((and (setq pi/1 (fppi)
173 pi/2 (list (car pi/1) (1- (cadr pi/1)))
174 dx (fpdifference pi/2 xx) )
175 (or (= 0 (car dx)) (< (cadr dx) (- fpprec))) ) ;; x is pi/2 within current prec
176 (cond ((eql fn 'sin) (intofp 1))
177 ((eql fn 'cos) dx)
178 ((eql fn 'tan) (merror (intl:gettext "attempted quotient by zero.")))
179 ((eql fn 'cot) dx) ))
181 (let ((res
182 (let* ((fpprec (+ fpprec 12)) ;; needs testing
183 (x (cdr (bigfloatp x))) )
184 (bcons
185 (multiple-value-bind (re im) (bs-exp-%i*x x fpprec)
186 (cond ((eq fn 'sin) im)
187 ((eq fn 'cos) re)
188 ((eq fn 'tan) (fpquotient im re))
189 ((eq fn 'cot) (fpquotient re im)) ))))))
190 (bigfloatp res) )))))
192 (defun bs-exp-%i*x (x prec) ;; assume 0 <= x <= pi/2
193 (let ((mant (car x)) (e (cadr x))
194 (acc (list (intofp 1) (list 0 0))) ;; complex-(1,0)
195 x/2? )
196 (when (fpgreaterp x (floattofp (/ pi 4))) ;; is x > pi/4 ?
197 (setq e (1- e) ;; x <-- x/2
198 x/2? t ))
199 ;; now x <= pi/4 (the algorithm needs x < 1)
200 (do ((k 0 (1+ k))
201 (nr 1)
202 (pos1 1 (ash pos1 1))
203 (pos2 1) u tmp )
204 ((= 0 pos2))
205 (setq pos2 (- prec (+ pos1 e)))
206 (when (< pos2 0)
207 (setq mant (ash mant (- pos2))
208 pos2 0 ))
209 (setq u (ldb (byte nr pos2) mant)
210 tmp (complex-exp-%i*u/2^2^k u k prec)
211 acc (complex-fptimes* acc tmp)
212 nr pos1 ))
213 (when x/2? (setq acc (complex-fpsquare acc))) ;; exp(%i*x) = exp(%i*x/2)^2
214 (apply #'values acc) ))
216 (defun complex-exp-%i*u/2^2^k (u k prec) ;; compute exp(%i*u/2^2^k) where u < 2^2^(k-1) resp. 2^-1 for k=0, integer u,k
217 (if (= 0 u)
218 (list (fpone) (list 0 0))
219 (let ((z (ash 1 k))
220 (kk (if (= k 0) 1/2 (ash 1 (1- k)))) ) ;; param for bs-exp-u/2^z-series-size, see (*)
221 (complex-exp-%i*u/2^z u z kk prec) )))
223 (defun complex-exp-%i*u/2^z (u z kk prec)
224 (let ((nr (bs-exp-u/2^z-series-size kk prec))
225 x y )
226 (do ((i 0))
227 ((logbitp i u)
228 (setq u (ash u (- i)) z (- z i)) )
229 (incf i) )
230 (multiple-value-bind (tt zz qq) (split-bs-exp-u/2^z 0 (1+ nr) (complex 0 u) z)
231 (setq qq (intofp qq)
232 x (fpquotient (intofp (realpart tt)) qq)
233 y (fpquotient (intofp (imagpart tt)) qq)
234 x (list (car x) (- (cadr x) zz))
235 y (list (car y) (- (cadr y) zz)) )
236 (list x y) )))
238 ;;----------------------------------------------------------------------------;;
241 ;;----------------------------------------------------------------------------;;
242 ;; exp(x)-1 for 0 <= x < log(2)
244 ;; x = mantissa*2^e = 2^(e-1) + ... where e <= 0
246 ;; exp(x) = 1 + x + x^2/2! + ... = 1 + 2^(e-1) + ...
248 ;; exp(x)-1 looses |e-1| bits precision
250 (defun $bs_expm1 (x) (bs-expm1 x)) ;; top level test function
252 (defun bs-expm1 (x) ;; 0 <= x < log(2)
253 (setq x (bigfloatp x))
254 (let* ((extra (1+ (- (caddr x))))
255 (res
256 (let ((fpprec (+ fpprec extra)))
257 (bcons (fpdifference (cdr (bs-exp x)) (fpone))) )))
258 (bigfloatp res) ))
260 ;;----------------------------------------------------------------------------;;
262 ;; exp(x), real 0 <= x < 1
264 (defun $bs_exp (x) (bs-exp x)) ;; top level test function
266 (defun bs-exp (x) ;; assume 0 <= x < 1
267 (let ((exp1
268 (let* ((fpprec (+ fpprec 12)) ;; needs testing
269 (x (cdr (bigfloatp x)))
270 (mant (car x)) (e (cadr x))
271 (res (fpone))
272 (nr 1) ;; nr of extracted bits
274 (do ((k 0 (1+ k))
275 (pos1 1 (ash pos1 1))
276 (pos2 1) )
277 ((= 0 pos2) (bcons res))
278 (setq pos2 (- fpprec (+ pos1 e)))
279 (when (< pos2 0)
280 (setq mant (ash mant (- pos2))
281 pos2 0 ))
282 (setq u (ldb (byte nr pos2) mant)
283 res (fptimes* res (bs-exp-u/2^2^k u k fpprec))
284 nr pos1) ))))
285 (bigfloatp exp1) ))
287 (defun bs-exp-u/2^2^k (u k prec) ;; compute exp(u/2^2^k) where u < 2^2^(k-1), integer u,k (k >= 0)
288 (if (= 0 u)
289 (fpone)
290 (let ((z (ash 1 k))
291 (kk (if (= k 0) 1/2 (ash 1 (1- k)))) ) ;; param for bs-exp-u/2^z-series-size, see (*)
292 (bs-exp-u/2^z u z kk prec) )))
294 ;; (*) End the Taylor-series when (u/2^2^k)^i/i! < 1/2^fpprec.
296 ;; u might not fit into a float. Use bound u < 2^2^(k-1), i.e. u/2^2^k < 1/2^2^(k-1).
298 ;; So end when i!*(2^2^(k-1))^i > 2^fpprec or
300 ;; log(i!) + i * 2^(k-1)*log(2) = sum(log(n) + 2^(k-1)*log(2), n,1,i) > fpprec * log(2).
302 (defun bs-exp-u/2^z-series-size (kk prec)
303 (setq kk (* kk (log 2)))
304 (do ((n 1 (1+ n))
305 (acc 0)
306 (lim (* prec (log 2))) )
307 ((> acc lim) n)
308 (incf acc (+ (log n) kk)) ))
310 (defun bs-exp-u/2^z (u z kk prec) ;; compute exp(u/2^z), integer u,z
311 (let (nr tt/qq)
312 (do ((i 0))
313 ((logbitp i u)
314 (setq u (ash u (- i))
315 z (- z i) ))
316 (incf i) )
317 (setq nr (bs-exp-u/2^z-series-size kk prec))
318 (multiple-value-bind (tt zz qq) (split-bs-exp-u/2^z 0 (1+ nr) u z)
319 (setq tt/qq (fpquotient (intofp tt) (intofp qq)))
320 (list (car tt/qq) (- (cadr tt/qq) zz)) )))
321 ;;----------------------------------------------------------------------------;;
324 ;;----------------------------------------------------------------------------;;
326 ;; Compute log(1 + eps), real eps > -1
328 (defun $bs_log1p (eps) (bs-log1p eps)) ;; top level test function
330 (defun bs-log1p (eps) ;; eps = x - 1
331 (setq eps (bigfloatp eps))
332 (let ((res
333 (let* ((extra (1+ (- (caddr eps)))) ;; give |e-1| extra bits
334 (fpprec (+ fpprec extra))
335 ;; x = 1 + eps = m*2^e = 2^(e-1) + ...
336 (x (bcons (fpplus (cdr (bigfloatp eps)) (fpone)))) )
337 (bs-log x) )))
338 (bigfloatp res) ))
340 ;;----------------------------------------------------------------------------;;
342 ;; Compute log(x), real x > 0
344 ;; Use log(x) = log(x/2^k) + k*log(2), where x/2^k < 1.
346 (defun $bs_log (x) (bs-log x)) ;; top level test function
348 (defun bs-log (x) ;; assume x > 0
349 (let ((res
350 (let* ((fpprec (+ fpprec 12)) ;; needs more testing
351 (x (cdr (bigfloatp x)))
352 (m (car x)) (k (cadr x)) ;; mantissa, exponent
353 ($float2bf t)
354 lgx )
355 (setq x (list m 0)) ;; now x = 0 or 1/2 <= x < 1
356 ;; choose k so that |x - 1| is as small as possible:
357 (when (fplessp x (floattofp (coerce 2/3 'flonum)))
358 (setq x (list m 1))
359 (decf k) )
360 ;; now |x - 1| <= 1/3 < 1/2
361 (setq lgx (bs-log1 x fpprec))
362 (bcons
363 (if (= k 0)
364 lgx
365 (fpplus lgx (fptimes* (intofp k) (comp-log2))) )))))
366 (bigfloatp res) ))
368 (defun bs-log1 (x prec) ;; assume |x - 1| < 1/2
369 (let ((one (fpone))
370 dx posp u e z )
371 (do ((y (list 0 0)) (k 0) m-len (pos 1))
372 ((= 0 pos) y)
373 (setq dx (fpdifference x one)
374 posp (fpposp dx)
375 dx (fpabs dx) )
376 (unless (fpposp dx) (return y))
377 (setq k (- (cadr dx))) ;; |x - 1| < 2^-k
378 (setq m-len (integer-length (car dx)) ;; m-len isn't always prec
379 pos (- m-len k)
380 u (ldb (byte k pos) (car dx)) ;; first k significant mantissa bits
381 u (if posp u (- u))
382 e (bs-exp-u/2^2k (- u) k prec)
383 x (fptimes* x e)
384 z (intofp u)
385 z (list (car z) (- (cadr z) (* 2 k)))
386 y (fpplus y z) ))))
388 (defun bs-exp-u/2^2k (u k prec) ;; compute exp(u/2^2k) where u < 2^k, integer u,k (k >= 0)
389 (if (= 0 u)
390 (fpone)
391 (bs-exp-u/2^z u (* 2 k) k prec) )) ;; k is param for bs-exp-u/2^z-series-size, see (**)
393 ;; (**) End the Taylor-series when (u/2^(2*k))^i/i! < 1/2^fpprec.
395 ;; u might not fit into a float. Use bound u < 2^k, i.e. u/2^(2*k) < 1/2^k.
397 ;; So end when i!*(2^k)^i > 2^fpprec or
399 ;; log(i!) + i * k*log(2) = sum(log(n) + k*log(2), n,1,i) > fpprec * log(2).
401 ;;----------------------------------------------------------------------------;;