Nov 042026
 

I just cannot stop, can I. Before embarking on further improvements of my CAS and in particular, its integration capabilities, I figured I should add 3D plots. And I did. (With Claude as my assistant, of course.)

It may not be evident from this screen shot, but the 3d plot is interactive: it can be rotated using the mouse.

As to how capable my CAS really is, I think the following lovely example tells a story:

This matrix was not picked randomly: the sequence of determinants is called the Smarandache cyclic determinant sequence and it has some intriguing properties.

 Posted by at 1:05 am
Nov 012026
 

I keep playing with my brand new creation, this Web-based computer algebra tool. One day, I’ll likely add some 3D plotting but for now, I am being fascinated by some density plots.

Anyhow, since we’re at it, I also included some nontrivial integrals and a second order differential equation, for fun. Yes, it can do that all.

 Posted by at 11:50 pm
Oct 272026
 

A few days ago, I exchanged some e-mails with a fellow volunteer Maxima developer. That exchange reminded me of a pet project of mine, something I’ve been contemplating for quite some time: a computer algebra system, one significantly less ambitious than a frontier-class system like Mathematica, Maple or, I dare include it in that list, Maxima, but one that runs in the browser, with no server-side code and nothing to install on the user’s computer either.

This time around, I decided to test my idea with Claude Opus 5.5, their latest and greatest model. I outlined my ambition in a couple of paragraphs. The result was less than overwhelming: After some 20 minutes, Claude ran out of “thinking” tokens, and returned nothing other than 128,000 tokens wasted.

Never mind, I figured, let’s try it again, with a slightly refined prompt to ensure that Claude doesn’t embark on a project bigger than itself. And that, well, worked. Claude presented a plan and then, following my instructions and my replies to its questions, began to proceed with the implementation one step at a time.

In this past, this is usually where things got derailed. Sometimes, the model got too ambitious and ran into token limits. Sometimes, the model’s code was incoherent, self-contradictory. Sometimes, the initial code was working, more-or-less, but additions did not.

But not this time. Claude kept churning out sensible code and, most notably, starting at the very first stage, included a testsuite. So even though we had no usable system yet, the testsuite was an assurance that the code we’re building is doing what it was supposed to do.

Notice that I am not using Claude as an “agent”. I did not give it carte blanche in a folder of its own, to build code, download libraries, mess around. No. Claude was talking to me. It was then I who decided if the code Claude presented was good enough or not, it was I who incorporated it into the code base. Claude, accordingly, provided helpful, detailed explanations at each stage. The important point is that the role I retained was that of a senior programmer who may delegate subtasks to junior programmers, but retains overall control of the project at a technical level.

And Claude delivered beyond expectations. Soon, I found myself hooked. One module after another, which I added, tested, tried. A rather nice-looking application was beginning to take shape. And there was both breadth and depth: the application was already acquiring features more sophisticated than I ever dared to plan for, while at the same time Claude never forgot to deal with the mundane, such as appropriate visual representation via MathJax or a growing collection of sections in interactive help.

About halfway through the project we got to basic calculus. I was beginning to wonder: When will Claude falter? When will useful code give way to AI hallucinations, code referencing non-existent language features or capabilities? But none of that happened. Claude robustly, methodically implemented basic calculus, one subroutine at a time.

Again, I stress, Claude had no free reign. We were proceeding step by step, I took the code Claude wrote, integrated it into the application, scrutinized it when I had to, looked for testsuite failures, even corrected the trivial ones.

Once we got past linear algebra and multivariate equations, I decided to get really greedy. I asked Claude, can we do ODEs and PDEs? Because even a half-decent differential equation solver makes a world of a difference when it comes to the usability of a CAS. Claude accordingly revised our plan, and then did the grunt work.

Claude remained enthusiastic throughout, but I began to notice that its responses felt… a little less coherent. Nothing big, no real mistakes, just subtle things like harder-to-understand explanations or instructions on where to place a code patch. No wonder: we were by now around 700,000 input tokens per conversational turn, rapidly approaching Claude’s 1 million token limit. (I also noticed the increasing frequency at which I received notifications from Anthropic about additional credit card charges. But, I digress.)

Claude was still eager to proceed with more ambitious additions but I asked it for something else: something genuinely useful in a CAS, a broader class of special functions like hypergeometric functions or Lambert’s W function. Claude delivered.

