1 ;; Maxima functions for floor, ceiling, and friends
2 ;; Copyright (C) 2004, 2005, 2007 Barton Willis
5 ;; Department of Mathematics
6 ;; University of Nebraska at Kearney
10 ;; This source code is licensed under the terms of the Lisp Lesser
11 ;; GNU Public License (LLGPL). The LLGPL consists of a preamble, published
12 ;; by Franz Inc. (http://opensource.franz.com/preamble.html), and the GNU
13 ;; Library General Public License (LGPL), version 2, or (at your option)
14 ;; any later version. When the preamble conflicts with the LGPL,
15 ;; the preamble takes precedence.
17 ;; This library is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 ;; Library General Public License for details.
22 ;; You should have received a copy of the GNU Library General Public
23 ;; License along with this library; if not, write to the
24 ;; Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
25 ;; Boston, MA 02110-1301, USA.
29 (macsyma-module nummod
)
31 ;; Let's have version numbers 1,2,3,...
33 (eval-when (:load-toplevel
:execute
)
34 ($put
'$nummod
3 '$version
)
35 ;; Let's remove built-in symbols from list for user-defined properties.
36 (setq $props
(remove '$nummod $props
)))
38 (defmacro opcons
(op &rest args
)
39 `(simplify (list (list ,op
) ,@args
)))
41 ;; charfun(pred) evaluates to 1 when the predicate 'pred' evaluates
42 ;; to true; it evaluates to 0 when 'pred' evaluates to false; otherwise,
43 ;; it evaluates to a noun form.
45 (defprop $charfun simp-charfun operators
)
46 (defprop %charfun simp-charfun operators
)
48 (defun simp-charfun (e yy z
)
51 (setq e
(take '($is
) (simplifya (specrepcheck (second e
)) z
)))
52 (let* (($prederror nil
)
56 ((op-equalp e
'$is
) `(($charfun simp
) ,(second e
)))
57 (t `(($charfun simp
) ,e
)))))
59 (defun integer-part-of-sum (e)
60 (let ((i-sum 0) (n-sum 0) (o-sum 0) (n))
63 (cond ((maxima-integerp ei
)
64 (setq i-sum
(add ei i-sum
)))
65 ((or (ratnump ei
) (floatp ei
) ($bfloatp ei
))
66 (setq n-sum
(add ei n-sum
)))
68 (setq o-sum
(add ei o-sum
)))))
69 (setq n
(opcons '$floor n-sum
))
70 (setq i-sum
(add i-sum n
))
71 (setq o-sum
(add o-sum
(sub n-sum n
)))
72 (values i-sum o-sum
)))
74 (defprop $floor simp-floor operators
)
76 (defprop $floor tex-matchfix tex
)
77 (defprop $floor
(("\\left \\lfloor " ) " \\right \\rfloor") texsym
)
79 ;; $floor distributes over lists, matrices, and equations
80 (setf (get '$floor
'distribute_over
) '(mlist $matrix mequal
))
82 ; These defprops for $entier are copied from orthopoly/orthopoly-init.lisp.
84 (defprop $entier tex-matchfix tex
)
85 (defprop $entier
(("\\lfloor ") " \\rfloor") texsym
)
87 ;; $entier and $fix distributes over lists, matrices, and equations
88 (setf (get '$entier
'distribute_over
) '(mlist $matrix mequal
))
89 (setf (get '$fix
'distribute_over
) '(mlist $matrix mequal
))
91 ;; For an example, see pretty-good-floor-or-ceiling. Code courtesy of Stavros Macrakis.
93 (defmacro bind-fpprec
(val &body exprs
)
94 `(let (fpprec bigfloatzero bigfloatone bfhalf bfmhalf
)
95 (let (($fpprec
(fpprec1 nil
,val
)))
98 ;; Return true if the expression can be formed using rational numbers, logs, mplus, mexpt, or mtimes.
100 (defun use-radcan-p (e)
101 (or ($ratnump e
) (and (op-equalp e
'%log
'mexpt
'mplus
'mtimes
) (every 'use-radcan-p
(cdr e
)))))
103 ;; When constantp(x) is true, we use bfloat evaluation to try to determine
104 ;; the ceiling or floor. If numerical evaluation of e is ill-conditioned, this function
105 ;; can misbehave. I'm somewhat uncomfortable with this, but it is no worse
106 ;; than some other stuff. One safeguard -- the evaluation is done with three
107 ;; values for fpprec. When the floating point evaluations are
108 ;; inconsistent, bailout and return nil. I hope that this function is
109 ;; much more likely to return nil than it is to return a bogus value.
111 (defun pretty-good-floor-or-ceiling (x fn
&optional digits
)
112 (let (($float2bf t
) ($algebraic t
) (f1) (f2) (f3) (eps) (lb) (ub) (n))
114 (setq digits
(if (and (integerp digits
) (> 0 digits
)) digits
25))
117 ;; To handle ceiling(%i^%i), we need to apply rectform. If bfloat
118 ;; is improved, it might be possible to remove this call to rectform.
120 (setq x
($rectform x
))
121 (if (not ($freeof
'$%i
'$minf
'$inf
'$und
'$infinity x
)) (throw 'done nil
))
123 ;; When x doesn't evaluate to a bigfloat, bailout and return nil.
124 ;; This happens when, for example, x = asin(2). For now, bfloatp
125 ;; evaluates to nil for a complex big float. If this ever changes,
126 ;; this code might need to be repaired.
129 (setq f1
($bfloat x
))
130 (if (not ($bfloatp f1
)) (throw 'done nil
)))
133 (setq f2
(bind-fpprec digits
($bfloat x
)))
134 (if (not ($bfloatp f2
)) (throw 'done nil
))
138 (setq f3
($bfloat x
))
139 (if (not ($bfloatp f3
)) (throw 'done nil
))
141 ;; Let's say that the true value of x is in the interval
142 ;; [f3 - |f3| * eps, f3 + |f3| * eps], where eps = 10^(20 - digits).
143 ;; Define n to be the number of integers in this interval; we have
145 (setq eps
(power ($bfloat
10) (- 20 digits
)))
146 (setq lb
(sub f3
(mult (take '(mabs) f3
) eps
)))
147 (setq ub
(add f3
(mult (take '(mabs) f3
) eps
)))
149 ;; If n > 1, we're going to give up. This is true iff ceiling(ub) -
150 ;; ceiling(lb) > 1. However, we don't want to blindly try to calculate
151 ;; that: if ub and lb are enormous, we probably won't have enough
152 ;; precision in the bigfloats to calculate either ceiling. Start by
153 ;; taking the difference: if it's at least 2, then we know that n >= 2.
154 (when (fpgreaterp (cdr (sub ub lb
)) (cdr ($bfloat
2)))
157 (setq n
(sub (take '($ceiling
) ub
)
158 (take '($ceiling
) lb
))))
160 (setq f1
(take (list fn
) f1
))
161 (setq f2
(take (list fn
) f2
))
162 (setq f3
(take (list fn
) f3
))
164 ;; Provided f1 = f2 = f3 and n = 0, return f1.
165 ;; If n = 1 and (use-radcan-p e) and ($radcan e) is a $ratnump, return
166 ;; floor / ceiling of radcan(x).
168 (cond ((and (= f1 f2 f3
) (= n
0)) f1
)
169 ((and (= f1 f2 f3
) (= n
1) (use-radcan-p x
))
171 (if ($ratnump x
) (take (list fn
) x
) nil
))
175 ;; (a) The function fpentier rounds a bigfloat towards zero--we need to
178 ;; (b) Since limit(f(x))=(m)inf implies limit(floor(f(x)))=(m)inf,
179 ;; floor(inf/minf) = inf/minf. Since minf<limit(f(x))<inf implies
180 ;; minf<limit(floor(f(x)))<inf, floor(ind)=ind
182 ;; I think floor(complex number) should be undefined too. Some folks
183 ;; might like floor(a + %i b) --> floor(a) + %i floor(b). But
184 ;; this would violate the integer-valuedness of floor.
185 ;; So floor(infinity) remains unsimplified
187 (defun simp-floor (e e1 z
)
189 (setq e
(simplifya (specrepcheck (nth 1 e
)) z
))
191 (cond ((numberp e
) (floor e
))
193 ((ratnump e
) (floor (cadr e
) (caddr e
)))
196 (setq e1
(fpentier e
))
197 (if (and (minusp (cadr e
)) (not (zerop1 (sub e1 e
))))
201 ((maxima-integerp e
) e
)
203 (($orderlessp e
(neg e
))
204 (sub 0 (opcons '$ceiling
(neg e
))))
206 ((and (setq e1
(mget e
'$numer
)) (floor e1
)))
208 ((or (member e infinities
) (eq e
'$und
) (eq e
'$ind
)) '$und
)
209 ;;((member e '($inf $minf $ind $und)) e) ; Proposed code
210 ;; leave floor(infinity) as is
211 ((or (eq e
'$zerob
)) -
1)
212 ((or (eq e
'$zeroa
)) 0)
214 ((and ($constantp e
) (pretty-good-floor-or-ceiling e
'$floor
)))
217 (let ((i-sum) (o-sum))
218 (multiple-value-setq (i-sum o-sum
) (integer-part-of-sum e
))
219 (setq o-sum
(if (equal i-sum
0) `(($floor simp
) ,o-sum
)
220 (opcons '$floor o-sum
)))
223 ;; handle 0 <= e < 1 implies floor(e) = 0 and
224 ;; -1 <= e < 0 implies floor(e) = -1.
226 ((and (member ($compare
0 e
) '("<" "<=") :test
#'equal
)
227 (equal ($compare e
1) "<"))
229 ((and (member ($compare -
1 e
) '("<" "<=") :test
#'equal
)
230 (equal ($compare e
0) "<"))
232 (t `(($floor simp
) ,e
))))
234 (defun floor-integral (x)
235 (let ((f (take '($floor
) x
)))
236 (mul f
(div 1 2) (add (mul 2 x
) -
1 (neg f
)))))
238 (defun ceiling-integral (x)
239 (let ((f (take '($ceiling
) x
)))
240 (mul f
(div 1 2) (add 1 (mul 2 x
) (neg f
)))))
242 (putprop '$floor
`((x) ,#'floor-integral
) 'integral
)
243 (putprop '$ceiling
`((x) ,#'ceiling-integral
) 'integral
)
245 (defprop $ceiling simp-ceiling operators
)
247 (defprop $floor simplim%floor simplim%function
)
249 (defun simplim%floor
(expr var val
)
250 (let* ((arg (cadr expr
))
251 (b (behavior arg var val
))
252 (arglimab (limit arg var val
'think
)) ; with $zeroa $zerob
253 (arglim (ridofab arglimab
)))
255 (maxima-integerp arglim
))
258 (maxima-integerp arglim
))
260 ((and ($constantp arglim
)
261 (not (maxima-integerp arglim
)))
262 (simplify (list '($floor
) arglim
)))
264 (throw 'limit nil
)))))
266 (defprop $ceiling tex-matchfix tex
)
267 (defprop $ceiling
(("\\left \\lceil " ) " \\right \\rceil") texsym
)
269 ;; $ceiling distributes over lists, matrices, and equations.
270 (setf (get '$ceiling
'distribute_over
) '(mlist $matrix mequal
))
272 (defun simp-ceiling (e e1 z
)
274 (setq e
(simplifya (specrepcheck (nth 1 e
)) z
))
275 (cond ((numberp e
) (ceiling e
))
277 ((ratnump e
) (ceiling (cadr e
) (caddr e
)))
280 (sub 0 (opcons '$floor
(sub 0 e
))))
282 ((maxima-integerp e
) e
)
284 (($orderlessp e
(neg e
))
285 (sub* 0 (opcons '$floor
(neg e
))))
287 ((and (setq e1
(mget e
'$numer
)) (ceiling e1
)))
289 ((or (member e infinities
) (eq e
'$und
) (eq e
'$ind
)) '$und
)
290 ((or (eq e
'$zerob
)) 0)
291 ((or (eq e
'$zeroa
)) 1)
293 ((and ($constantp e
) (pretty-good-floor-or-ceiling e
'$ceiling
)))
296 (let ((i-sum) (o-sum))
297 (multiple-value-setq (i-sum o-sum
) (integer-part-of-sum e
))
298 (setq o-sum
(if (equal i-sum
0) `(($ceiling simp
) ,o-sum
)
299 (opcons '$ceiling o-sum
)))
302 ;; handle 0 < e <= 1 implies ceiling(e) = 1 and
303 ;; -1 < e <= 0 implies ceiling(e) = 0.
305 ((and (equal ($compare
0 e
) "<")
306 (member ($compare e
1) '("<" "<=") :test
#'equal
))
308 ((and (equal ($compare -
1 e
) "<")
309 (member ($compare e
0) '("<" "<=") :test
#'equal
))
311 (t `(($ceiling simp
) ,e
))))
313 (defprop $ceiling simplim%ceiling simplim%function
)
315 (defun simplim%ceiling
(expr var val
)
316 (let* ((arg (cadr expr
))
317 (b (behavior arg var val
))
318 (arglimab (limit arg var val
'think
)) ; with $zeroa $zerob
319 (arglim (ridofab arglimab
)))
321 (maxima-integerp arglim
))
324 (maxima-integerp arglim
))
326 ((and ($constantp arglim
)
327 (not (maxima-integerp arglim
)))
328 (simplify (list '($ceiling
) arglim
)))
330 (throw 'limit nil
)))))
333 (defprop $mod simp-nummod operators
)
334 (defprop $mod tex-infix tex
)
335 (defprop $mod
(" \\rm{mod} ") texsym
)
336 (defprop $mod
180. tex-lbp
)
337 (defprop $mod
180. tex-rbp
)
339 ;; $mod distributes over lists, matrices, and equations
340 (setf (get '$mod
'distribute_over
) '(mlist $matrix mequal
))
342 ;; See "Concrete Mathematics," Section 3.21.
344 (defun simp-nummod (e e1 z
)
346 (let ((x (simplifya (specrepcheck (cadr e
)) z
))
347 (y (simplifya (specrepcheck (caddr e
)) z
)))
348 (cond ((or (equal 0 y
) (equal 0 x
)) x
)
349 ((equal 1 y
) (sub x
(opcons '$floor x
)))
350 ((and ($constantp x
) ($constantp y
))
351 (sub x
(mul y
(opcons '$floor
(div x y
)))))
352 ((not (equal 1 (setq e1
($gcd x y
))))
353 (mul e1
(opcons '$mod
(div x e1
) (div y e1
))))
354 (t `(($mod simp
) ,x
,y
)))))
356 ;; The function 'round' rounds a number to the nearest integer. For a tie, round to the
357 ;; nearest even integer.
359 (defprop %round simp-round operators
)
360 (setf (get '%round
'integer-valued
) t
)
361 (setf (get '%round
'reflection-rule
) #'odd-function-reflect
)
362 (setf (get '$round
'alias
) '%round
)
363 (setf (get '$round
'verb
) '%round
)
364 (setf (get '%round
'noun
) '$round
)
366 ;; round distributes over lists, matrices, and equations.
367 (setf (get '%round
'distribute_over
) '(mlist $matrix mequal
))
369 (defun simp-round (e yy z
)
371 (setq yy
(caar e
)) ;; find a use for the otherwise unused YY.
372 (setq e
(simplifya (specrepcheck (second e
)) z
))
373 (cond (($featurep e
'$integer
) e
) ;; takes care of round(round(x)) --> round(x).
374 ((member e
'($inf $minf $und $ind
) :test
#'eq
) e
)
378 (let* ((lb (take '($floor
) e
))
379 (ub (take '($ceiling
) e
))
380 (sgn (csign (sub (sub ub e
) (sub e lb
)))))
381 (cond ((eq sgn t
) `((,yy simp
) ,e
))
384 ((alike lb ub
) lb
) ;; For floats that are integers, this can happen. Maybe featurep should catch this.
385 ((and (eq sgn
'$zero
) ($featurep lb
'$even
)) lb
)
386 ((and (eq sgn
'$zero
) ($featurep ub
'$even
)) ub
)
387 ((apply-reflection-simp yy e t
))
388 (t `((,yy simp
) ,e
)))))))
390 (defprop %round simplim%round simplim%function
)
392 (defun simplim%round
(expr var val
)
393 (let* ((arg (cadr expr
))
394 (b (behavior arg var val
))
395 (arglimab (limit arg var val
'think
)) ; with $zeroa $zerob
396 (arglim (ridofab arglimab
)))
398 (maxima-integerp (m+ 1//2 arglim
)))
401 (maxima-integerp (m+ 1//2 arglim
)))
404 (not (maxima-integerp (m+ 1//2 arglim
))))
405 (simplify (list '(%round
) arglim
)))
407 (throw 'limit nil
)))))
409 ;; Round a number towards zero.
411 (defprop %truncate simp-truncate operators
)
412 (setf (get '%truncate
'integer-valued
) t
)
413 (setf (get '%truncate
'reflection-rule
) #'odd-function-reflect
)
414 (setf (get '$truncate
'alias
) '%truncate
)
415 (setf (get '$truncate
'verb
) '%truncate
)
416 (setf (get '%truncate
'noun
) '$truncate
)
418 ;; truncate distributes over lists, matrices, and equations.
419 (setf (get '%truncate
'distribute_over
) '(mlist $matrix mequal
))
421 (defun simp-truncate (e yy z
)
423 (setq yy
(caar e
)) ;; find a use for the otherwise unused YY.
424 (setq e
(simplifya (specrepcheck (second e
)) z
))
425 (cond (($featurep e
'$integer
) e
) ;; takes care of truncate(truncate(x)) --> truncate(x).
426 ((member e
'($inf $minf $und $ind
) :test
#'eq
) e
)
430 (let ((sgn (csign e
)))
431 (cond ((member sgn
'($neg $nz
) :test
#'eq
) (take '($ceiling
) e
))
432 ((member sgn
'($zero $pz $pos
) :test
#'eq
) (take '($floor
) e
))
433 ((apply-reflection-simp yy e t
))
434 (t `((,yy simp
) ,e
)))))))
436 ;;; integration for signum, unit_step, and mod.
438 ;; integrate(signum(x),x) = |x|
439 (defun signum-integral (x)
442 ;; integrate(unit_step(x),x) = (x + |x|)/2
443 (defun unit-step-integral (x)
444 (div (add x
(take '(mabs) x
)) 2))
446 ;; We have mod(x,a) = x-a*floor(x/a). This gives
447 ;; integrate(mod(x,a),x) = (a^2 floor(x/a)^2 + (a^2 - 2 a x) floor(x/a) + x^2)/2
448 ;; In terms of mod(x,a), an antiderivative is
449 ;; integrate(mod(x,a),x) = (mod(x,a)^2-a*mod(x,a)+a*x)/2
450 ;; Before this function is called, Maxima checks if a explicitly depends on x. So
451 ;; this function doesn't need to do that check.
452 (defun mod-integral (x a
)
453 (let ((q (take '($mod
) x a
)))
454 (div (add (mul q q
) (mul -
1 a q
) (mul a x
)) 2)))
456 (putprop '%signum
(list (list 'x
) #'signum-integral
) 'integral
)
457 (putprop '$unit_step
(list (list 'x
) #'unit-step-integral
) 'integral
)
459 ;; integrate(mod(x,a),a) doesn't have representation in terms of functions
460 ;; known to Maxima, I think. (Barton Willis, 2020).
461 (putprop '$mod
(list (list 'x
'y
) #'mod-integral nil
) 'integral
)