Aligning equations with multiple marks and lineups in `groff eqn`

593 views Asked by At

I am trying to align some fairly long equations the way I would usually do with LaTeX in groff. The general form I am aiming for:

A = B + C
  = D * E
    + F * G
  = H + I   = J

In LaTeX I would do this as follows:

\documentclass[fleqn]{article}

\usepackage{amsmath}

\begin{document}
\begin{alignat*}{3}
A
  & = B + C \\
  & =
  \begin{aligned}[t]
    & D * E \\
    & + F * G
  \end{aligned} \\
  & = H + I
    && = J
\end{alignat*}
\end{document}

In eqn, equation alignment is accomplished with the mark and lineup commands. Quoting Kernighan and Cherry from Typesetting Mathematics 2nd Ed (found here) on how these work:

The word mark may appear once at any place in an equation. It remembers the horizontal position where it appeared. Successive equations can contain one occurence of the word lineup. The place where lineup appears is made to line up with the place marked by the previous mark if at all possible.

Upon reading this I was under the impression that the system does not prohibit both aligning with the previous mark with lineup as well as setting a new mark within the same line of an equation, e.g. I would expect the following:

.PP
.EQ I
A mark =
B + C
.EN
.EQ I
lineup = mark
D * E
.EN
.EQ I
lineup + F * G
.EN

to produce something like this:

A = B + C
  = D * E
    + F * G

That is not the case, however. eqn aligns the plus sign with the equals:

A = B + C
  = D * E
  + F * G

and produces a warning:

eqn:test.ms:10: multiple marks and lineups

I compile my .ms files with a small script:

eqn $1 -Tpdf | groff -ms -Tpdf > ${1/%.ms/.pdf}

I would like to know if there is some macro that would let me store multiple horizontal offsets (or how to define one). Some clarification as to how exactly mark and lineup macros work would also help.

3

There are 3 answers

4
meuh On BEST ANSWER

Here's a possible solution for having a second mark on the same line as lineup. It uses the base troff sequence \k to mark the current horizontal position into a register myposn. There is then an eqn macro mylineup which calls the eqn special command to run a troff define MyLineup which "returns" a character that has a horizontal movement of myposn units. Before the call eqn sets up some registers with the word following the call, and expects them to be updated to whatever output is desired at that point. We do not use this dummy word, which can be anything, but must not be omitted. For example,

.de MyLineup
. ds 0s "\h'\\n[myposn]u'
. nr 0w \\n[myposn]u
. nr 0h 0
. nr 0d 0
. nr 0skern 0
. nr 0skew 0
..

.EQ I
define mymark '\k[myposn]'
define mylineup 'special MyLineup'
A mark = B + C
.EN
.EQ I
lineup = mymark D * E
.EN
.EQ I
mylineup dummy + F * G
.EN

The lines from .de to .. are the MyLineup definition. The two define lines should be in the first equation. Thereafter, mymark and mylineup dummy can be used in place of mark and lineup. The original mark and lineup are unchanged. If necessary, the real mark can be used again in the line with mylineup, and so on.


To generalise this to having many marks is complicated. The first problem is that mymark is just an eqn define and cannot take a number parameter. It cannot use the special command because it needs to be inline. A trivial solution is just to create a lot of definitions at the start:

define mymark1 '\k[myposn1]'
define mymark2 '\k[myposn2]'

The second problem is that when eqn calls MyLineup it does not pass the following argument ("dummy" which we intend to change to "1") as a parameter of the call, but in the variable 0s; also the string is embedded in formatting code and is actually (in my test anyway) \f[R]\,1\/\fP. So we need to substring the number out of this, with .substring 0s 7 -6. The modified macro and test is:

.\"   gets passed 0s = \f[R]\,1\/\fP     !!! not just 1
.de MyLineup
. substring 0s 7 -6
. ds 0s \\h'\\n[myposn\\*[0s]]u'
. nr 0w \\n[myposn\\*[0s]]u
. nr 0h 0
. nr 0d 0
. nr 0skern 0
. nr 0skew 0
..
.EQ I
define mymark1 '\k[myposn1]'
define mymark2 '\k[myposn2]'
define mylineup 'special MyLineup'
A mark = B + C
.EN
.EQ I
lineup = mymark1 D * mymark2 E
.EN
.EQ I
mylineup 1 + F * G
.EN
.EQ I
mylineup 2 + X
.EN
.EQ I
lineup = H + I
.EN
0
meuh On

I may have been misled by there being several .EQ calls in your example. I was assuming there would be text between the calls and the equations. However, if the intention is to have a single, multi-line equation, it can be done using matrix (or perhaps lpile too). For example,

.nr PS 20p
.nr VS 24p
.PP
.EQ
matrix { 
 lcol { A }
 lcol { = above = above ~ above = }
 lcol { B + C above D * E above + F * G above H + I }
 lcol { ~ above ~ above ~ above = J }
}
.EN

equation

1
meuh On

It is very disappointing that eqn does not allow a new mark to be set. Here is a poor workaround that might be of some use. It consists of repeating the first equation but with the keyword mark in the new position, and diverting the output to nowhere so it does not appear. .di is a base troff to start and end a diversion.

.EQ I
A mark = B + C
.EN
.EQ I
lineup = D * E
.EN
.di mydump
.EQ I
A = mark B + C
.EN
.di
.EQ I
lineup + F * G
.EN

Note, you can also use eqn commands like fwd 99 to move right 99/100 ems, where an em is about the width of the character "m".