So roughly 36 hours after I sent my first prompt to Claude, I was in possession of a full-featured, rich, eminently capable browser-based computer algebra application. One that can also be installed on an Android device as an offline computer algebra tool.

This has been an amazing experience. Was it “vibe coding”? I don’t think so. I was not just playing the dumb user, asking the agentic AI to deliver a solution. I was a senior partner with an incredibly capable, knowledgeable junior assistant, but I retained intellectual ownership of the result, indeed I can confidently edit, and fix/expand the code myself when I have to, since I understand what it does.

End result: 26,412 code lines in 163 files, at the cost of 31,006,699 input tokens and 3,048,131 output tokens, consumed over the course of less than two days. And WebCAS — the name picked by Claude — is already my favorite computer algebra application, one that will likely receive regular updates frmo me in the future.

 Posted by at 10:38 pm
Jul 292025
 

I just completed the process to release Maxima 5.48.

The new version introduces several noteworthy enhancements for symbolic computation, with improvements in performance, functionality, and user experience.

Highlights:

– Unicode-enabled output (when supported by the Lisp compiler)
– Numerous performance improvements across core routines
– New package for symbolic radical denesting
– New package for inferring closed-form expressions from sequences
– New package for simplification of gamma functions
– Resolution of more than 150 tickets, spanning both long-standing and recent bugs

Developed in Common Lisp, Maxima remains a reliable and customizable tool for research, education, science, and engineering.

To install, explore, or contribute: https://maxima.sourceforge.io

 Posted by at 2:56 am
May 312023
 

I just finished uploading the latest release, 5.47.0, of our beautiful Maxima project.

It was a harder battle than I anticipated, lots of little build issues I had to fix before it was ready to go.

Maxima remains one of the three major computer algebra systems. Perhaps a bit (but only a bit!) less elegant-looking than Mathematica, and perhaps a bit (but only a bit!) less capable on some friends (exotic integrals, differential equations) than Maple, yet significantly more capable on other fronts (especially I believe abstract index tensor algebra and calculus), it also has a unique property: it’s free and open source.

It is also one of the oldest pieces of major software that remains in continuous use. Its roots go back to the 1960s. I occasionally edit 50-year old code in its LISP code base.

And it works. I use it every day. It is “finger memory”, my “go to” calculator, and of course there’s that tensor algebra bit.

Maxima also has a beautiful graphical interface, which I admit I don’t use much though. You might say that I am “old school” given my preference for the text UI, but that’s really not it: the main reason is that once you know what you’re doing, the text UI is simply more efficient.

I hope folks will welcome this latest release.

 Posted by at 8:54 pm
Apr 032023
 

We all know that GPT is very competent, very knowledgeable. So let me go ahead and ask a technical question. It’s not trivial but not terribly complicated either; any student studying general relativity should be able to show the correct derivation. Here is my question:

Derive the geodesic equations for \(d\tau^2=Bdt^2-Adr^2-r^2d\theta^2-r^2\sin^2\theta d\phi^2\), where \(A\) and \(B\) are functions of \(r\) only.

Let’s see how GPT3.5 responds:

The geodesic equations for the given metric are given by:

$$\frac{d^2x^\alpha}{d\tau^2}+\Gamma^\alpha_{\mu\nu}\frac{dx^\mu}{d\tau}\frac{dx^\nu}{d\tau}=0$$

where \(\tau\) is the proper time, \(x^\alpha\) are the coordinates \((t,r,\theta,\phi)\), and \(\Gamma^\alpha_{\mu\nu}\) are the Christoffel symbols of the second kind, given by:

$$\Gamma^\alpha_{\mu\nu}=\frac{1}{2}g^{\alpha\beta}(\partial_\mu g_{\beta\nu}+\partial_\nu g_{\beta\mu}-\partial_\beta g_{\mu\nu})$$

where \(g_{\alpha\beta}\) is the metric tensor.

For the given metric, the non-zero Christoffel symbols are:

\begin{align*}
\Gamma^t_{rr}&=\frac{1}{2}B’\\
\Gamma^t_{tt}&=\frac{1}{2}B\frac{A’}{B}\\
\Gamma^r_{rt}&=\frac{1}{2}\frac{A’}{B}\\
\Gamma^r_{rr}&=-\frac{1}{2}\frac{A’}{B}\\
\Gamma^\theta_{r\theta}&=\frac{1}{r}\\
\Gamma^\theta_{\theta\phi}&=-\sin\theta\cos\theta\\
\Gamma^\phi_{r\phi}&=\frac{1}{r}\\
\Gamma^\phi_{\theta\phi}&=\cot\theta
\end{align*}

