summaryrefslogtreecommitdiff
path: root/source/luametatex/source/tex/texarithmetic.c
blob: d9cf9859d21a0158bb5702fea44918dbf160d1ce (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
/*
    See license.txt in the root of this project.
*/

# include "luametatex.h"

/*tex

    The principal computations performed by \TEX\ are done entirely in terms of integers less than
    $2^{31}$ in magnitude; and divisions are done only when both dividend and divisor are
    nonnegative. Thus, the arithmetic specified in this program can be carried out in exactly the
    same way on a wide variety of computers, including some small ones. Why? Because the arithmetic
    calculations need to be spelled out precisely in order to guarantee that \TEX\ will produce
    identical output on different machines.

    If some quantities were rounded differently in different implementations, we would find that
    line breaks and even page breaks might occur in different places. Hence the arithmetic of \TEX\
    has been designed with care, and systems that claim to be implementations of \TEX82 should
    follow precisely the \TEX82\ calculations as they appear in the present program.

    Actually there are three places where \TEX\ uses |div| with a possibly negative numerator.
    These are harmless; see |div| in the index. Also if the user sets the |\time| or the |\year| to
    a negative value, some diagnostic information will involve negative|-|numerator division. The
    same remarks apply for |mod| as well as for |div|.

    The |half| routine, defined in the header file, calculates half of an integer, using an
    unambiguous convention with respect to signed odd numbers.

    The |round_decimals| function, defined in the header file, is used to create a scaled integer
    from a given decimal fraction $(.d_0d_1 \ldots d_{k-1})$, where |0 <= k <= 17|. The digit $d_i$
    is given in |dig[i]|, and the calculation produces a correctly rounded result.

    Keep in mind that in spite of these precautions results can be different over time. For
    instance, fonts and hyphenation patterns do evolve over, and actually did in the many decades
    that \TEX\ has been around. Also, delegating work to \LUA, which uses doubles, can have
    consequences.

*/

/*tex

    Physical sizes that a \TEX\ user specifies for portions of documents are represented internally
    as scaled points. Thus, if we define an |sp| (scaled point) as a unit equal to $2^{-16}$
    printer's points, every dimension inside of \TEX\ is an integer number of sp. There are exactly
    4,736,286.72 sp per inch. Users are not allowed to specify dimensions larger than $2^{30} - 1$
    sp, which is a distance of about 18.892 feet (5.7583 meters); two such quantities can be added
    without overflow on a 32-bit computer.

    The present implementation of \TEX\ does not check for overflow when dimensions are added or
    subtracted. This could be done by inserting a few dozen tests of the form |if x >= 010000000000|
    then |report_overflow|, but the chance of overflow is so remote that such tests do not seem
    worthwhile.

    \TEX\ needs to do only a few arithmetic operations on scaled quantities, other than addition and
    subtraction, and the following subroutines do most of the work. A single computation might use
    several subroutine calls, and it is desirable to avoid producing multiple error messages in case
    of arithmetic overflow; so the routines set the global variable |arith_error| to |true| instead
    of reporting errors directly to the user. Another global variable, |tex_remainder|, holds the
    remainder after a division.

    The first arithmetical subroutine we need computes $nx+y$, where |x| and~|y| are |scaled| and
    |n| is an integer. We will also use it to multiply integers.

*/

inline static scaled tex_aux_m_and_a(int n, scaled x, scaled y, scaled max_answer)
{
    if (n == 0) {
        return y;
    } else {
        if (n < 0) {
            x = -x;
            n = -n;
        }
        if (((x <= (max_answer - y) / n) && (-x <= (max_answer + y) / n))) {
            return n * x + y;
        } else {
            lmt_scanner_state.arithmic_error = 1;
            return 0;
        }
    }
}

scaled tex_multiply_and_add  (int n, scaled x, scaled y, scaled max_answer) { return tex_aux_m_and_a(n, x, y,   max_answer); }
scaled tex_nx_plus_y         (int n, scaled x, scaled y)                    { return tex_aux_m_and_a(n, x, y,  07777777777); }
scaled tex_multiply_integers (int n, scaled x)                              { return tex_aux_m_and_a(n, x, 0, 017777777777); }

/*tex We also need to divide scaled dimensions by integers. */

/*
scaled tex_x_over_n_r(scaled x, int n, int *remainder)
{
    if (n == 0) {
        lmt_scanner_state.arithmic_error = 1;
        if (remainder) {
            *remainder = x;
        }
        return 0;
    } else {
        int negative = 0;
        if (n < 0) {
            x = -x;
            n = -n;
            negative = 1;
        }
        if (x >= 0) {
            int r = x % n;
            if (remainder) {
                if (negative) {
                    r = -r;
                }
                *remainder = r;
            }
            return (x / n);
        } else {
            int r = -((-x) % n);
            if (remainder) {
                if (negative) {
                    r = -r;
                }
                *remainder = r;
            }
            return -((-x) / n);
        }
    }
}
*/

scaled tex_x_over_n_r(scaled x, int n, int *remainder)
{
    /*tex Should |tex_remainder| be negated? */
    if (n == 0) {
        lmt_scanner_state.arithmic_error = 1;
        *remainder = x;
        return 0;
    } else {
        *remainder = x % n;
        return x/n;
    }
}

/*
scaled tex_x_over_n(scaled x, int n)
{
     if (n == 0) {
        lmt_scanner_state.arithmic_error = 1;
        return 0;
    } else {
        if (n < 0) {
            x = -x;
            n = -n;
        }
        if (x >= 0) {
            return (x / n);
        } else {
            return -((-x) / n);
        }
    }
}
*/

scaled tex_x_over_n(scaled x, int n)
{
     if (n == 0) {
        lmt_scanner_state.arithmic_error = 1;
        return 0;
    } else {
        return x/n;
    }
}

/*tex

    Then comes the multiplication of a scaled number by a fraction |n/d|, where |n| and |d| are
    nonnegative integers |<= 2^16| and |d| is positive. It would be too dangerous to multiply by~|n|
    and then divide by~|d|, in separate operations, since overflow might well occur; and it would
    be too inaccurate to divide by |d| and then multiply by |n|. Hence this subroutine simulates
    1.5-precision arithmetic.

*/

/*
scaled tex_xn_over_d_r(scaled x, int n, int d, int *remainder)
{
    if (x == 0) {
        if (remainder) {
            *remainder = 0;
        }
        return 0;
    } else {
        int positive = 1;
        unsigned int t, u, v, xx, dd;
        if (x < 0) {
            x = -x;
            positive = 0;
        }
        xx = (unsigned int) x;
        dd = (unsigned int) d;
        t = ((xx % 0100000) * (unsigned int) n);
        u = ((xx / 0100000) * (unsigned int) n + (t / 0100000));
        v = (u % dd) * 0100000 + (t % 0100000);
        if (u / dd >= 0100000) {
            lmt_scanner_state.arithmic_error = 1;
        } else {
            u = 0100000 * (u / dd) + (v / dd);
        }
        if (positive) {
            if (remainder) {
                *remainder = (int) (v % dd);
            }
            return (scaled) u;
        } else {
            if (remainder) {
                *remainder = - (int) (v % dd);
            }
            return - (scaled) u;
        }
    }
}
*/

scaled tex_xn_over_d_r(scaled x, int n, int d, int *remainder)
{
    if (x == 0) {
        *remainder = 0;
        return 0;
    } else {
        long long v = (long long) x * (long long) n;
        *remainder = (scaled) (v % d);
        return (scaled) (v / d); 
    }
}

/*
scaled tex_xn_over_d(scaled x, int n, int d)
{
    if (x == 0) {
        return 0;
    } else {
        int positive = 1;
        unsigned int t, u, v, xx, dd;
        if (x < 0) {
            x = -x;
            positive = 0;
        }
        xx = (unsigned int) x;
        dd = (unsigned int) d;
        t = ((xx % 0100000) * (unsigned int) n);
        u = ((xx / 0100000) * (unsigned int) n + (t / 0100000));
        v = (u % dd) * 0100000 + (t % 0100000);
        if (u / dd >= 0100000) {
            lmt_scanner_state.arithmic_error = 1;
        } else {
            u = 0100000 * (u / dd) + (v / dd);
        }
        if (positive) {
            return (scaled) u;
        } else {
            return - (scaled) u;
        }
    }
}
*/

scaled tex_xn_over_d(scaled x, int n, int d)
{
    if (x == 0) {
        return 0;
    } else {
        long long v = (long long) x * (long long) n;
        return (scaled) (v / d); 
    }
}

/*tex

    When \TEX\ packages a list into a box, it needs to calculate the proportionality ratio by which
    the glue inside the box should stretch or shrink. This calculation does not affect \TEX's
    decision making, so the precise details of rounding, etc., in the glue calculation are not of
    critical importance for the consistency of results on different computers.

    We shall use the type |glue_ratio| for such proportionality ratios. A glue ratio should take the
    same amount of memory as an |integer| (usually 32 bits) if it is to blend smoothly with \TEX's
    other data structures. Thus |glue_ratio| should be equivalent to |short_real| in some
    implementations of \PASCAL. Alternatively, it is possible to deal with glue ratios using nothing
    but fixed-point arithmetic; see {\em TUGboat \bf3},1 (March 1982), 10--27. (But the routines
    cited there must be modified to allow negative glue ratios.)

*/

/*
scaled tex_round_xn_over_d(scaled x, int n, unsigned int d)
{
    if (x == 0) {
        return 0;
    } else if (n == d) {
        return x;
    } else {
        int positive = 1;
        unsigned t, u, v;
        if (x < 0) {
            positive = ! positive;
            x = -x;
        }
        if (n < 0) {
            positive = ! positive;
            n = -n;
        }
        t = (unsigned) ((x % 0100000) * n);
        u = (unsigned) (((unsigned) (x) / 0100000) * (unsigned) n + (t / 0100000));
        v = (u % d) * 0100000 + (t % 0100000);
        if (u / d >= 0100000) {
            scanner_state.arithmic_error = 1;
        } else {
            u = 0100000 * (u / d) + (v / d);
        }
        v = v % d;
        if (2 * v >= d) {
            u++;
        }
        return positive ? (scaled) u : - (scaled) u;
    }
}
*/

/*
scaled tex_round_xn_over_d(scaled x, int n, unsigned int d)
{
    if (x == 0|| n == d) {
        return x;
    } else {
        double v = (1.0 / d) * n * x;
        return (v < 0.0) ? (int) (v - 0.5) : (int) (v + 0.5);
    }
}
*/

scaled tex_round_xn_over_d(scaled x, int n, unsigned int d)
{
    if (x == 0 || (unsigned int) n == d) {
        return x;
    } else {
        return scaledround((1.0 / d) * n * x);
    }
}

/*tex

    The return value is a decimal number with the point |dd| places from the back, |scaled_out| is
    the number of scaled points corresponding to that.

*/

/* not used:

scaled tex_divide_scaled(scaled s, scaled m, int dd)
{
    if (s == 0) {
        return 0;
    } else {
        scaled q, r;
        int sign = 1;
        if (s < 0) {
            sign = -sign;
            s = -s;
        }
        if (m < 0) {
            sign = -sign;
            m = -m;
        }
        if (m == 0) {
            normal_error("arithmetic", "divided by zero");
        } else if (m >= (max_integer / 10)) {
            normal_error("arithmetic", "number too big");
        }
        q = s / m;
        r = s % m;
        for (int i = 1; i <= (int) dd; i++) {
            q = 10 * q + (10 * r) / m;
            r =          (10 * r) % m;
        }
        if (2 * r >= m) {
            q++; // rounding
        }
        return sign * q;
    }
}
*/

/*
scaled divide_scaled_n(double sd, double md, double n)
{
    scaled di = 0;
    double dd = sd / md * n;
    if (dd > 0.0) {
        di =  ifloor(  dd  + 0.5);
    } else if (dd < 0.0) {
        di = -ifloor((-dd) + 0.5);
    }
    return di;
}
*/

scaled tex_divide_scaled_n(double sd, double md, double n)
{
    return scaledround(sd / md * n);
}

/*
scaled tex_ext_xn_over_d(scaled x, scaled n, scaled d)
{
    double r = (((double) x) * ((double) n)) / ((double) d);
    if (r > DBL_EPSILON) {
        r += 0.5;
    } else {
        r -= 0.5;
    }
    if (r >= (double) max_integer || r <= -(double) max_integer) {
        tex_normal_warning("internal", "arithmetic number too big");
    }
    return (scaled) r;
}
*/

scaled tex_ext_xn_over_d(scaled x, scaled n, scaled d)
{
    double r = (((double) x) * ((double) n)) / ((double) d);
    if (r >= (double) max_integer || r <= -(double) max_integer) {
        /* can we really run into this? */
        tex_normal_warning("internal", "arithmetic number too big");
    }
    return scaledround(r);
}