Solving Equations With Emacs literate-calc-mode
When literate programming meets spreadsheet calculations: learn how to integrate calculations into your writing projects using Emacs and literate-calc-mode.
The wonderful Emacs package literate-calc-mode combines the power of Emacs Calc with the readability of plain text to let you document your math in a more friendly, less technical way, similar to literate programming.
Today I want to show you how to take it one step further, leveraging the solving capabilities of Calc to do something a bit more fancy than your average calculator app or spreadsheet.
Installation and preparation
Use MELPA and M-x package-install
to install literate-calc-mode
. Or if you’re using Doom Emacs, just add this line to packages.el
and run doom sync
in a terminal:
;; ~/.config/doom/packages.el (package! literate-calc-mode)
Now you’re ready to type out a series of expressions or equations like this, in any major mode
Price = 80.00 AUD
Discount = 26%
= Price - (Price * Discount)
Then enable literate-calc-minor-mode
to show the results, which will appear as overlays next to your text, starting with =>
Price = 80.00 AUD
=> Price: 80. AUD
Discount = 26%
=> Discount: 0.26
= Price - (Price * Discount)
=> 59.2 AUD
Let’s dive into a more complicated calculation now, using Calc’s solve
🚀
1. Set up the known values
We’re going with a bread baking example here, which seems to be traditional in these kinds of examples. Math in the kitchen can be difficult! 😵💫
Dough = uconv(1.68kg, g)
=> Dough: 1,680. g
Hydration = 65%
=> Hydration: 0.65
Salinity = 2%
=> Salinity: 0.02
Inoculation = 1%
=> Inoculation: 0.01
Note that uconv
is a custom algebraic function for easy unit conversion1
Also note that this example is different to the one demonstrated in the literate-calc-mode
README, in that we’re starting with the final dough weight and back-calculating the individual components2 🤯
2. Set up the system of equations
These are the four equalities that we want to solve:
e1 = Dough - (Water + Flour + Salt + Yeast)
=> e1: 1,680. g - Water - Flour - Salt - Yeast
e2 = Hydration - (Water / Flour)
=> e2: 0.65 - Water / Flour
e3 = Salinity - (Salt / Flour)
=> e3: 0.02 - Salt / Flour
e4 = Inoculation - (Yeast / Flour)
=> e4: 0.01 - Yeast / Flour
Why write the equalities in such an indirect way? We’re using the fact that each each equality will be implicitly solved for zero.
So instead of saying “the weight of the dough should equal the total weight of water, flour, salt and yeast”, we move things around to state that “the weight of the dough minus the total weight of water, flour, salt and yeast should equal zero”. These two statements are mathematically equivalent, and makes using solve
a lot easier in the next step.
3. Solve for unknown variables
Now we just need to plug in e1
e2
e3
and e4
into solve
and ask it for the values of Water
Flour
Salt
and Yeast
Solution = solve([e1, e2, e3, e4], [Water, Flour, Salt, Yeast])
=> Solution: [Water = 650. g, Flour = 1,000. g, Salt = 20. g, Yeast = 10. g]
You can absolutely stop here if the above style of solution is good enough to your eye.
Destructure solution & final results (optional)
Maybe you want to extract out each component in the solution vector and reassign the appropriate variables, to make things tidier.
Water = rmeq(mcol(Solution, 1))
=> Water: 650. g
Flour = rmeq(mcol(Solution, 2))
=> Flour: 1,000. g
Salt = rmeq(mcol(Solution, 3))
=> Salt: 20. g
Yeast = rmeq(mcol(Solution, 4))
=> Yeast: 10. g
Note: rmeq
removes the equal sign in each of the solution results and mcol
extracts out a specified element of a vector, by index.
Now our four key variables look like this:
= Water
=> 650. g
= Flour
=> 1,000. g
= Salt
=> 20. g
= Yeast
=> 10. g
Footnotes:
uconv
is a custom function that exposes Emacs Calc’s unit conversion utilities via an algebraic function, which lets us use it in with literate-calc-mode
. It’s defined in my Doom Emacs config.el
as:
(after! calc (defalias 'calcFunc-uconv 'math-convert-units))
I have actually done calculations like this before, while cooking. I’m probably just bad at math, but I found it difficult to figure out ingredient weights going back from baker’s percentages unless I used actual algebra.