where a prime denotes differentiation with respect to \(r\).

Using these, we can write down the geodesic equations explicitly:

\begin{align*}
\frac{d^2t}{d\tau^2}+\frac{B’}{2B}\left(\frac{dt}{d\tau}\right)^2+\frac{A’}{2B}\left(\frac{dr}{d\tau}\right)\left(\frac{dt}{d\tau}\right)&=0\\
\frac{d^2r}{d\tau^2}-\frac{A’}{2B}\left(\frac{dt}{d\tau}\right)^2+\frac{A’}{2B}\left(\frac{dr}{d\tau}\right)^2+r\left(\left(\frac{d\theta}{d\tau}\right)^2+\sin^2\theta\left(\frac{d\phi}{d\tau}\right)^2\right)&=0\\
\frac{d^2\theta}{d\tau^2}+2\frac{dr}{d\tau}\frac{d\theta}{d\tau}-\sin\theta\cos\theta\left(\frac{d\phi}{d\tau}\right)^2&=0\\
\frac{d^2\phi}{d\tau^2}+2\frac{dr}{d\tau}\frac{d\phi}{d\tau}+2\cot\theta\frac{d\theta}{d\tau}\frac{d\phi}{d\tau}&=0
\end{align*}

Hmmm… sounds eminently knowledgeable, does it not?

Well… maybe not. How about we ask an actual computer algebra system:

(%i1) load(ctensor)$
(%i2) derivabbrev:true$
(%i3) ct_coords:[t,r,u,v]$
(%i4) depends([A,B],[r])$
(%i5) lg:matrix([B,0,0,0],[0,-A,0,0],[0,0,-r^2,0],[0,0,0,-r^2*sin(u)^2])$
(%i6) cmetric(false)$
(%i7) christof(mcs)$
                                            B
                                             r
(%t7)                          mcs        = ---
                                  1, 1, 2   2 A

                                            B
                                             r
(%t8)                          mcs        = ---
                                  1, 2, 1   2 B

                                            A
                                             r
(%t9)                          mcs        = ---
                                  2, 2, 2   2 A

                                             1
(%t10)                          mcs        = -
                                   2, 3, 3   r

                                             1
(%t11)                          mcs        = -
                                   2, 4, 4   r

                                              r
(%t12)                         mcs        = - -
                                  3, 3, 2     A

                                           cos(u)
(%t13)                        mcs        = ------
                                 3, 4, 4   sin(u)

                                               2
                                          r sin (u)
(%t14)                     mcs        = - ---------
                              4, 4, 2         A

(%t15)                   mcs        = - cos(u) sin(u)
                            4, 4, 3

(%i16) geod:[0,0,0,0]$
(%i17) cgeodesic(true)$
                                   B t    + B  r  t
                                      s s    r  s  s
(%t17)                     geod  = -----------------
                               1           B

                        2        2           2          2                     2
                 2 r sin (u) (v )  + 2 r (u )  - B  (t )  - 2 A r    - A  (r )
                               s           s      r   s          s s    r   s
(%t18) geod  = - --------------------------------------------------------------
           2                                  2 A

                                            2
                        r cos(u) sin(u) (v )  - r u    - 2 r  u
                                          s        s s      s  s
(%t19)        geod  = - ----------------------------------------
                  3                        r

                   r sin(u) v    + 2 r cos(u) u  v  + 2 r  sin(u) v
                             s s               s  s      s         s
(%t20)     geod  = -------------------------------------------------
               4                       r sin(u)

Looks different, doesn’t it. And no, I don’t mean LaTeX vs. the fixed pitch character representations of equations in a text terminal. Rather, the content.

The thing is, what GPT produces looks plausible. It has the right idea. The equations seem to make sense. Unless you know what to expect, you are likely to accept the result as correct, since it appears correct. But GPT sucks at math. It gets easily confused. It is a text model that is optimized to write equations that look right… but only has a superficial understanding of what it produces. Kind of like a student who is trying hard to remember, produces something that resembles the right thing, but without a perfect memory (and keep in mind, trained neural nets are not like any other software we are used to using, as they have no perfect memory!) and without in-depth understanding, fails.

