/* 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 % This needs to be adapted to new variable names! It's the old documentation. 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 |arithmetic_error| field in |lmt_scanner_state| to |1| instead of reporting errors directly to the user. An optional out-parameter 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. Helpers like this evolve as one sometimes reads about how compilers and processors deal with this (it's interesting to see progress when one started with microprocessors as kid). When searching a bit on compiler optimizations llm's recognize these integer based \TEX\ paradigms but also see that we do things differently already so we mostly stick to what we have now. */ static inline scaled tex_aux_m_and_a(int n, scaled x, scaled y, scaled max_answer) { if (n == 0) { return y; } // 64-bit widening: 0 division cycles, perfectly safe against overflow long long res = (long long) n * (long long) x + (long long) y; if (res >= -((long long) max_answer) && res <= (long long) max_answer) { return (scaled)res; } else { lmt_scanner_state.arithmetic_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, 0x3FFFFFFF); } // 07777777777 scaled tex_multiply_integers (int n, scaled x) { return tex_aux_m_and_a(n, x, 0, 0x7FFFFFFF); } // 017777777777 scaled tex_x_over_n_r(scaled x, int n, int *remainder) { /*tex The optional |remainder| has the sign of the dividend. */ if (n == 0) { lmt_scanner_state.arithmetic_error = 1; if (remainder) { *remainder = x; } return 0; } else { if (remainder) { *remainder = x % n; } return x / n; } } scaled tex_x_over_n(scaled x, int n) { if (n == 0) { lmt_scanner_state.arithmetic_error = 1; return 0; } else { return x / n; } } scaled tex_x_over_n_unity(scaled x) { return x / unity; } scaled tex_x_over_n_factor(scaled x) { return x / scaling_factor; } /*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 (d == 0) { lmt_scanner_state.arithmetic_error = 1; if (remainder) { *remainder = x; } return 0; } if (x == 0) { if (remainder) { *remainder = 0; } return 0; } else { long long v = (long long) x * (long long) n; long long q = v / d; long long r = v % d; if (q < (long long) min_dimension || q > (long long) max_dimension) { lmt_scanner_state.arithmetic_error = 1; if (remainder) { *remainder = (scaled) r; } return 0; } if (remainder) { *remainder = (scaled) r; } return (scaled) q; } } scaled tex_xn_over_d(scaled x, int n, int d) { if (d == 0) { lmt_scanner_state.arithmetic_error = 1; return 0; } if (x == 0) { return 0; } long long q = ((long long) x * (long long) n) / d; if (q < (long long) min_dimension || q > (long long) max_dimension) { lmt_scanner_state.arithmetic_error = 1; return 0; } return (scaled) q; } /*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.) */ /* this (double) constant integer ... will the compiler optimize that? */ static inline scaled tex_aux_checked_scaledround(double value, scaled lo, scaled hi) { if (value == 0) { return 0; } else if (! isfinite(value)) { lmt_scanner_state.arithmetic_error = 1; return 0; } else { double rounded = value >= 0.0 ? value + 0.5 : value - 0.5; if (rounded < (double) lo || rounded > (double) hi) { lmt_scanner_state.arithmetic_error = 1; return 0; } return (scaled) rounded; } } scaled tex_round_xn_over_d(scaled x, int n, unsigned int d) { if (d == 0) { lmt_scanner_state.arithmetic_error = 1; return 0; } else if (x == 0 || (n >= 0 && (unsigned int) n == d && x >= min_dimension && x <= max_dimension)) { return x; } else { return tex_aux_checked_scaledround((1.0 / d) * n * x, min_dimension, max_dimension); } } /*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. */ scaled tex_divide_scaled_n(double sd, double md, double n) { if (md == 0.0) { lmt_scanner_state.arithmetic_error = 1; return 0; } else { return tex_aux_checked_scaledround(sd / md * n, min_integer, max_integer); } } scaled tex_ext_xn_over_d(scaled x, scaled n, scaled d) { if (d == 0) { lmt_scanner_state.arithmetic_error = 1; return 0; } else { double r = (((double) x) * ((double) n)) / ((double) d); if (r > (double) max_integer || r < (double) min_integer) { tex_normal_warning("internal", "arithmetic number too big"); } return tex_aux_checked_scaledround(r, min_integer, max_integer); } } scaled tex_nx_plus_y_posit(halfword p, scaled x, scaled y) { if (p == 0) { return y; } else { double d = tex_posit_to_double(p) * x + y; long long r = llround(d); if (r < -0x3FFFFFFF || r > 0x3FFFFFFF) { lmt_scanner_state.arithmetic_error = 1; return 0; } else { return (halfword) r; } } }