diff options
Diffstat (limited to 'source/luametatex/source/tex/texarithmetic.c')
-rw-r--r-- | source/luametatex/source/tex/texarithmetic.c | 433 |
1 files changed, 433 insertions, 0 deletions
diff --git a/source/luametatex/source/tex/texarithmetic.c b/source/luametatex/source/tex/texarithmetic.c new file mode 100644 index 000000000..d9cf9859d --- /dev/null +++ b/source/luametatex/source/tex/texarithmetic.c @@ -0,0 +1,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); +} |