I am sure over time this will improve. GPT-4 is already better at it than 3.5 (which was used to produce this outcome). And future versions may likely interface with computer algebra subsystems (among other things) to augment the neural net with specific capabilities. But for now, perhaps I can be forgiven for asking GPT’s cousin, DALL-E, to draw me a cat, exasperated by the bad math GPT produces:

 Posted by at 2:06 pm
Apr 202022
 

Came across a question tonight: How do you construct the matrix

$$\begin{pmatrix}1&2&…&n\\n&1&…&n-1\\…\\2&3&…&1\end{pmatrix}?$$

Here’s a bit of Maxima code to make it happen:

(%i1) M(n):=apply(matrix,makelist(makelist(mod(x-k+n,n)+1,x,0,n-1),k,0,n-1))$
(%i2) M(5);
                               [ 1  2  3  4  5 ]
                               [               ]
                               [ 5  1  2  3  4 ]
                               [               ]
(%o2)                          [ 4  5  1  2  3 ]
                               [               ]
                               [ 3  4  5  1  2 ]
                               [               ]
                               [ 2  3  4  5  1 ]

I also ended up wondering about the determinants of these matrices:

(%i3) makelist(determinant(M(i)),i,1,10);
(%o3) [1, - 3, 18, - 160, 1875, - 27216, 470596, - 9437184, 215233605, - 5500000000]

I became curious if this sequence of numbers was known, and indeed that is the case. It is sequence number A052182 in the Encyclopedia of Integer Sequences: “Determinant of n X n matrix whose rows are cyclic permutations of 1..n.” D’oh.

As it turns out, this sequence also has another name: it’s the Smarandache cyclic determinant sequence. In closed form, it is given by

$${\rm SCDNS}(n)=(-1)^{n+1}\frac{n+1}{2}n^{n-1}.$$

(%i4) SCDNS(n):=(-1)^(n+1)*(n+1)/2*n^(n-1);
                                      n + 1
                                 (- 1)      (n + 1)   n - 1
(%o4)               SCDNS(n) := (------------------) n
                                         2
(%i5) makelist(determinant(SCDNS(i)),i,1,10);
(%o5) [1, - 3, 18, - 160, 1875, - 27216, 470596, - 9437184, 215233605, - 5500000000]

Surprisingly, apart from the alternating sign it shares the first several values with another sequence, A212599. But then they deviate.

Don’t let anyone tell you that math is not fun.

 Posted by at 1:36 am
Apr 132022
 

Acting as “release manager” for Maxima, the open-source computer algebra system, I am happy to announce that just minutes ago, I released version 5.46.

I am an avid Maxima user myself; I’ve used Maxima’s tensor algebra packages, in particular, extensively in the context of general relativity and modified gravity. I believe Maxima’s tensor algebra capabilities remain top notch, perhaps even unsurpassed. (What other CAS can derive Einstein’s field equations from the Einstein-Hilbert Lagrangian?)

The Maxima system has more than half a century of history: its roots go back to the 1960s, when I was still in kindergarten. I have been contributing to the project for nearly 20 years myself.

Anyhow, Maxima 5.46, here we go! I hope I made no blunders while preparing this release, but if I did, I’m sure I’ll hear about it shortly.

 Posted by at 2:26 am
Jul 142021
 

The other day, someone asked a question: Can the itensor package in Maxima calculate the Laplace-Beltrami operator applied to a scalar field in the presence of torsion?

Well, it can. But I was very happy to get this question because it allowed me to uncover some long-standing, subtle bugs in the package that prevented some essential simplifications and in some cases, even produced nonsensical results.

With these fixes, Maxima now produces a beautiful result, as evidenced by this nice newly created demo, which I am about to add to the package:

(%i1) if get('itensor,'version) = false then load(itensor)
(%i2) "First, we set up the basic properties of the system"
(%i3) imetric(g)
(%i4) "Demo is faster in 3D but works for other values of dim, too" 
(%i5) dim:3
(%i6) "We declare symmetries of the metric and other symbols"
(%i7) decsym(g,2,0,[sym(all)],[])
(%i8) decsym(g,0,2,[],[sym(all)])
(%i9) components(g([a],[b]),kdelta([a],[b]))
(%i10) decsym(levi_civita,0,dim,[],[anti(all)])
(%i11) decsym(itr,2,1,[anti(all)],[])
(%i12) "It is useful to set icounter to avoid indexing conflicts"
(%i13) icounter:100
(%i14) "We choose the appropriate convention for exterior algebra"
(%i15) igeowedge_flag:true
(%i16) "Now let us calculate the Laplacian of a scalar field and simplify"
(%i17) canform(hodge(extdiff(hodge(extdiff(f([],[]))))))
(%i18) contract(expand(lc2kdt(%)))
(%i19) ev(%,kdelta)
(%i20) D1:ishow(canform(%))
                  %1 %2  %3 %4                 %1 %2            %1 %2
