texarithmetic.c /size: 12 Kb    last modification: 2024-01-16 10:22
1/*
2    See license.txt in the root of this project.
3*/
4
5# include "luametatex.h"
6
7/*tex
8
9    The principal computations performed by \TEX\ are done entirely in terms of integers less than
10    $2^{31}$ in magnitude; and divisions are done only when both dividend and divisor are
11    nonnegative. Thus, the arithmetic specified in this program can be carried out in exactly the
12    same way on a wide variety of computers, including some small ones. Why? Because the arithmetic
13    calculations need to be spelled out precisely in order to guarantee that \TEX\ will produce
14    identical output on different machines.
15
16    If some quantities were rounded differently in different implementations, we would find that
17    line breaks and even page breaks might occur in different places. Hence the arithmetic of \TEX\
18    has been designed with care, and systems that claim to be implementations of \TEX82 should
19    follow precisely the \TEX82\ calculations as they appear in the present program.
20
21    Actually there are three places where \TEX\ uses |div| with a possibly negative numerator.
22    These are harmless; see |div| in the index. Also if the user sets the |\time| or the |\year| to
23    a negative value, some diagnostic information will involve negative|-|numerator division. The
24    same remarks apply for |mod| as well as for |div|.
25
26    The |half| routine, defined in the header file, calculates half of an integer, using an
27    unambiguous convention with respect to signed odd numbers.
28
29    The |round_decimals| function, defined in the header file, is used to create a scaled integer
30    from a given decimal fraction $(.d_0d_1 \ldots d_{k-1})$, where |0 <= k <= 17|. The digit $d_i$
31    is given in |dig[i]|, and the calculation produces a correctly rounded result.
32
33    Keep in mind that in spite of these precautions results can be different over time. For
34    instance, fonts and hyphenation patterns do evolve over, and actually did in the many decades
35    that \TEX\ has been around. Also, delegating work to \LUA, which uses doubles, can have
36    consequences.
37
38*/
39
40/*tex
41
42    Physical sizes that a \TEX\ user specifies for portions of documents are represented internally
43    as scaled points. Thus, if we define an |sp| (scaled point) as a unit equal to $2^{-16}$
44    printer's points, every dimension inside of \TEX\ is an integer number of sp. There are exactly
45    4,736,286.72 sp per inch. Users are not allowed to specify dimensions larger than $2^{30} - 1$
46    sp, which is a distance of about 18.892 feet (5.7583 meters); two such quantities can be added
47    without overflow on a 32-bit computer.
48
49    The present implementation of \TEX\ does not check for overflow when dimensions are added or
50    subtracted. This could be done by inserting a few dozen tests of the form |if x >= 010000000000|
51    then |report_overflow|, but the chance of overflow is so remote that such tests do not seem
52    worthwhile.
53
54    \TEX\ needs to do only a few arithmetic operations on scaled quantities, other than addition and
55    subtraction, and the following subroutines do most of the work. A single computation might use
56    several subroutine calls, and it is desirable to avoid producing multiple error messages in case
57    of arithmetic overflow; so the routines set the global variable |arith_error| to |true| instead
58    of reporting errors directly to the user. Another global variable, |tex_remainder|, holds the
59    remainder after a division.
60
61    The first arithmetical subroutine we need computes $nx+y$, where |x| and~|y| are |scaled| and
62    |n| is an integer. We will also use it to multiply integers.
63
64*/
65
66inline static scaled tex_aux_m_and_a(int n, scaled x, scaled y, scaled max_answer)
67{
68    if (n == 0) {
69        return y;
70    } else {
71        if (n < 0) {
72            x = -x;
73            n = -n;
74        }
75        if (((x <= (max_answer - y) / n) && (-x <= (max_answer + y) / n))) {
76            return n * x + y;
77        } else {
78            lmt_scanner_state.arithmic_error = 1;
79            return 0;
80        }
81    }
82}
83
84scaled tex_multiply_and_add  (int n, scaled x, scaled y, scaled max_answer) { return tex_aux_m_and_a(n, x, y, max_answer); }
85scaled tex_nx_plus_y         (int n, scaled x, scaled y)                    { return tex_aux_m_and_a(n, x, y, 0x3FFFFFFF); } //  07777777777
86scaled tex_multiply_integers (int n, scaled x)                              { return tex_aux_m_and_a(n, x, 0, 0x7FFFFFFF); } // 017777777777
87
88/*tex We also need to divide scaled dimensions by integers. */
89
90/*
91scaled tex_x_over_n_r(scaled x, int n, int *remainder)
92{
93    if (n == 0) {
94        lmt_scanner_state.arithmic_error = 1;
95        if (remainder) {
96            *remainder = x;
97        }
98        return 0;
99    } else {
100        int negative = 0;
101        if (n < 0) {
102            x = -x;
103            n = -n;
104            negative = 1;
105        }
106        if (x >= 0) {
107            int r = x % n;
108            if (remainder) {
109                if (negative) {
110                    r = -r;
111                }
112                *remainder = r;
113            }
114            return (x / n);
115        } else {
116            int r = -((-x) % n);
117            if (remainder) {
118                if (negative) {
119                    r = -r;
120                }
121                *remainder = r;
122            }
123            return -((-x) / n);
124        }
125    }
126}
127*/
128
129scaled tex_x_over_n_r(scaled x, int n, int *remainder)
130{
131    /*tex Should |tex_remainder| be negated? */
132    if (n == 0) {
133        lmt_scanner_state.arithmic_error = 1;
134        *remainder = x;
135        return 0;
136    } else {
137        *remainder = x % n;
138        return x/n;
139    }
140}
141
142/*
143scaled tex_x_over_n(scaled x, int n)
144{
145     if (n == 0) {
146        lmt_scanner_state.arithmic_error = 1;
147        return 0;
148    } else {
149        if (n < 0) {
150            x = -x;
151            n = -n;
152        }
153        if (x >= 0) {
154            return (x / n);
155        } else {
156            return -((-x) / n);
157        }
158    }
159}
160*/
161
162scaled tex_x_over_n(scaled x, int n)
163{
164     if (n == 0) {
165        lmt_scanner_state.arithmic_error = 1;
166        return 0;
167    } else {
168        return x/n;
169    }
170}
171
172/*tex
173
174    Then comes the multiplication of a scaled number by a fraction |n/d|, where |n| and |d| are
175    nonnegative integers |<= 2^16| and |d| is positive. It would be too dangerous to multiply by~|n|
176    and then divide by~|d|, in separate operations, since overflow might well occur; and it would
177    be too inaccurate to divide by |d| and then multiply by |n|. Hence this subroutine simulates
178    1.5-precision arithmetic.
179
180*/
181
182/*
183scaled tex_xn_over_d_r(scaled x, int n, int d, int *remainder)
184{
185    if (x == 0) {
186        if (remainder) {
187            *remainder = 0;
188        }
189        return 0;
190    } else {
191        int positive = 1;
192        unsigned int t, u, v, xx, dd;
193        if (x < 0) {
194            x = -x;
195            positive = 0;
196        }
197        xx = (unsigned int) x;
198        dd = (unsigned int) d;
199        t = ((xx % 0x8000) * (unsigned int) n);
200        u = ((xx / 0x8000) * (unsigned int) n + (t / 0x8000));
201        v = (u % dd) * 0x8000 + (t % 0x8000);
202        if (u / dd >= 0x8000) {
203            lmt_scanner_state.arithmic_error = 1;
204        } else {
205            u = 0x8000 * (u / dd) + (v / dd);
206        }
207        if (positive) {
208            if (remainder) {
209                *remainder = (int) (v % dd);
210            }
211            return (scaled) u;
212        } else {
213            if (remainder) {
214                *remainder = - (int) (v % dd);
215            }
216            return - (scaled) u;
217        }
218    }
219}
220*/
221
222scaled tex_xn_over_d_r(scaled x, int n, int d, int *remainder)
223{
224    if (x == 0) {
225        *remainder = 0;
226        return 0;
227    } else {
228        long long v = (long long) x * (long long) n;
229        *remainder = (scaled) (v % d);
230        return (scaled) (v / d); 
231    }
232}
233
234/*
235scaled tex_xn_over_d(scaled x, int n, int d)
236{
237    if (x == 0) {
238        return 0;
239    } else {
240        int positive = 1;
241        unsigned int t, u, v, xx, dd;
242        if (x < 0) {
243            x = -x;
244            positive = 0;
245        }
246        xx = (unsigned int) x;
247        dd = (unsigned int) d;
248        t = ((xx % 0x8000) * (unsigned int) n);
249        u = ((xx / 0x8000) * (unsigned int) n + (t / 0x8000));
250        v = (u % dd) * 0x8000 + (t % 0x8000);
251        if (u / dd >= 0x8000) {
252            lmt_scanner_state.arithmic_error = 1;
253        } else {
254            u = 0x8000 * (u / dd) + (v / dd);
255        }
256        if (positive) {
257            return (scaled) u;
258        } else {
259            return - (scaled) u;
260        }
261    }
262}
263*/
264
265scaled tex_xn_over_d(scaled x, int n, int d)
266{
267    if (x == 0) {
268        return 0;
269    } else {
270        long long v = (long long) x * (long long) n;
271        return (scaled) (v / d); 
272    }
273}
274
275/*tex
276
277    When \TEX\ packages a list into a box, it needs to calculate the proportionality ratio by which
278    the glue inside the box should stretch or shrink. This calculation does not affect \TEX's
279    decision making, so the precise details of rounding, etc., in the glue calculation are not of
280    critical importance for the consistency of results on different computers.
281
282    We shall use the type |glue_ratio| for such proportionality ratios. A glue ratio should take the
283    same amount of memory as an |integer| (usually 32 bits) if it is to blend smoothly with \TEX's
284    other data structures. Thus |glue_ratio| should be equivalent to |short_real| in some
285    implementations of \PASCAL. Alternatively, it is possible to deal with glue ratios using nothing
286    but fixed-point arithmetic; see {\em TUGboat \bf3},1 (March 1982), 10--27. (But the routines
287    cited there must be modified to allow negative glue ratios.)
288
289*/
290
291/*
292scaled tex_round_xn_over_d(scaled x, int n, unsigned int d)
293{
294    if (x == 0) {
295        return 0;
296    } else if (n == d) {
297        return x;
298    } else {
299        int positive = 1;
300        unsigned t, u, v;
301        if (x < 0) {
302            positive = ! positive;
303            x = -x;
304        }
305        if (n < 0) {
306            positive = ! positive;
307            n = -n;
308        }
309        t = (unsigned) ((x % 0x8000) * n);
310        u = (unsigned) (((unsigned) (x) / 0x8000) * (unsigned) n + (t / 0x8000));
311        v = (u % d) * 0x8000 + (t % 0x8000);
312        if (u / d >= 0x8000) {
313            lmt_scanner_state.arithmic_error = 1;
314        } else {
315            u = 0x8000 * (u / d) + (v / d);
316        }
317        v = v % d;
318        if (2 * v >= d) {
319            u++;
320        }
321        return positive ? (scaled) u : - (scaled) u;
322    }
323}
324*/
325
326/*
327scaled tex_round_xn_over_d(scaled x, int n, unsigned int d)
328{
329    if (x == 0|| n == d) {
330        return x;
331    } else {
332        double v = (1.0 / d) * n * x;
333        return (v < 0.0) ? (int) (v - 0.5) : (int) (v + 0.5);
334    }
335}
336*/
337
338scaled tex_round_xn_over_d(scaled x, int n, unsigned int d)
339{
340    if (x == 0 || (unsigned int) n == d) {
341        return x;
342    } else {
343        return scaledround((1.0 / d) * n * x);
344    }
345}
346
347/*tex
348
349    The return value is a decimal number with the point |dd| places from the back, |scaled_out| is
350    the number of scaled points corresponding to that.
351
352*/
353
354/* not used:
355
356scaled tex_divide_scaled(scaled s, scaled m, int dd)
357{
358    if (s == 0) {
359        return 0;
360    } else {
361        scaled q, r;
362        int sign = 1;
363        if (s < 0) {
364            sign = -sign;
365            s = -s;
366        }
367        if (m < 0) {
368            sign = -sign;
369            m = -m;
370        }
371        if (m == 0) {
372            normal_error("arithmetic", "divided by zero");
373        } else if (m >= (max_integer / 10)) {
374            normal_error("arithmetic", "number too big");
375        }
376        q = s / m;
377        r = s % m;
378        for (int i = 1; i <= (int) dd; i++) {
379            q = 10 * q + (10 * r) / m;
380            r =          (10 * r) % m;
381        }
382        if (2 * r >= m) {
383            q++; // rounding
384        }
385        return sign * q;
386    }
387}
388*/
389
390/*
391scaled divide_scaled_n(double sd, double md, double n)
392{
393    scaled di = 0;
394    double dd = sd / md * n;
395    if (dd > 0.0) {
396        di =  ifloor(  dd  + 0.5);
397    } else if (dd < 0.0) {
398        di = -ifloor((-dd) + 0.5);
399    }
400    return di;
401}
402*/
403
404scaled tex_divide_scaled_n(double sd, double md, double n)
405{
406    return scaledround(sd / md * n);
407}
408
409/*
410scaled tex_ext_xn_over_d(scaled x, scaled n, scaled d)
411{
412    double r = (((double) x) * ((double) n)) / ((double) d);
413    if (r > DBL_EPSILON) {
414        r += 0.5;
415    } else {
416        r -= 0.5;
417    }
418    if (r >= (double) max_integer || r <= -(double) max_integer) {
419        tex_normal_warning("internal", "arithmetic number too big");
420    }
421    return (scaled) r;
422}
423*/
424
425scaled tex_ext_xn_over_d(scaled x, scaled n, scaled d)
426{
427    double r = (((double) x) * ((double) n)) / ((double) d);
428    if (r >= (double) max_integer || r <= -(double) max_integer) {
429        /* can we really run into this? */
430        tex_normal_warning("internal", "arithmetic number too big");
431    }
432    return scaledround(r);
433}
434