Calc

Calc is a calculator with many advanced features, once described as the poor man’s Mathematica. It has been part of GNU Emacs since version 22.

As a simple calculator

If all you need is a simple calculation and don’t want to deal with RPN mode, you can use Calc in algebraic mode. To do so, just run Calc with M-x calc <RETURN>, and then hit apostrophe (’) - now you can type in your formula normally and hit enter for the result.

    ' 3 + 7 * (5 - 2)
    24

Quick tutorial

Calc’s standard interface acts like a traditional RPN calculator, operated by the normal Emacs keyboard. When you start the Calculator, the Emacs screen splits into two windows with the file you were editing on top and Calc on the bottom.

     ...
     --**-Emacs: myfile             (Fundamental)----All----------------------
     --- Emacs Calculator Mode --                    |Emacs Calc Mode v2.00...
     2:  17.3                                        |    17.3
     1:  -5                                          |    3
         .                                           |    2
                                                     |    4
                                                     |  * 8
                                                     |  ->-5
                                                     |
     --%%-Calc: 12 Deg       (Calculator)----All----- --%%-Emacs: *Calc Trail*

As you can see, Calc actually makes two windows side-by-side. The lefthand one is called the “stack window” and the righthand one is called the “trail window.” The stack holds the numbers involved in the calculation you are currently performing. The trail holds a complete record of all calculations you have done. In a desk calculator with a printer, the trail corresponds to the paper tape that records what you do.