(%t20)   (- f    g      g      g     ) + f    g      + f       g
             ,%4  ,%3           %1 %2     ,%2  ,%1      ,%1 %2
(%i21) "We can re-express the result using Christoffel symbols, too"
(%i22) ishow(canform(conmetderiv(D1,g)))
               %1 %4  %2 %5      %3                   %1 %2      %3
(%t22) 2 f    g      g      ichr2      g      - f    g      ichr2
          ,%5                    %1 %2  %3 %4    ,%3             %1 %2
                                              %1 %3      %2               %1 %2
                                      - f    g      ichr2      + f       g
                                         ,%3             %1 %2    ,%1 %2
(%i23) "Nice. Now let us repeat the same calculation with torsion"
(%i24) itorsion_flag:true
(%i25) canform(hodge(extdiff(hodge(extdiff(f([],[]))))))
(%i26) "Additional simplifications are now needed"
(%i27) contract(expand(lc2kdt(%th(2))))
(%i28) ev(%,kdelta)
(%i29) canform(%)
(%i30) ev(%,ichr2)
(%i31) ev(%,ikt2)
(%i32) ev(%,ikt1)
(%i33) ev(%,g)
(%i34) ev(%,ichr1)
(%i35) contract(rename(expand(canform(%))))
(%i36) flipflag:not flipflag
(%i37) D2:ishow(canform(%th(2)))
                %1 %2  %3 %4                 %1 %2    %3            %1 %2
(%t37) (- f    g      g      g     ) + f    g      itr      + f    g
           ,%1         ,%2    %3 %4     ,%1           %2 %3    ,%1  ,%2
                                                                          %1 %2
                                                               + f       g
                                                                  ,%1 %2
(%i38) "Another clean result; can also be expressed using Christoffel symbols"
(%i39) ishow(canform(conmetderiv(D2,g)))
               %1 %2  %3 %4      %5                   %1 %2    %3
(%t39) 2 f    g      g      ichr2      g      + f    g      itr
          ,%1                    %2 %3  %4 %5    ,%1           %2 %3
                     %1 %2      %3            %2 %3      %1               %1 %2
             - f    g      ichr2      - f    g      ichr2      + f       g
                ,%1             %2 %3    ,%1             %2 %3    ,%1 %2
(%i40) "Finally, we see that the two results differ only by torsion"
(%i41) ishow(canform(D2-D1))
                                   %1 %2    %3
(%t41)                       f    g      itr
                              ,%1           %2 %3
(%i42) "Last but not least, d^2 is not nilpotent in the presence of torsion"
(%i43) extdiff(extdiff(f([],[])))
(%i44) ev(%,icc2,ikt2,ikt1)
(%i45) canform(%)
(%i46) ev(%,g)
(%i47) ishow(contract(%))
                                       %3
(%t47)                         f    itr
                                ,%3    %275 %277
(%i48) "Reminder: when dim = 2n, the Laplacian is -1 times these results."

The learning curve is steep and there are many pitfalls, but itensor remains an immensely powerful package.

 Posted by at 3:51 pm
Jun 082020
 

Long overdue, but I just finished preparing the latest Maxima release, version 5.44.

I am always nervous when I do this. It is one thing to mess with my own projects, it is another thing to mess with a project that is the work of many people and contains code all the way back from the 1960s.

 Posted by at 2:11 am
May 222020
 

I am one of the maintainers of the Maxima computer algebra system. Maxima’s origins date back to the 1960s, when I was still in kindergarten. I feel very privileged that I can participate in the continuing development of one of the oldest continuously maintained software system in wide use.

It has been a while since I last dug deep into the core of the Maxima system. My LISP skills are admittedly a bit rusty. But a recent change to a core Maxima capability, its ability to create Taylor-series expansions of expressions, broke an important feature of Maxima’s tensor algebra packages, so it needed fixing.

