forked from BildungBegabung/dsa-latex
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mathalign.sty
executable file
·300 lines (249 loc) · 9 KB
/
mathalign.sty
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
\NeedsTeXFormat{LaTeX2e}[1995/12/01]
\ProvidesClass{mathalign}%
[2005/07/13 Flexible Math Alignment]
%
% This class provides the environment mathalign, which implements a
% slightly more flexible way of math alignment than eqnarray does.
% Well, it's what we need, anyway.
%
% Without \\, & or \label, mathalign behaves like displaymath:
%
% \begin{mathalign}
% a^2 + b^2 = c^2
% \end{mathalign}
%
% Lines are separated by \\ and (by default) will be left aligned.
%
% \begin{mathalign}
% (a + b)^2 = a^2 + 2ab + b^2 \\
% (a - b)^2 = a^2 - 2ab + b^2 \\
% (a + b)(a - b) = a^2 - b^2
% \end{mathalign}
%
% Using _one_ & allows aligning the relations -- multiple ones are
% ignored.
%
% \begin{mathalign}
% (a + b)^2 &= a^2 + ab + ba + b^2 \\
% &= a^2 + 2ab + b^2
% \end{mathalign}
%
% Using one line without & and a following one with & indents the
% relation by 1cm
%
% \begin{mathalign}
% (x - a)(x - b)(x -c)\cdots(x - z) \\
% &= (x - a)\cdots (x - x)\cdots (x - z) = 0
% \end{mathalign}
%
% If a line starts with an equivalence relation, it is put left of the
% whole alignment:
%
% \begin{mathalign}
% a^2 + b^2 &= c^2 \\
% \iff a^2 &= b^2 + b^2
% \end{mathalign}
%
% By default, \Leftrightarrow, \Leftarrow, \Rightarrow and \iff are
% recognised, but every option given to this class which is neither
% fleqn, *reset nor *fakelabels will be added to this list. If *reset
% is among the options, the default list will be replaced instead of
% being added to.
%
% Every line can be given a number by specifying \label:
%
% \begin{mathalign}
% a^2 + b^2 = c^2 \label{pyth}
% \end{mathalign}
%
% is just like the equation-environment. If *fakelabels is among the
% options, we do not display the numbers but instead the labels
% prefixed with `EQ:'.
%
% The environment can be used both in vertical mode and in math mode.
% In vertical mode it behaves like eqnarray, aligning labels to the
% borders of the textarea (it understands the documentwide fleqn
% option). In math mode it packages everything up in a \vcenter,
% typesetting labels a \qquad away from the side of the equation.
%
% First, configuration. Since we want our documents to look
% consistent, we will have to keep track of where the equation
% numbers go.
\newif\ifma@fleqn \ma@fleqnfalse
\DeclareOption{fleqn}%
{\ma@fleqntrue}
% Then, we sometimes (that is, in the preview) don't want equation
% numbers to show up at all -- we want the label names to take
% their place.
\def\ma@labeltext #1{\@eqnnum}
\def\ma@fakelabeltext #1{(EQ:#1)}
\DeclareOption{*fakelabels}%
{\let\ma@labeltext\ma@fakelabeltext}
% Everything else is dedicated to managing the list of equivalence
% relations. This is the default, ...
\def\ma@equivrelations%
{\do\Leftarrow \do\Rightarrow \do \Leftrightarrow \do\iff}
% ... this is how we clear it out, ...
\DeclareOption{*reset}
{\let\ma@equivrelations\empty}
% ... and this is how we add to it.
\def\ma@doaddtoequivrels#1\ma@end%
{\def\ma@equivrelations{#1}}
\def\ma@addtoequivrels#1%
{\expandafter\ma@doaddtoequivrels\ma@equivrelations\do#1\ma@end}
\DeclareOption*%
{\expandafter\ma@addtoequivrels\csname\CurrentOption\endcsname}
\ProcessOptions
% Now for the actual implementation. Since we gather all the
% material in the first column and split it up ourselves, we will
% make the alignment character active. Note that the &s in the
% macros below are _not_ active.
\bgroup
\catcode`\&=\active
\gdef\ma@activateamp%
{\catcode`\&=\active
\let&\ma@column
\let\begin\ma@begin}%
\egroup
% One major problem with the way things are done here is that
% (since we change the catcode of &) nested alignments just
% won't work the way it should. Unfortunately, I don't know a
% way to redefine \halign which always correctly restores
% the catcode and undoes the change, even in the presence of
% \edef-trickery as used in array. We can, however postulate
% that everything within an environment ought to be a normal &.
% This still breaks commands which take ampersands as an argument,
% but it is better than nothing ...
% FIXME can we make this work reliably?
\let\ma@begin@orig\begin
\let\ma@begingroup@orig\begingroup
\def\ma@begin@hook%
{\let\begingroup\ma@begingroup@orig \begingroup
\catcode`\&=4 \let\begin\ma@begin@orig}
\def\ma@begin%
{\let\begingroup\ma@begin@hook \ma@begin@orig}
% Communication between the different columns happens via the
% following means:
\newbox\ma@lhs \newbox\ma@rhs
\def\ma@doiflhs%
{\relax\ifvoid\ma@lhs
\expandafter\@secondoftwo
\else
\expandafter\@firstoftwo
\fi}
\def\ma@doifrhs%
{\relax\ifvoid\ma@rhs
\expandafter\@secondoftwo
\else
\expandafter\@firstoftwo
\fi}
% Note that this assumes that \ma@buildline _always_ empties
% \box\ma@rhs (if possible).
\def\ma@init@communication%
{\global\let\ma@curlabel\empty
\global\let\ma@curequiv\empty}
% Every line ends with the following macro (which will be named \\
% in the end). This macro terminates the first column (thus allowing
% \ma@split to do its work, which will ultimately leave the first
% column empty) and then starts rebuilding the columns.
\def\ma@cr%
{\@ifnextchar\ma@crcr\empty\ma@crcr}
\def\ma@crcr%
{&\ma@buildline}
% Splitting works by typesetting the tokens in an hbox. We have to
% split this macro in two -- passing the tokens as an argument
% prevents expansion, so TeX will not find the & buried in a \\.
% FIXME: No need to split, we can fake it with \aftergroup. But is
% it worth it?
\def\ma@split@start%
{\ma@init@communication
\global\setbox\ma@lhs=\hbox
\bgroup$\displaystyle\ma@startline}
\def\ma@split@stop%
{$\egroup}
% This allows for example (global) redefinition of things like the
% current label or the desired equivalence relation.
\let\ma@label@orig\label
\def\ma@label#1%
{\refstepcounter{equation}\ma@label@orig{#1}%
\gdef\ma@curlabel{\ma@labeltext{#1}}}
% The latter is a bit special, since it is only supposed to be magical
% at the beginning of a line. We therefore introduce a new signal and
% check for it.
\def\ma@signalamount {42sp }
\def\ma@startline {\kern\ma@signalamount}
\def\ma@equiv#1%
{\relax\ifdim\lastkern=\ma@signalamount
\gdef\ma@curequiv{$#1$\qquad}%
\else
#1%
\fi}
% Actually, for equivalence relations this is supposed to happen
% automatically. So let's activate them:
\def\ma@rel@{ma@rel@}
\def\ma@doactivaterels#1%
{\expandafter\let\csname\ma@rel@\string#1\endcsname#1%
\edef#1{\noexpand\ma@equiv
\expandafter\noexpand\csname\ma@rel@\string#1\endcsname}}
\def\ma@activaterels%
{\let\do\ma@doactivaterels \ma@equivrelations}
% Switching columns is realized by terminating the first and starting
% another hbox in a different register. We remember the fact that
% we have done so, among other things to ignore more than one &
% in one row.
\def\ma@column%
{\ma@doiflhs\empty
{$\egroup
\global\setbox\ma@rhs=\hbox
\bgroup$\displaystyle{}}}
% Now that we have \ma@curequiv, \ma@curlabel and the two sides if the
% equation, we can start building the actual alignment. Note that the
% template für these columns will be empty, so we have to insert all
% the \hfils here.
% In case there is no rhs, we fix the left column to be 1cm wide and
% empty and let the right column lap into it. This gives the
% impression of a \lefteqn{...} while still taking the width of the
% line into account.
\def\ma@buildline%
{\ifma@fleqn
\ma@curlabel\hfill\ma@labelspacing
\fi&%
\hfil\ma@curequiv&%
\ma@doifrhs
{\hfil\unhbox\ma@lhs&%
\unhbox\ma@rhs\hfil&}%
{\kern1cm&%
\kern-1cm\unhbox\ma@lhs\hfil&}%
\ifma@fleqn\else
\ma@labelspacing\ma@curlabel\hfil
\fi\cr}
% The actual alignment now only has to activate the commands defined
% above and then get out of the way.
\def\ma@align@start%
{\let\label\ma@label \let\\\ma@cr
\ma@activaterels
\ma@align\bgroup
\ma@split@start##\ma@split@stop
&##%
\tabskip\@centering&##%
\tabskip\z@ &##&##%
\tabskip\@centering&##%
\tabskip\z@\cr}
\def\ma@align@end%
{\ma@crcr\egroup}
% Now we only have to make sure that the ampersand is actually active
% when the equation is scanned. Oh, and look if we are used in math
% mode -- in this case we package everything up in a box and adjust
% the spacing at the labels.
\def\mathalign%
{\ifmmode
\let\ma@labelspacing\qquad \let\ma@align\ialign
\vcenter\bgroup\let\ma@mathalign@end\egroup
\else
\let\ma@labelspacing\empty \def\ma@align{\ialign to \displaywidth}%
$$\def\ma@mathalign@end{$$\@ignoretrue}%
\fi
\ma@activateamp\ma@align@start}
\def\endmathalign%
{\ma@align@end\ma@mathalign@end}
\endinput