summaryrefslogtreecommitdiff
path: root/macros/latex/exptl/graphicp/div16b.sty
blob: 523fa75026f9a0f09c589a30e07841833ef9f25e (plain)
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
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
%
% div16.tex -- perform 16.16 bit fixed point division
% by pts@fazekas.hu at Fri Dec 27 22:40:52 CET 2002 --- ...
%
% This .tex file contains TeX macros for double precision multiplication,
% division of integer and real numbers and image size scaling. The most
% remarkable macro in this file is \@@muldivletposdim (see the comments in
% front of it for detailed documentation).
%
% If you process this file with
% Plain TeX, the unit regression testsuite will be run, and the results of
% the operations issued at the end of this file will be printed to the
% terminal and into the .log file.
%
% The documentation of this macro package is scattered through this file as
% embedded TeX comments.
%
% History
% ~~~~~~~
% started at Fri Dec 27 22:40:52 CET 2002
% --- Sat Dec 28 01:26:52 CET 2002
% 31.31 bit arithmetic: Tue Jan  7 18:12:53 CET 2003
%   skippable, partial \@@muldivpos at Tue Jan  7 21:00:17 CET 2003
%   skippable, finished, doesn't work at Tue Jan  7 21:43:25 CET 2003
%   first successful test case at Tue Jan  7 22:34:32 CET 2003
%   all test cases OK at Tue Jan  7 23:30:32 CET 2003
% Fri Jan 24 12:56:23 CET 2003
%   uses laemu.sty
%

\expandafter\ifx\csname ifLaTeX\endcsname\relax\input laemu.sty\relax\fi%
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{div16b}[2003/01/24 v0.0 high precision div and mul]

%
% SUXX: `\count0=2\count1' and `\advance\count0 by 2\count1' disallowed
% Dat:  `\count0=-\count1' and `\advance\count0 by -\count1' OK
%
% Imp: @@ namespace
%

% \def\@empty{} % from laemu.sty
% \catcode`\@=11 % \makeatletter

%\def\loop#1\repeat{%
%  \def\iterate{#1\relax\expandafter\iterate\fi}%
%  \iterate \let\iterate\relax%
%}%
%\let\repeat\fi%

% vvv so we can nest a \@@loop inside \loop or vica versa
\def\@@loop#1\@@repeat{%
  \def\@@iterate{#1\relax\expandafter\@@iterate\fi}%
  \@@iterate \let\@@iterate\relax%
}%
\let\@@repeat\fi%

%\repeat
%alma
%\iftrue
%korte
%\loop

\def\@@gobbleend#1\end{}%

%** `\@@aftergroupbegin ... \end' types `...' token-by-token \aftergroup
%** Argument of \@@aftergroupbegin must not contain \end, {, or }
\def\@@aftergroupbegin#1{%
  \ifx#1\end%
    %\message{done}
  \else%
    %\message{\string#1}%
    \aftergroup#1%
    \expandafter\@@aftergroupbegin%
  \fi%
}%

