Built with Alectryon, running Coq+SerAPI. Bubbles () indicate interactive fragments: hover for details, tap to reveal contents. Use Ctrl+↑ Ctrl+↓ to navigate, Ctrl+🖱️ to focus. On Mac, use instead of Ctrl.

Using MathJax with Alectryon

This example shows how to combine Alectryon with MathJax. We'll do a pretty-printed version of the proof that \(\sum_{i = 0}^n i = \frac{n \cdot (n + 1)}{2}\).

First, we start with a definition of sum:

Require Import NArith ArithRing.

Fixpoint nsum max f :=
  match max with
  | O => f 0
  | S max' => f max + nsum max' f
  end.

Then, we add notations to print LaTeX code (this is only one way to do it; another way would be to parse Coq's output to reconstruct LaTeX):

Notation "'\ccNat{}'" := nat.
Notation "'\ccSucc{' n '}'" := (S n).
Infix "\times" := mult
  (at level 30).
Notation "'\ccNsum{' x '}{' max '}{' f '}'" :=
  (nsum max (fun x => f))
    (format "'\ccNsum{' x '}{' max '}{' f '}'").
Notation "\ccNot{ x }" := (not x)
  (at level 100).
Infix "\wedge" := and
  (at level 190, right associativity).
Notation "A \Rightarrow{} B" := ( (_ : A), B)
  (at level 200, right associativity).
Notation "'\ccForall{' x .. y '}{' P '}'" := ( x, .. ( y, P) ..)
  (at level 200, x binder, y binder, right associativity,
   format "'\ccForall{' x .. y '}{' P '}'").

Then, we add MathJax definitions for each of these custom macros (look at the page source to see them):

\(\newcommand{\ccQ}{\mathbb{Q}}\) \(\newcommand{\ccNat}{\mathbb{N}}\) \(\newcommand{\ccSucc}[1]{\mathrm{S}\:#1}\) \(\newcommand{\ccFrac}[2]{\frac{#1}{#2}}\) \(\newcommand{\ccPow}[2]{{#1}^{#2}}\) \(\newcommand{\ccNot}[1]{{\lnot #1}}\) \(\newcommand{\ccEvar}[1]{\textit{\texttt{#1}}}\) \(\newcommand{\ccForall}[2]{\forall \: #1. \; #2}\) \(\newcommand{\ccNsum}[3]{\sum_{#1 = 0}^{#2} #3}\)

Then we set up MathJax to render the proofs properly (look at the page source to see the relevant script):

And finally we write the actual proofs:


\ccForall{n : \ccNat{}}{2 \times \ccNsum{i}{n}{i} = n \times (n + 1)}

2 \times 0 = 0 \times (0 + 1)
n:\ccNat{}
IHn:2 \times \ccNsum{i}{n}{i} = n \times (n + 1)
2 \times (\ccSucc{ n} + \ccNsum{i}{n}{i}) = \ccSucc{ n} \times (\ccSucc{ n} + 1)

2 \times 0 = 0 \times (0 + 1)
reflexivity.
n:\ccNat{}
IHn:2 \times \ccNsum{i}{n}{i} = n \times (n + 1)

2 \times (\ccSucc{ n} + \ccNsum{i}{n}{i}) = \ccSucc{ n} \times (\ccSucc{ n} + 1)
n:\ccNat{}
IHn:2 \times \ccNsum{i}{n}{i} = n \times (n + 1)

2 \times \ccSucc{ n} + 2 \times \ccNsum{i}{n}{i} = \ccSucc{ n} \times (\ccSucc{ n} + 1)
n:\ccNat{}
IHn:2 \times \ccNsum{i}{n}{i} = n \times (n + 1)

2 \times \ccSucc{ n} + n \times (n + 1) = \ccSucc{ n} \times (\ccSucc{ n} + 1)
ring. Qed.

Note that Alectryon loads MathJax with the defer attribute, so if you need to call MathJax.typeset() or MathJax.typesetPromise(), you'll want to do that from a deferred script or from a DOMContentLoaded event listener.