The fix doesn’t amount to much, just a few lines of code:

It did take more than a few minutes though to find the right (I hope) way to implement this fix.

Even so, I had fun. This is the kind of programming that I really, really enjoy doing. Sadly, it’s not the kind of programming for which people usually pay you Big Bucks… Oh well. The fun alone was worth it.

 Posted by at 12:16 am
Jun 052019
 

And before I forget: Last week, wearing my release manager hat I successfully created a new version of Maxima, the open-source computer algebra system. As a result, Maxima is again named one of SourceForge’s projects of the week, for the week of June 10.

The release turned out to be more of an uphill battle than I anticipated, but in the end, I think everything went glitch-free.

Others have since created installers for different platforms, including Windows.

And I keep promising myself that when I grow up, I will one day understand exactly what git does and how it works, instead of just blindly following arcane scripts…

 Posted by at 1:50 pm
Dec 182016
 

I have been so busy this week, I forgot to blog about our latest Maxima release, 5.39. Nothing spectacular, just incremental improvements over 5.38; for me, this was a big milestone though as this was the first time that I used a CentOS platform to prepare the release. (Which, incidentally, is why I haven’t done this months ago.)

And SourceForge, kindly enough, once again designated Maxima as one of the site’s Projects of the Week.

 Posted by at 1:23 am
Apr 022016
 

Sometime last year, I foolishly volunteered to manage new releases of the Maxima computer algebra system (CAS).

For the past several weeks, I’ve been promising to do my first release, but I kept putting it off as I had other, more pressing work obligations.

Well, not anymore… today, I finally found the time, after brushing up on the Git version management system, and managed to put together a release, 5.38.0.

maxima

Maxima is beautiful and incredibly powerful. I have been working on its tensor algebra packages for the past 15 years or so. As far as I know, Maxima is the only general purpose CAS that can derive the field equations of a Lagrangian field theory; for instance, it can derive Einstein’s field equations from the Einstein-Hilbert Lagrangian.

I use Maxima a lot for tensor algebra, though I admit that when it comes to integration, differential equations or plotting, I prefer Maple. Maple’s ODE/PDE solvers are unbeatable. But when it comes to tensor algebra, or just as a generic on-screen symbolic calculator, Maxima wins hands down. I prefer to use its command-line version: Nothing fancy, just ASCII art, but very snappy, very responsive, and does exactly what I want it to do.

So then, Maxima 5.38.0: Say hi to the world. World, this is the latest version of the oldest (nearly half a century old) continuously maintained CAS in existence.

 Posted by at 9:31 pm
Apr 242012
 

I was having a discussion with a lawyer friend of mine. I was trying to illustrate the difference between the advocating done by lawyers and the scientist’s unbiased (or at least, not intentionally biased) search for the truth. One is about cherry-picking facts and arguments to prove a preconceived notion; the other about trying to understand the world around us.

I told him that anything and the opposite of anything can be proven by cherry-picking facts. Then it occurred to me that it is true even in math. For instance, by cherry-picking facts, I can easily prove that \(2\times 2=5\). Let’s start with three variables, \(a\), \(b\) and \(c\), for which it is true that \(a=b+c\). Then, multiplying by 5 gives

$$5a=5b+5c.$$

Multiplying by 4 and switching the two sides gives

$$4b+4c=4a.$$

Adding these two equations together, we get

$$5a+4b+4c=4a+5b+5c.$$

Subtracting \(9a\) from both sides, we obtain

$$4b+4c-4a=5b+5c-5a,$$

or

$$4(b+c-a)=5(b+c-a).$$

Dividing both sides by \(b+c-a\) gives the final result:

$$4=5.$$

And no, I did not make some simple mistake in my derivation. In fact, I can use computer algebra to obtain the same result, and computers surely don’t lie. Here it is, with Maxima:

(%i1) eq1:5*a=5*b+5*c$
(%i2) eq2:4*b+4*c=4*a$
(%i3) eq3:eq1+eq2$
(%i4) eq4:eq3-9*a$
(%i5) eq5:factor(eq4)$
(%i6) eq6:eq5/(b+c-a);
(%o6)                                4 = 5

All I had to do to make this happen was to ignore an inconvenient little fact, which is precisely what lawyers (not to mention politicians) do all the time. Surely, if I can prove that \(2\times 2=5\), I can prove anything. So can lawyers and they know it.

 Posted by at 9:22 am