%** Doesn't make a \global assignment to #1, but keeps its value after the
%** the next `}'.
%** @param #1 name of a \count, \dimen or \skip register (i.e \count0 or
%**   \jot)
\def\@@noresetaftergroup#1{%
  \@@aftergroupbegin#1=\end%
  \expandafter\@@aftergroupbegin\the#1 \end%
  % ^^^ The space before \end is required, because in LaTeX, \end is expandable
  \aftergroup\relax% finish assignment
}%
%** Doesn't work if the definition of #1 contains strange tokens
%** @param #1 a macro \cs name
\def\@@keepdefaftergroup#1{%
  \aftergroup\def%
  \aftergroup#1%
  \aftergroup{%
  \expandafter\@@aftergroupbegin#1\end%
  \aftergroup}%
}%

%** Performs the `#1 := #2' assignment, avoids `Dimension too large' error if
%** both #1 and #2 are \counts.
%** @param #1 a \count or \dimen register specification
%** @param #2 a \count or \dimen register specification, an integer, a real
%**   number or a dimension. Numbers are assumed to have unit `sp'.
\def\@@letdimcount#1#2{{%
  \afterassignment\@@letdimcount@a\count0=#2 \end%
  % Dat: now \@@letdimcount@c contains any of "", "sp", ".45", ".45pt"
  \afterassignment\@@gobbleend\dimen0=1\@@letdimcount@c sp \end% "1", "1sp" "1.45sp" "1.45pt"
  % Dat: 1.99999999sp == 1sp
  \ifdim\dimen0=1sp%
    %\showthe\count0 \message{\the\count0-sp}%
    \afterassignment\@@gobbleend#1=\count0 sp \end%
  \else%
    \afterassignment\@@gobbleend\dimen0=#2 sp \end%
    \afterassignment\@@gobbleend#1=\dimen0 sp \end%
  \fi%
  \@@noresetaftergroup{#1}% return(\T)
}}%
\def\@@letdimcount@a#1\end{\def\@@letdimcount@c{#1}}%
%** Performs the `#1 := #2' assignment, doesn't avoid `Dimension too large'
%** error if both #1 and #2 are \counts.
%** `\@@letdimcount#1#2' is the same as `\@@letdimcountany#1#2{sp}', but
%** \@@letdimcount avoids the error.
%** @param #1 a \count or \dimen register specification
%** @param #2 a \count or \dimen register specification, an integer, a real
%**   number or a dimension. Numbers are assumed to have unit `#3'.
%** @param #3 default metric unit
\def\@@letdimcountany#1#2#3{{%
  \afterassignment\@@letdimcount@a\dimen0=#2 #3 bp\end%
  #1=\dimen0%
  \@@noresetaftergroup{#1}% return(\T)
}}%

\iffalse
% Regression test for \@@letdimcount:
\@@letdimcount{\count0}{2147483647} \message{\the\count0}
\@@letdimcount{\count0}{2147483647sp} \message{\the\count0}
\@@letdimcount{\count0}{2147483647.0sp} \message{\the\count0}
\@@letdimcount{\count0}{2147483647.999999sp} \message{\the\count0}
\@@letdimcount{\count0}{16383.99999pt} \message{\the\count0}
\@@letdimcount{\dimen0}{16383.99999pt} \message{\the\dimen0}
%\@@letdimcount{\count0}{16384pt} % Dimension too large.
%\@@letdimcount{\dimen0}{2000000000} % Dimension too large.
\@@letdimcount{\dimen0}{\count0} \message{\the\dimen0}
\fi

% ---

% !! why no \space in \write?
%** Performs the assignment `#1 := \dimen0 / \dimen1',
%** @param #1 is a name of a TeX dimen/count register (i.e \jot or \count7).
%**   If dimen, then it contains quotient in dimension `pt' (i.e 1in / 1cm
%**   is 2.54pt). If count, then its value divided by 65536 is the quotient
%**   (so this is 16.16 bit fixed-point notation).
%** \dimen0 must not be negative. \dimen1 must be positive.
%** Overwrites \dimen2.
\def\@@divletpos#1{%
  \dimen2=1pt\relax%
  \multiply#1 0% `#1 := 0' works for both \dimen and \count registers
  \loop\ifdim\dimen0>0pt%
    \immediate\write16{0 a=\the\dimen0:b=\the\dimen1:m=\the\dimen2:q=\the#1}
    \@@loop\ifdim\dimen1<0.5\dimen0%
      \advance\dimen1\dimen1%
      \advance\dimen2\dimen2%
      % ^^^ this is the only statement where an arithmetic overflow can occur.
      %     an arithmetic overflow means that the divisor is zero is very
      %     small positive, so the result will be a ``dimen too large''
    \@@repeat%
    \immediate\write16{1 a=\the\dimen0\space b=\the\dimen1\space m=\the\dimen2\space q=\the#1}
    \@@loop\ifdim\dimen1>\dimen0%
      \divide\dimen1 2%
      \divide\dimen2 2%
      \ifdim\dimen2=0pt%
        \dimen0\dimen2%
        \dimen1\dimen2%
      \fi%
    \@@repeat%
    \immediate\write16{2 a=\the\dimen0\space b=\the\dimen1\space m=\the\dimen2\space q=\the#1}
    \ifdim\dimen0>0pt%
      \@@loop\ifdim\dimen0<8192pt%
        \multiply\dimen0 2%
        \multiply\dimen1 2%
      \@@repeat%
    \fi%
    \immediate\write16{3 a=\the\dimen0\space b=\the\dimen1\space m=\the\dimen2\space q=\the#1}
    \advance\dimen0-\dimen1%
    \advance#1\dimen2%
    % ^^^ Dat: because of this, \dimen2 must be a dimen (and not a count)
    \immediate\write16{4 a=\the\dimen0\space b=\the\dimen1\space m=\the\dimen2\space q=\the#1}
  \repeat%
  %\ifdim\dimen0>0pt \message{SUXX}\fi%
}%

%** The same as \@@divletpos, except for #2 and #3 may be either positive or
%** negative, and they might be either dimen or count registers. (A count is
%** measured in sp.)
%** Please don't pass \dimen0, \dimen1 or \dimen2 as argument.
%** @param #1 is a name of a TeX dimen/count register (i.e \jot or \count7).
%**   If dimen, then it contains quotient in dimension `pt' (i.e 1in / 1cm
%**   is 2.54pt). If count, then its value divided by 65536 is the quotient
%**   (so this is 16.16 bit fixed-point notation).
%** @param #2 and #3 are token lists
%**   containing a _positive_ integer or floating point value
%**   (measured in sp, must be specified explicitly), or a dimen such as `2.54cm'.
\def\@@divletdim#1#2#3{{%
  \afterassignment\@@gobbleend\dimen0=#2 sp\end% \dimen0 := #1, convert count -> sp
  \afterassignment\@@gobbleend\dimen1=#3 sp\end%
  \ifdim\dimen1=0pt%
    \divide\dimen0 by0% trigger a TeX division-by-zero error
  \else%
    \ifdim\dimen1<0pt%
      \dimen1-\dimen1%
      \ifdim\dimen0<0pt%
        \dimen0-\dimen0%
        \@@divletpos{#1}%
      \else
        \@@divletpos{#1}%
        #1-#1%
      \fi%
    \else%
      \ifdim\dimen0<0pt%
        \dimen0-\dimen0%
        \@@divletpos{#1}%
        #1-#1%
      \else
        \@@divletpos{#1}%
      \fi%
    \fi%
  \fi%
  % Imp: round to nearest integer
  \@@noresetaftergroup{#1}%
}}%

% --- Dobule (high) precision multiplication and division.

% The type co31.31 denotes two \count registers, representing a nonnegative
% value in 31.31 bit fixed point notation. The sign bits of the registers
% are unused.

%** Subtracts #3#4 from #1#2, modifies #1#2 in place
%** @param #1 \control-sequence on less
%** @param #2 \control-sequence on greater-or-equal
%** @param #3#4 co31.31
%** @param #5#6 co31.31
\def\@@dolt#1#2#3#4#5#6{%
  \ifnum#3<#5%
    \expandafter#1%
  \else%
    \ifnum#3>#5%
      \expandafter#2%
    \else%
      \ifnum#4<#6%
        \expandafter#1%
      \else%
        \expandafter#2%
      \fi%
    \fi%
  \fi%
}%

%** Subtracts #3#4 from #1#2, modifies #1#2 in place
%** @param #1#2 co31.31
%** @param #3#4 co31.31
\def\@@letsub#1#2#3#4#5{%
  \ifnum#2<#4%
    \advance#1-1%
    \advance#2-#4%
    \advance#2-#5% #2 += 0x80000000
  \else%
    \advance#2-#4%
  \fi%
  \advance#1-#3%
}%

%** Adds #3#4 to #1#2, modifies #1#2 in place
%** @param #1#2 co31.31
%** @param #3#4 co31.31
%** @param #5 \countx, whose value is -0x80000000 (-2147483648)
\def\@@letadd#1#2#3#4#5{%
  % \message{add:\the#3:\the#4}%
  \advance#2#5% #2 -= 0x80000000
  \ifnum#2<-#4% orig#2-0x80000000<-#4 <=> orig#2+#4 < 0x80000000
    \advance#2-#5% #2 += 0x80000000
  \else%
    \advance#1 1%
  \fi%
  \advance#2#4%
  \advance#1#3%
}%

%** Multiplies #1#2 by two, modifies #1#2 in place
%** @param #1#2 co31.31
\def\@@lettwice#1#2{%
  \advance#1#1%
  \ifnum#2>1073741823%
    \advance#1 1%
    \advance#2-1073741824%
  \fi%
  \advance#2#2%
}%

%** Divides #1#2 by two, discards LSB, modifies #1#2 in place
%** @param #1#2 co31.31
\def\@@lethalf#1#2{%
  \divide#2 2%
  \ifodd#1%
    \advance#2 1073741824%
  \fi%
  \divide#1 2%
}%

% vvv SUXX: \newif is `\outer'
\newif\ifLOOP
% \def\x{ \expandafter\newif\csname ifLOOP\endcsname}%

%** `\@@muldivletposdim#1#2#3#4' performs the assignment `#1 := #2 * #3 / #4'.
%** `\@@muldivletposdim#1{1pt}#3#4' performs the assignment `#1 := #3 / #4'.
%** `\@@muldivletposdim#1#2#3{1pt}' performs the assignment `#1 := #2 * #3'.
%**
%** All source arguments (#2, #3, #4) can be valid TeX dimen/count values, or
%** \dimen / \count register specifications. Input values may be negative or
%** positive or zero.
%**
%** -- Before each multiplication and division, the arguments are converted
%**    to `pt' (if the argument is a bare number or a \count register, it is
%**    treated as being a dimension in `sp'), the operation is performed on
%**    the numbers, and `pt' is added
%**    to the resulting number, so the result will be a dimension.
%** -- The default unit of measure is `sp'. That is, all bare numbers and
%**    \count registers are treated as being a dimension in `sp'. This
%**    applies to both source and target argument. (You may notice that 1pt
%**    == 65536sp. So the operations are performed in a fixed-point
%**    arithmetic with 16 bits in the fraction.)
%** -- The multiplication is performed exactly (i.e without loss of
%**    precision). However, the division and the 31.31 -> 15.16 rounding
%**    after the division may degrade precision. Internal calculations are
%**    performed in fixed-point arithmetic with 31 bits in the fraction.
%** -- The following constants are valid source arguments, and represent the
%**    same number (one and a half): `1.5pt', `98304sp', `98304 sp',
%**    `98304', `98304.1sp', `98304.9sp'.
%** -- To specify the real number `1.5', please specify `1.5 pt'.
%** -- To specify the integer `42', please specify `42 pt'.
%** -- If you do only division, set #2 := `1pt'. In that special case you may
%**    specify bare integers (or `... sp') for _both_ #3 and #4.
%** -- The following are valid arguments: `\count0',
%**    `\dimen7', `\jot', `\interdisplaylinepenalty'
%** -- Due to a limitation of TeX's fixed-precision arithmetic, it is
%**    impossible to specify exactly `1.5pt' in inches. `0.02075958251953in'
%**    is `1.49974pt', and `0.02075958251954in' is `1.50084pt'. (Of course,
%**    `1.5pt' is `1.5pt'.) That's because TeX converts the real constant
%**    0.02075958251953 to the fraction `1360/65536' and
%**    0.02075958251954 to the fraction `1361/65536', before multiplying.
%** -- If you specify >=2 of \dimen0, \dimen1 and \dimen2 as input, please
%**    specify them in this order.
%**
%** @param #1 target, must be a \dimen or \count register
%**   specification (including `\count0' and `\jot'). If a \dimen register,
%**   its maximum will be 1073741823sp. If a \count register, its maximum
%**   will be 2147483647.
%** @param #2 must be >=0
%** @param #3 must be >=0
%** @param #4 must be >0
%**
\def\@@muldivletposdim#1#2#3#4{{%
  %\afterassignment\@@gobbleend\dimen0=#2 sp\end% \dimen0 := #1, convert count -> sp
  %\afterassignment\@@gobbleend\dimen1=#3 sp\end%
  %\afterassignment\@@gobbleend\dimen2=#4 sp\end%
  \countdef\Y 2 \@@letdimcount\Y{#4}%
  \countdef\F 14 \ifnum\Y<0 \Y-\Y \F-1 \else \F1 \fi%
%  \showthe\Y%
  \ifnum0=\Y % SUXX: \ifnum\Y=0 doesn't work
    \divide\Y0% trigger a TeX division-by-zero error (`arithmetic overflow')
  \else%
    % vvv declare my input arguments as local variables
    \countdef\XA 0 \@@letdimcount\XA{#2}% treat dimen as in `sp'
    \ifnum\XA<0 \XA-\XA \F-\F\fi%
    \countdef\XB 1 \@@letdimcount\XB{#3}%
    \ifnum\XB<0 \XB-\XB \F-\F\fi%
    %\countdef\XA 0 \XA\dimen0% treat dimen as in `sp'
    %\countdef\XB 1 \XB\dimen1%
    %\countdef\Y  2 \Y \dimen2%
    % vvv declare local variables
    \countdef\C  3%
    \countdef\B  4%
    \countdef\R  5%
    \countdef\S  6%
    \countdef\T  7%
    \countdef\U  8%
    \countdef\D  9%
    \countdef\A 10%
    \countdef\L 11%
    \countdef\M 12%
    % vvv ++++ comment out the line below to get rid of the error ++++
    \countdef\G 13 \relax \G-1073741824 \advance\G\G% G := -0x80000000
    %
    % Imp: support negative numbers here
    \ifnum\XB=\Y% shortcut
      \T\XA%
    \else%
      \ifnum\XA=\Y% shortcut
        \T\XB%
      \else% really multiply and divide
        \@@muldivpos%
        %
        % vvv convert (round) \T\U: co31.31 -> \T: dimen15.16
        %     return (\U>=0x7fffc000) ? ((\T+1)<<16) : (\T<<16)+((\U+0x4000)>>15)
        \multiply\T65536% result too large
        \ifnum\U<2147467264% 0x80000000-0x4000
          \advance\U16384%
          \divide\U32768%
          \advance\T\U%
        \else%
          \advance\T65536%
        \fi%
      \fi%
    \fi%
    \ifnum\F<0 \T-\T\fi%
    %\afterassignment\@@gobbleend#1=\T sp\end% works if #1 is a dimen/count
    \@@letdimcount{#1}\T%
    \@@noresetaftergroup{#1}% return(\T)
  \fi% else of division-by-zero
}}%

%** Performs the assignment `\T\U := \XA * \XB / \Y'. Destroys contents of
%** \XA \XB \Y \C \B \R \S \T \U \D \A \L and \M during operations. All
%** control sequences mentioned in this paragraph must be predefined \count
%** registers.
%**   Assumes \newif\LOOP, sets \LOOPfalse upon return
%**   Inputs are: \XA \XB \Y and \G. \XA \XB \Y must be in 15.16 bit
%** fixed-point notation (i.e dimension in `pt' converted to a \count).
%** \G must be -0x80000000.
%**   Outputs are \T and \U. \T\U is a real number in 31.31 bit fixed-point
%** notation (the sign bits are unused).
%**   This is helper routine called by \@@muldivletposdim.
\def\@@muldivpos{%
  %\message{A}%
  \R\XA \divide\R65536 \S-\R \multiply\S65536 \advance\S\XA% my(\R,\S)=(( \XA >>16), \XA &0xffff);
  %\message{A.S=\the\S,R=\the\XA}%
  \T\XB \divide\T65536 \U-\T \multiply\U65536 \advance\U\XB% my(\T,\U)=(( \XB >>16), \XB &0xffff);
  \D0 \A\Y \divide\A65536 \multiply\A32768 % my (\D,\A)=(0, ((( \Y >>16)&0xffff)<<15)); % round() final result
  \L\R \advance\L\L \multiply\L\T \M0 \@@letadd\D\A\L\M\G% \@@letadd( (\D,\A), ((\R*\T)<<1, 0)
  \L\R \multiply\L\U \M\L \divide\L32768 \multiply\L32768 \advance\M-\L \divide\L32768 \multiply\M65536 \@@letadd\D\A\L\M\G% \@@letadd( (\D,\A), ((\R*\U)>>15, ((\R*\U)&0x7fff)<<16)
  \L\S \multiply\L\T \M\L \divide\L32768 \multiply\L32768 \advance\M-\L \divide\L32768 \multiply\M65536 \@@letadd\D\A\L\M\G% \@@letadd( (\D,\A), ((\S*\T)>>15, ((\S*\T)&0x7fff)<<16)
  \ifnum\S>32767 %
    \L0 \M\U \multiply\M32768 \@@letadd\D\A\L\M\G% \@@letadd( (\D,\A), (0, \U<<15)
    \advance\S-32768%
  \fi%
  %\message{B.S=\the\S,U=\the\U}%
  \L0 \M\S \multiply\M\U \@@letadd\D\A\L\M\G% \@@letadd( (\D,\A), (0, \S*\U)
  \C\Y \divide\C 32768 \B-\C \multiply\B32768 \advance\B\Y \multiply\B65536 % my(\C,\B)=(( \Y >>15),(( \Y &0x7fff)<<16));
  \L1 \M0 \T0 \U0% my (\L,\M)=(1,0); (\T,\U)=(0,0);
  %\showthe\D \showthe\A \showthe\C \showthe\B
  %\message{C}%
  \loop% vvv while (\D!=0 or \A!=0) % DA != 0 % \D!=0 <=> \D>0
    %\message{if}%
    \LOOPfalse%
    \ifnum\D>0 \LOOPtrue \fi% Dat: must have a space after `>0'
    \ifnum\A>0 \LOOPtrue \fi%
    \ifLOOP% loop condition
    \R\D \S\A \@@lethalf\R\S% (\R,\S)=(\D,\A); _Half(\R,\S);
    \@@loop% vvv while (_lt((\C,\B),(\R,\S))) % while (CB<DA/2)
      %\message{ina}%
      \@@dolt\LOOPtrue\LOOPfalse\C\B\R\S%
      \ifLOOP% loop condition
      \@@lettwice\C\B%
      \ifnum\L=1073741824% Dat: \L\M is a power of 2
        \divide\L0% die "overflow or division by 0" if \L<0
      \fi%
      \@@lettwice\L\M%
    \@@repeat%
    \@@loop% vvv while (_lt((\D,\A),(\C,\B)))
      %\immediate\write16{D=\the\D:A=\the\A:C=\the\C:B=\the\B}%
      %\message{inb}%
      \@@dolt\LOOPtrue\LOOPfalse\D\A\C\B%
      \ifLOOP% loop condition
      \ifnum\D<1073741824 \@@lettwice\D\A \else \@@lethalf\C\B \fi%
      \@@lethalf\L\M%
      \ifnum0=\L \ifnum0=\M %
        \D0 \A0% (\D,\A)=(0,0) if \L==0 and \M==0;
        \B0 \C0%
      \fi\fi%
    \@@repeat%
    \@@letsub\D\A\C\B\G%
    \@@letadd\T\U\L\M\G%
  \repeat%
  \ifnum\T>32767\divide\T0\fi% die "overflow2 or division by 0" if \T>=0x8000
  % ^^^ !! Imp: more meaningful overflow error message (here and at other places)
  % return value: \T\U: co31.31
  % to convert it to \dimen, do: (\U>=0x7fffc000) ? ((\T+1)<<16) : (\T<<16)+((\U+0x4000)>>15)
}%

\endinput%