In this case, the trail shows that four numbers (17.3, 3, 2, and 4) were first entered into the Calculator, then the 2 and 4 were multiplied to get 8, then the 3 and 8 were subtracted to get -5. (The `>’ symbol shows that this was the most recent calculation.) The net result is the two numbers 17.3 and -5 sitting on the stack.

Most Calculator commands deal explicitly with the stack only, but there is a set of commands that allow you to search back through the trail and retrieve any previous result.

Calc allows you to work with many different data types: Integers, fractions, and floats are various ways of describing real numbers. HMS forms also for many purposes act as real numbers. These types can be combined to form complex numbers, modulo forms, error forms, or interval forms. Finally, all these types of numbers may be combined into vectors, matrices, or algebraic formulas (hit the apostrophe key to enter algebraic expressions).

Calc even allows you to simplify formulas, perform symbolic differentiation and integration, numerical integration, and to compute taylor series. It helps you solve equations symbolically, helps you find solutions numerically, and it helps you fit curves.

In short, Calc is a powerful, although mostly non-graphical, computer algebra system (disguised as a whimpy little RPN calculator).

Calc from Emacs Lisp

Emacs Lispers frustrated by Emacs’s lack of bignum handling: calc.el provides very good bignum capabilities.

If must make sure that calc is loaded before you start using it.

(require 'calc-ext)
(defvar calc-command-flags)

The last command defines calc-command-flags as nil. If this is not defined Calc won’t work (but this will be taken care of as of Emacs 23.2).

If you want to make sure that any personal Calc settings are also loaded, you might want to run:

(load calc-settings-file t)

or, if you want to be more careful (the following is from calc-mode in calc.el):

(or calc-loaded-settings-file
    (null calc-settings-file)
    (equal calc-settings-file user-init-file)
    (progn
        (setq calc-loaded-settings-file t)
	(load (file-name-sans-extension calc-settings-file) t)))

Now stuff like (math-add 1 2), (Math-lessp (math-expt 2 100) (math-expt 3 50)) will work.

Even “operator overloading” works. If you use defmath instead of defun, then all operators will be replaced with their bignum equivalents. Thus, you can now use < instead of math-lessp.

elispexample

Examples of Calc used from EmacsLisp

calc from EmacsLisp is quite hard to grok. NicFerrier used it in the BSON decoding implementation for mongo-el (MongoDb database adapter for EmacsLisp).

Here's a link to his implementation

Date calculation

While it is possible to do date calculations using the plain Elisp functions, Calc is more powerful. The following example implements a function that can be used to return a time duration between two dates. It seems that the more convenient Calc functions to use directly from Elisp are prefixed by ‘calcFunc-’ and suffixed by their arithmetic name. For example, in the Calc info manual, the command to return the number of business days between two dates is shown as “‘t -’ (‘calc-business-days-minus’) [‘bsub’]”. The arithmetic name of that function is thus “bsub” [1], which means we can use it from Elisp using ‘calcFunc-bsub’.

[1] This is explained in the “info ‘(calc)Notations Used in This Manual’”.

Example without Calc

The first example is implemented without Calc using Emacs date primitives such as ‘time-substract’, ‘time-add’, ‘date-to-time’ and ‘time-to-number-of-days’. The implemented function ‘days-of-presence’ can be used to calculate the number of days between two days, and optionally a number of periods of absence to exclude, as shown in the examples below.

(defun time-of-presence (start-time end-time &optional absence-periods-time)
  (let ((absence-time
         (seq-reduce #'time-add
                     (mapcar (lambda (x)
                               (time-subtract (cadr x) (car x)))
                             absence-periods-time)
                     '(0 0 0 0))))
    
    (seq-reduce #'time-subtract (list start-time absence-time) end-time)))

(defun days-of-presence (start-date end-date &optional absence-periods)
  "Return the number of days given a START-DATE, END-DATE and
ABSENCES periods. All dates should be formatted as RFC822 date
strings (e.g., \"Sat Sep 12 12:21:54 1998 +0200\") and
ABSENCE-PERIODS should be a given as a list of two-tuples like
\(\(START-ABSENCE-DATE END-ABSENCE-DATE\) ...\)"
  (let ((absence-periods-time
         (mapcar (lambda (x)
                   (list (date-to-time (car x)) (date-to-time (cadr x))))
                 absence-periods)))

    (time-to-number-of-days (time-of-presence (date-to-time start-date)
                                              (date-to-time end-date)
                                              absence-periods-time))))
;; Days of presence in 2015
(days-of-presence "Fri Aug 14 00:00:00 2015 -0700"
                  "Fri Dec 25 00:00:00 2015 -0700")
;; => 133 days

;; Days of presence in 2016
(days-of-presence "Tue Jan 05 00:00:00 2016 -0700"
                  "Sat Dec 31 00:00:00 2016 -0700"
                  '(("Tue Oct 04 00:00:00 2016 -0700"
                     "Tue Oct 11 00:00:00 2016 -0700")
                    ("Fri Dec 23 00:00:00 2016 -0700"
                     "Fri Dec 30 00:00:00 2016 -0700")))
;; => 347 days
Example with Calc

The second example shows how to use Calc functions ‘calcFunc-bsub’ or ‘calcFunc-sub’ and ‘calc-eval’ to implement the same thing, with the added feature of being able to calculate both regular days or business days.

(require 'calc-ext)

(defun calc-count-days (start-date end-date &optional absence-periods
                                   exclude-weekends?)
  "Return the number of days between START-DATE and END-DATE,
minus any days covered in one or more ABSENCE-PERIODS. Dates
should be entered a string such as \"YYYY, MM, DD\".
ABSENCE-PERIODS should be a given as a list of pairs like
\(\(START-ABSENCE-DATE END-ABSENCE-DATE\) ...\).
EXCLUDE-WEEKENDS? can be set to `t' to exclude weekend days from
the calculated number of days."
  (let ((date-sub-func (if exclude-weekends? #'calcFunc-bsub #'calcFunc-sub)))
    (defun eval-calc-date (calc-date-string)
      (calc-eval (concat "(date(" calc-date-string "))") 'raw))

    (defun eval-absence-periods (absence-periods)
      (mapcar (lambda (x)
                (list (eval-calc-date (car x))
                      (eval-calc-date (cadr x))))
              absence-periods))

    (defun calculate-absent-days (absences)
      (seq-reduce #'+
                  (mapcar (lambda (x)
                            (funcall date-sub-func (cadr x) (car x)))
                          absences)
                  0))

    (let* ((start (eval-calc-date start-date))
           (end (eval-calc-date end-date))
           (absences (eval-absence-periods absence-periods))
           (absent-days (calculate-absent-days absences)))
      (calc-eval "$ - $$" nil (funcall date-sub-func end start) absent-days))))

;; Days of presence in 2015
(calc-count-days "2015, 08, 14" "2015, 12, 25")
;; => 133 days

;; Days of presence in 2016
(calc-count-days "2016, 01, 05" "2016, 12, 31"
                  '(("2016, 10, 04" "2016, 10, 11")
                    ("2016, 12, 23" "2016, 12, 30")))
;; => 347 days

;; Days of presence in 2017
(calc-count-days "2017, 01, 01" "2017, 06, 10"
                 '(("2017, 03, 16" "2017, 04, 15")))
;; => 130 days

;; Days of work in 2015
(calc-count-days "2015, 08, 14" "2015, 12, 25" nil t)
;; => 95 days

;; Days of work in 2016
(calc-count-days "2016, 01, 05" "2016, 12, 31"
                  '(("2016, 10, 04" "2016, 10, 11")
                    ("2016, 12, 23" "2016, 12, 30")) t)
;; => 249

Defining Units

You can see the Calculator Units Table with the command calc-enter-units-table (bound to u v in the “stack window”). New units can be added in your .emacs file like so:

(eval-after-load "calc-units"
  '(progn
     (setq math-additional-units
           '((Bohr "0.05291772083 * nm" "Atomic unit of distance")
             (Debye "0.3934301 * e * Bohr" "Unit of dipole moment")
             (Buckingham "Debye * Ang" "Unit of quadrupole moment")
             (Hartree "4.35974381e-18 * J" "Atomic unit of energy")
             (Stone "14 * lb" "Stone (mass)"))
           math-units-table nil)))

Alternate way to define units

Another way to define units is to put your expression on the stack, say

0.05291772083 nm

and then call calc-define-unit with

u d  

This new definition can be saved permanently with calc-permanent units by typing

u p

User defined units can also be edited with

s U

Minimalistic calculation

I always calculate something, so there is a way i use calc.el

;; usefull mini calculator
(defun my-mini-calc (expr &optional arg)
  "Calculate expression

If ARG is given, then insert the result to current-buffer"
  (interactive
   (list (read-from-minibuffer "Enter expression: ")
	 current-prefix-arg))

  (let ((result (calc-eval expr)))
    (if arg
	(insert result)
      (message (format "Result: [%s] = %s" expr result)))))

I bind it to ‘C-c m c’ and found it very usefull for calculations at point.

Quick calc

There is also quick-calc, which can be called with `M-# q

It will give you a prompt in the minibuffer, and the result will be given in the minibuffer and put in the kill-ring. If the input is ended with a linefeed (or ‘C-j’) instead of RET, the result will be put in the current buffer as well as the minibuffer.

Embedded Formula To Stack

I was seeking out a function to take an embedded formula, evaluate it and put it on the stack. I didn’t find one so here is the one I wrote. Beware, it uses current calc settings.

(defun calc-embedded-formula-to-stack ()
  (interactive)
  (save-excursion
    (save-match-data
     (calc-embedded-find-bounds)))
  (let ((eq-str (buffer-substring calc-embed-top calc-embed-bot)))
    (calc-eval eq-str 'push)))

To use defaults replace eq-str with (list eq-str). To specify options overriding defaults, replace it with, for example,

(append (list eq-str) '(calc-internal-prec 17 calc-float-format (sci 12)))

Allowing the mouse to insert data

When dealing with calc, a common problem I run into is how to yank data into calc using the mouse. The solution I’ve found is binding mouse-2 to the calc-yank function.

   (define-key calc-mode-map [mouse-2] 'calc-yank)

Calculate the Region, Then Insert

With the following function you can evaluate an expression using Calc’s calc-eval. With prefix arg, the result will be appended after the region itself, just the way shell-command does.

(defun calc-eval-region (arg beg end)
  "Calculate the region and display the result in the echo area.
With prefix ARG non-nil, insert the result at the end of region."
  (interactive "P\nr")
  (let* ((expr (buffer-substring-no-properties beg end))
         (result (calc-eval expr)))
    (if (null arg)
        (message "%s = %s" expr result)
      (goto-char end)
      (save-excursion
        (insert result)))))

PiotrMieszkowski

Feature Proposals

Imagine the possible features of Calc:

- Easy defining of new language modes (all the languages you use!)

- External computations (would Maxima or Yacas calculate the indefinite integral differently? maybe Octave should integrate the ODE?)

- Drag & drop (this term goes there, reduce this with that, to the other side of equation with it… and voila!)

- Verification history (no more cheating: let’s see how you derived it, Calc)

- Interaction with preview-latex (to minimise post-editing of TeX code; WYSIWYG: What You See Incrementally When You Go on with transforming your formula)

- Support for basic algebra integrated with drag & drop: e.g. registering a function as a quadratic form

What is interesting about Calc is that it’s nearly there… (or at least, half the way.) Calc has language modes. Calc has powerful rewrites. Calc has selections. But the language interface is hard-coded not generally enough. – LukaszStafiniak

Other resources

Open Questions

Calc indicates that “window-edges” is void when started and quit from an frame with multiple windows under xemacs on Win32, Mac OS X, Linux

“window-edges” is not defined in xemacs. See [1] for a workaround.

Emacs 21

Note that you have to apply a patch to some of the calc files to make this work under Emacs 21, and those patches are incompatible with Emacs 20! I have had to create subdirectories in my lisp directory, called “emacs20” and “emacs21”, which only are added to the load-path in their respective versions, and each of which contains its emacs-specific version of this code. The patch can be found in etc/PROBLEMS (‘C-h P’). – CharlesSebold

The latest (1997-01-05) version not included in Emacs is ftp://ftp.gnu.org/pub/gnu/calc/calc-2.02f.tar.gz. That version doesn’t work out of the box for any recent Emacs. It works for NT Emacs, but the installation might not be straight forward if you haven’t installed make. The version that works is the one that comes with newer version of GNU Emacs and with EmacsFromCVS.


CategoryCalculators CategoryModes