Subject: Re: bug#71890: 31.0.50; Calc can't integrate 0

From: Stefan Kangas <[email-masked]>
Date: Mon, 3 Mar 2025 17:41:26 -0800

Michael Heerdegen <[email-masked]> writes:

> Hello,
>
> Any Calc experts around?  emacs -Q:

I don't think we do, sadly.  Mattias has looked at it a little bit at
times in the past, maybe he could take a look?

>   M-x calc RET
>   0 RET
>   a i x RET
>
> is a way to integrate 0 using Calc (with integration variable x in this
> case).  Calc fails to solve it:
>
> The result Calc gives you is "integ(0, x)" - Calc leaves the integral
> unresolved, which is Calc's way to say "I can't solve this".  This
> is definitely not a feature or a way to represent an integration
> constant - (info "(calc) Integration"):
>
> | Also, any indefinite integral should be considered to have an
> | arbitrary constant of integration added to it, although Calc does not
> | write an explicit constant of integration in its result.
>
> So what is going on?  Other functions, including constants like 1 or a
> symbol work as expected.
>
> As far as I understand, Calc is internally perfectly able to solve this
> integral but at one place in the code the correct result is rejected
> because this case is so special that the authors forgot to make the code
> expect a result of such a simple form.  This small change helps:
>
> From a4351149314af2dab8f03715a9effeb2ee26490c Mon Sep 17 00:00:00 2001
> From: Michael Heerdegen <[email-masked]>
> Date: Fri, 23 Jun 2023 04:44:34 +0200
> Subject: [PATCH] WIP: Try to fix integration of 0
>
> ---
>  lisp/calc/calcalg2.el | 13 +++++++------
>  1 file changed, 7 insertions(+), 6 deletions(-)
>
> diff --git a/lisp/calc/calcalg2.el b/lisp/calc/calcalg2.el
> index 5e9ae8a3e5a..fafbd8418ef 100644
> --- a/lisp/calc/calcalg2.el
> +++ b/lisp/calc/calcalg2.el
> @@ -774,12 +774,13 @@ math-integral
>      (setq val math-cur-record)
>      (while (vectorp (nth 1 val))
>        (setq val (aref (nth 1 val) 0)))
> -    (setq val (if (memq (nth 1 val) '(parts parts2))
> -		  (progn
> -		    (setcar (cdr val) 'parts2)
> -		    (list 'var 'PARTS val))
> -		(and (consp (nth 1 val))
> -		     (nth 1 val))))
> +    (let ((val-1 (nth 1 val)))
> +      (setq val (if (memq val-1 '(parts parts2))
> +		    (progn
> +		      (setcar (cdr val) 'parts2)
> +		      (list 'var 'PARTS val))
> +		  (and (or (consp val-1) (equal val '(0 0)))
> +		       val-1))))
>      (math-tracing-integral "Integral of "
>  			   (math-format-value expr 1000)
>  			   "  is  "
> --
> 2.39.2
>
>
> The problem is: I found this fix by trial and error, someone who knows
> the code should verify that the fix is correct.  For example, I don't
> know whether 0 is always represented as list (0 0) at this point (given
> how many different settings there are...) and whether it is the correct
> code place to handle this case.

We have minimal or no unit tests in this area.

Maybe this would be a good opportunity to add more?