1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2004
3114402Sru   Free Software Foundation, Inc.
4114402Sru     Written by James Clark (jjc@jclark.com)
5114402Sru
6114402SruThis file is part of groff.
7114402Sru
8114402Srugroff is free software; you can redistribute it and/or modify it under
9114402Sruthe terms of the GNU General Public License as published by the Free
10114402SruSoftware Foundation; either version 2, or (at your option) any later
11114402Sruversion.
12114402Sru
13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
15114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16114402Srufor more details.
17114402Sru
18114402SruYou should have received a copy of the GNU General Public License along
19114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21114402Sru
22114402Sru
23114402Sru#include "troff.h"
24114402Sru#include "hvunits.h"
25151497Sru#include "stringclass.h"
26151497Sru#include "mtsm.h"
27114402Sru#include "env.h"
28114402Sru#include "token.h"
29114402Sru#include "div.h"
30114402Sru
31114402Sruvunits V0;
32114402Sruhunits H0;
33114402Sru
34114402Sruint hresolution = 1;
35114402Sruint vresolution = 1;
36114402Sruint units_per_inch;
37114402Sruint sizescale;
38114402Sru
39114402Srustatic int parse_expr(units *v, int scale_indicator,
40114402Sru		      int parenthesised, int rigid = 0);
41114402Srustatic int start_number();
42114402Sru
43114402Sruint get_vunits(vunits *res, unsigned char si)
44114402Sru{
45114402Sru  if (!start_number())
46114402Sru    return 0;
47114402Sru  units x;
48114402Sru  if (parse_expr(&x, si, 0)) {
49114402Sru    *res = vunits(x);
50114402Sru    return 1;
51114402Sru  }
52114402Sru  else
53114402Sru    return 0;
54114402Sru}
55114402Sru
56114402Sruint get_hunits(hunits *res, unsigned char si)
57114402Sru{
58114402Sru  if (!start_number())
59114402Sru    return 0;
60114402Sru  units x;
61114402Sru  if (parse_expr(&x, si, 0)) {
62114402Sru    *res = hunits(x);
63114402Sru    return 1;
64114402Sru  }
65114402Sru  else
66114402Sru    return 0;
67114402Sru}
68114402Sru
69114402Sru// for \B
70114402Sru
71114402Sruint get_number_rigidly(units *res, unsigned char si)
72114402Sru{
73114402Sru  if (!start_number())
74114402Sru    return 0;
75114402Sru  units x;
76114402Sru  if (parse_expr(&x, si, 0, 1)) {
77114402Sru    *res = x;
78114402Sru    return 1;
79114402Sru  }
80114402Sru  else
81114402Sru    return 0;
82114402Sru}
83114402Sru
84114402Sruint get_number(units *res, unsigned char si)
85114402Sru{
86114402Sru  if (!start_number())
87114402Sru    return 0;
88114402Sru  units x;
89114402Sru  if (parse_expr(&x, si, 0)) {
90114402Sru    *res = x;
91114402Sru    return 1;
92114402Sru  }
93114402Sru  else
94114402Sru    return 0;
95114402Sru}
96114402Sru
97114402Sruint get_integer(int *res)
98114402Sru{
99114402Sru  if (!start_number())
100114402Sru    return 0;
101114402Sru  units x;
102114402Sru  if (parse_expr(&x, 0, 0)) {
103114402Sru    *res = x;
104114402Sru    return 1;
105114402Sru  }
106114402Sru  else
107114402Sru    return 0;
108114402Sru}
109114402Sru
110114402Sruenum incr_number_result { BAD, ABSOLUTE, INCREMENT, DECREMENT };
111114402Sru
112114402Srustatic incr_number_result get_incr_number(units *res, unsigned char);
113114402Sru
114114402Sruint get_vunits(vunits *res, unsigned char si, vunits prev_value)
115114402Sru{
116114402Sru  units v;
117114402Sru  switch (get_incr_number(&v, si)) {
118114402Sru  case BAD:
119114402Sru    return 0;
120114402Sru  case ABSOLUTE:
121114402Sru    *res = v;
122114402Sru    break;
123114402Sru  case INCREMENT:
124114402Sru    *res = prev_value + v;
125114402Sru    break;
126114402Sru  case DECREMENT:
127114402Sru    *res = prev_value - v;
128114402Sru    break;
129114402Sru  default:
130114402Sru    assert(0);
131114402Sru  }
132114402Sru  return 1;
133114402Sru}
134114402Sru
135114402Sruint get_hunits(hunits *res, unsigned char si, hunits prev_value)
136114402Sru{
137114402Sru  units v;
138114402Sru  switch (get_incr_number(&v, si)) {
139114402Sru  case BAD:
140114402Sru    return 0;
141114402Sru  case ABSOLUTE:
142114402Sru    *res = v;
143114402Sru    break;
144114402Sru  case INCREMENT:
145114402Sru    *res = prev_value + v;
146114402Sru    break;
147114402Sru  case DECREMENT:
148114402Sru    *res = prev_value - v;
149114402Sru    break;
150114402Sru  default:
151114402Sru    assert(0);
152114402Sru  }
153114402Sru  return 1;
154114402Sru}
155114402Sru
156114402Sruint get_number(units *res, unsigned char si, units prev_value)
157114402Sru{
158114402Sru  units v;
159114402Sru  switch (get_incr_number(&v, si)) {
160114402Sru  case BAD:
161114402Sru    return 0;
162114402Sru  case ABSOLUTE:
163114402Sru    *res = v;
164114402Sru    break;
165114402Sru  case INCREMENT:
166114402Sru    *res = prev_value + v;
167114402Sru    break;
168114402Sru  case DECREMENT:
169114402Sru    *res = prev_value - v;
170114402Sru    break;
171114402Sru  default:
172114402Sru    assert(0);
173114402Sru  }
174114402Sru  return 1;
175114402Sru}
176114402Sru
177114402Sruint get_integer(int *res, int prev_value)
178114402Sru{
179114402Sru  units v;
180114402Sru  switch (get_incr_number(&v, 0)) {
181114402Sru  case BAD:
182114402Sru    return 0;
183114402Sru  case ABSOLUTE:
184114402Sru    *res = v;
185114402Sru    break;
186114402Sru  case INCREMENT:
187114402Sru    *res = prev_value + int(v);
188114402Sru    break;
189114402Sru  case DECREMENT:
190114402Sru    *res = prev_value - int(v);
191114402Sru    break;
192114402Sru  default:
193114402Sru    assert(0);
194114402Sru  }
195114402Sru  return 1;
196114402Sru}
197114402Sru
198114402Sru
199114402Srustatic incr_number_result get_incr_number(units *res, unsigned char si)
200114402Sru{
201114402Sru  if (!start_number())
202114402Sru    return BAD;
203114402Sru  incr_number_result result = ABSOLUTE;
204114402Sru  if (tok.ch() == '+') {
205114402Sru    tok.next();
206114402Sru    result = INCREMENT;
207114402Sru  }
208114402Sru  else if (tok.ch() == '-') {
209114402Sru    tok.next();
210114402Sru    result = DECREMENT;
211114402Sru  }
212114402Sru  if (parse_expr(res, si, 0))
213114402Sru    return result;
214114402Sru  else
215114402Sru    return BAD;
216114402Sru}
217114402Sru
218114402Srustatic int start_number()
219114402Sru{
220114402Sru  while (tok.space())
221114402Sru    tok.next();
222114402Sru  if (tok.newline()) {
223114402Sru    warning(WARN_MISSING, "missing number");
224114402Sru    return 0;
225114402Sru  }
226114402Sru  if (tok.tab()) {
227114402Sru    warning(WARN_TAB, "tab character where number expected");
228114402Sru    return 0;
229114402Sru  }
230114402Sru  if (tok.right_brace()) {
231114402Sru    warning(WARN_RIGHT_BRACE, "`\\}' where number expected");
232114402Sru    return 0;
233114402Sru  }
234114402Sru  return 1;
235114402Sru}
236114402Sru
237114402Sruenum { OP_LEQ = 'L', OP_GEQ = 'G', OP_MAX = 'X', OP_MIN = 'N' };
238114402Sru
239114402Sru#define SCALE_INDICATOR_CHARS "icfPmnpuvMsz"
240114402Sru
241114402Srustatic int parse_term(units *v, int scale_indicator,
242114402Sru		      int parenthesised, int rigid);
243114402Sru
244114402Srustatic int parse_expr(units *v, int scale_indicator,
245114402Sru		      int parenthesised, int rigid)
246114402Sru{
247114402Sru  int result = parse_term(v, scale_indicator, parenthesised, rigid);
248114402Sru  while (result) {
249114402Sru    if (parenthesised)
250114402Sru      tok.skip();
251114402Sru    int op = tok.ch();
252114402Sru    switch (op) {
253114402Sru    case '+':
254114402Sru    case '-':
255114402Sru    case '/':
256114402Sru    case '*':
257114402Sru    case '%':
258114402Sru    case ':':
259114402Sru    case '&':
260114402Sru      tok.next();
261114402Sru      break;
262114402Sru    case '>':
263114402Sru      tok.next();
264114402Sru      if (tok.ch() == '=') {
265114402Sru	tok.next();
266114402Sru	op = OP_GEQ;
267114402Sru      }
268114402Sru      else if (tok.ch() == '?') {
269114402Sru	tok.next();
270114402Sru	op = OP_MAX;
271114402Sru      }
272114402Sru      break;
273114402Sru    case '<':
274114402Sru      tok.next();
275114402Sru      if (tok.ch() == '=') {
276114402Sru	tok.next();
277114402Sru	op = OP_LEQ;
278114402Sru      }
279114402Sru      else if (tok.ch() == '?') {
280114402Sru	tok.next();
281114402Sru	op = OP_MIN;
282114402Sru      }
283114402Sru      break;
284114402Sru    case '=':
285114402Sru      tok.next();
286114402Sru      if (tok.ch() == '=')
287114402Sru	tok.next();
288114402Sru      break;
289114402Sru    default:
290114402Sru      return result;
291114402Sru    }
292114402Sru    units v2;
293114402Sru    if (!parse_term(&v2, scale_indicator, parenthesised, rigid))
294114402Sru      return 0;
295114402Sru    int overflow = 0;
296114402Sru    switch (op) {
297114402Sru    case '<':
298114402Sru      *v = *v < v2;
299114402Sru      break;
300114402Sru    case '>':
301114402Sru      *v = *v > v2;
302114402Sru      break;
303114402Sru    case OP_LEQ:
304114402Sru      *v = *v <= v2;
305114402Sru      break;
306114402Sru    case OP_GEQ:
307114402Sru      *v = *v >= v2;
308114402Sru      break;
309114402Sru    case OP_MIN:
310114402Sru      if (*v > v2)
311114402Sru	*v = v2;
312114402Sru      break;
313114402Sru    case OP_MAX:
314114402Sru      if (*v < v2)
315114402Sru	*v = v2;
316114402Sru      break;
317114402Sru    case '=':
318114402Sru      *v = *v == v2;
319114402Sru      break;
320114402Sru    case '&':
321114402Sru      *v = *v > 0 && v2 > 0;
322114402Sru      break;
323114402Sru    case ':':
324114402Sru      *v = *v > 0 || v2 > 0;
325114402Sru      break;
326114402Sru    case '+':
327114402Sru      if (v2 < 0) {
328114402Sru	if (*v < INT_MIN - v2)
329114402Sru	  overflow = 1;
330114402Sru      }
331114402Sru      else if (v2 > 0) {
332114402Sru	if (*v > INT_MAX - v2)
333114402Sru	  overflow = 1;
334114402Sru      }
335114402Sru      if (overflow) {
336114402Sru	error("addition overflow");
337114402Sru	return 0;
338114402Sru      }
339114402Sru      *v += v2;
340114402Sru      break;
341114402Sru    case '-':
342114402Sru      if (v2 < 0) {
343114402Sru	if (*v > INT_MAX + v2)
344114402Sru	  overflow = 1;
345114402Sru      }
346114402Sru      else if (v2 > 0) {
347114402Sru	if (*v < INT_MIN + v2)
348114402Sru	  overflow = 1;
349114402Sru      }
350114402Sru      if (overflow) {
351114402Sru	error("subtraction overflow");
352114402Sru	return 0;
353114402Sru      }
354114402Sru      *v -= v2;
355114402Sru      break;
356114402Sru    case '*':
357114402Sru      if (v2 < 0) {
358114402Sru	if (*v > 0) {
359114402Sru	  if (*v > -(unsigned)INT_MIN / -(unsigned)v2)
360114402Sru	    overflow = 1;
361114402Sru	}
362114402Sru	else if (-(unsigned)*v > INT_MAX / -(unsigned)v2)
363114402Sru	  overflow = 1;
364114402Sru      }
365114402Sru      else if (v2 > 0) {
366114402Sru	if (*v > 0) {
367114402Sru	  if (*v > INT_MAX / v2)
368114402Sru	    overflow = 1;
369114402Sru	}
370114402Sru	else if (-(unsigned)*v > -(unsigned)INT_MIN / v2)
371114402Sru	  overflow = 1;
372114402Sru      }
373114402Sru      if (overflow) {
374114402Sru	error("multiplication overflow");
375114402Sru	return 0;
376114402Sru      }
377114402Sru      *v *= v2;
378114402Sru      break;
379114402Sru    case '/':
380114402Sru      if (v2 == 0) {
381114402Sru	error("division by zero");
382114402Sru	return 0;
383114402Sru      }
384114402Sru      *v /= v2;
385114402Sru      break;
386114402Sru    case '%':
387114402Sru      if (v2 == 0) {
388114402Sru	error("modulus by zero");
389114402Sru	return 0;
390114402Sru      }
391114402Sru      *v %= v2;
392114402Sru      break;
393114402Sru    default:
394114402Sru      assert(0);
395114402Sru    }
396114402Sru  }
397114402Sru  return result;
398114402Sru}
399114402Sru
400114402Srustatic int parse_term(units *v, int scale_indicator,
401114402Sru		      int parenthesised, int rigid)
402114402Sru{
403114402Sru  int negative = 0;
404114402Sru  for (;;)
405114402Sru    if (parenthesised && tok.space())
406114402Sru      tok.next();
407114402Sru    else if (tok.ch() == '+')
408114402Sru      tok.next();
409114402Sru    else if (tok.ch() == '-') {
410114402Sru      tok.next();
411114402Sru      negative = !negative;
412114402Sru    }
413114402Sru    else
414114402Sru      break;
415114402Sru  unsigned char c = tok.ch();
416114402Sru  switch (c) {
417114402Sru  case '|':
418114402Sru    // | is not restricted to the outermost level
419114402Sru    // tbl uses this
420114402Sru    tok.next();
421114402Sru    if (!parse_term(v, scale_indicator, parenthesised, rigid))
422114402Sru      return 0;
423114402Sru    int tem;
424114402Sru    tem = (scale_indicator == 'v'
425114402Sru	   ? curdiv->get_vertical_position().to_units()
426114402Sru	   : curenv->get_input_line_position().to_units());
427114402Sru    if (tem >= 0) {
428114402Sru      if (*v < INT_MIN + tem) {
429114402Sru	error("numeric overflow");
430114402Sru	return 0;
431114402Sru      }
432114402Sru    }
433114402Sru    else {
434114402Sru      if (*v > INT_MAX + tem) {
435114402Sru	error("numeric overflow");
436114402Sru	return 0;
437114402Sru      }
438114402Sru    }
439114402Sru    *v -= tem;
440114402Sru    if (negative) {
441114402Sru      if (*v == INT_MIN) {
442114402Sru	error("numeric overflow");
443114402Sru	return 0;
444114402Sru      }
445114402Sru      *v = -*v;
446114402Sru    }
447114402Sru    return 1;
448114402Sru  case '(':
449114402Sru    tok.next();
450114402Sru    c = tok.ch();
451114402Sru    if (c == ')') {
452114402Sru      if (rigid)
453114402Sru	return 0;
454114402Sru      warning(WARN_SYNTAX, "empty parentheses");
455114402Sru      tok.next();
456114402Sru      *v = 0;
457114402Sru      return 1;
458114402Sru    }
459114402Sru    else if (c != 0 && strchr(SCALE_INDICATOR_CHARS, c) != 0) {
460114402Sru      tok.next();
461114402Sru      if (tok.ch() == ';') {
462114402Sru	tok.next();
463114402Sru	scale_indicator = c;
464114402Sru      }
465114402Sru      else {
466114402Sru	error("expected `;' after scale-indicator (got %1)",
467114402Sru	      tok.description());
468114402Sru	return 0;
469114402Sru      }
470114402Sru    }
471114402Sru    else if (c == ';') {
472114402Sru      scale_indicator = 0;
473114402Sru      tok.next();
474114402Sru    }
475114402Sru    if (!parse_expr(v, scale_indicator, 1, rigid))
476114402Sru      return 0;
477114402Sru    tok.skip();
478114402Sru    if (tok.ch() != ')') {
479114402Sru      if (rigid)
480114402Sru	return 0;
481114402Sru      warning(WARN_SYNTAX, "missing `)' (got %1)", tok.description());
482114402Sru    }
483114402Sru    else
484114402Sru      tok.next();
485114402Sru    if (negative) {
486114402Sru      if (*v == INT_MIN) {
487114402Sru	error("numeric overflow");
488114402Sru	return 0;
489114402Sru      }
490114402Sru      *v = -*v;
491114402Sru    }
492114402Sru    return 1;
493114402Sru  case '.':
494114402Sru    *v = 0;
495114402Sru    break;
496114402Sru  case '0':
497114402Sru  case '1':
498114402Sru  case '2':
499114402Sru  case '3':
500114402Sru  case '4':
501114402Sru  case '5':
502114402Sru  case '6':
503114402Sru  case '7':
504114402Sru  case '8':
505114402Sru  case '9':
506114402Sru    *v = 0;
507114402Sru    do {
508114402Sru      if (*v > INT_MAX/10) {
509114402Sru	error("numeric overflow");
510114402Sru	return 0;
511114402Sru      }
512114402Sru      *v *= 10;
513114402Sru      if (*v > INT_MAX - (int(c) - '0')) {
514114402Sru	error("numeric overflow");
515114402Sru	return 0;
516114402Sru      }
517114402Sru      *v += c - '0';
518114402Sru      tok.next();
519114402Sru      c = tok.ch();
520114402Sru    } while (csdigit(c));
521114402Sru    break;
522114402Sru  case '/':
523114402Sru  case '*':
524114402Sru  case '%':
525114402Sru  case ':':
526114402Sru  case '&':
527114402Sru  case '>':
528114402Sru  case '<':
529114402Sru  case '=':
530114402Sru    warning(WARN_SYNTAX, "empty left operand");
531114402Sru    *v = 0;
532114402Sru    return rigid ? 0 : 1;
533114402Sru  default:
534114402Sru    warning(WARN_NUMBER, "numeric expression expected (got %1)",
535114402Sru	    tok.description());
536114402Sru    return 0;
537114402Sru  }
538114402Sru  int divisor = 1;
539114402Sru  if (tok.ch() == '.') {
540114402Sru    tok.next();
541114402Sru    for (;;) {
542114402Sru      c = tok.ch();
543114402Sru      if (!csdigit(c))
544114402Sru	break;
545114402Sru      // we may multiply the divisor by 254 later on
546114402Sru      if (divisor <= INT_MAX/2540 && *v <= (INT_MAX - 9)/10) {
547114402Sru	*v *= 10;
548114402Sru	*v += c - '0';
549114402Sru	divisor *= 10;
550114402Sru      }
551114402Sru      tok.next();
552114402Sru    }
553114402Sru  }
554114402Sru  int si = scale_indicator;
555114402Sru  int do_next = 0;
556114402Sru  if ((c = tok.ch()) != 0 && strchr(SCALE_INDICATOR_CHARS, c) != 0) {
557114402Sru    switch (scale_indicator) {
558114402Sru    case 'z':
559114402Sru      if (c != 'u' && c != 'z') {
560114402Sru	warning(WARN_SCALE,
561114402Sru		"only `z' and `u' scale indicators valid in this context");
562114402Sru	break;
563114402Sru      }
564114402Sru      si = c;
565114402Sru      break;
566114402Sru    case 0:
567114402Sru      warning(WARN_SCALE, "scale indicator invalid in this context");
568114402Sru      break;
569114402Sru    case 'u':
570114402Sru      si = c;
571114402Sru      break;
572114402Sru    default:
573114402Sru      if (c == 'z') {
574114402Sru	warning(WARN_SCALE, "`z' scale indicator invalid in this context");
575114402Sru	break;
576114402Sru      }
577114402Sru      si = c;
578114402Sru      break;
579114402Sru    }
580114402Sru    // Don't do tok.next() here because the next token might be \s, which
581114402Sru    // would affect the interpretation of m.
582114402Sru    do_next = 1;
583114402Sru  }
584114402Sru  switch (si) {
585114402Sru  case 'i':
586114402Sru    *v = scale(*v, units_per_inch, divisor);
587114402Sru    break;
588114402Sru  case 'c':
589114402Sru    *v = scale(*v, units_per_inch*100, divisor*254);
590114402Sru    break;
591114402Sru  case 0:
592114402Sru  case 'u':
593114402Sru    if (divisor != 1)
594114402Sru      *v /= divisor;
595114402Sru    break;
596114402Sru  case 'f':
597114402Sru    *v = scale(*v, 65536, divisor);
598114402Sru    break;
599114402Sru  case 'p':
600114402Sru    *v = scale(*v, units_per_inch, divisor*72);
601114402Sru    break;
602114402Sru  case 'P':
603114402Sru    *v = scale(*v, units_per_inch, divisor*6);
604114402Sru    break;
605114402Sru  case 'm':
606114402Sru    {
607114402Sru      // Convert to hunits so that with -Tascii `m' behaves as in nroff.
608114402Sru      hunits em = curenv->get_size();
609114402Sru      *v = scale(*v, em.is_zero() ? hresolution : em.to_units(), divisor);
610114402Sru    }
611114402Sru    break;
612114402Sru  case 'M':
613114402Sru    {
614114402Sru      hunits em = curenv->get_size();
615114402Sru      *v = scale(*v, em.is_zero() ? hresolution : em.to_units(), divisor*100);
616114402Sru    }
617114402Sru    break;
618114402Sru  case 'n':
619114402Sru    {
620114402Sru      // Convert to hunits so that with -Tascii `n' behaves as in nroff.
621114402Sru      hunits en = curenv->get_size()/2;
622114402Sru      *v = scale(*v, en.is_zero() ? hresolution : en.to_units(), divisor);
623114402Sru    }
624114402Sru    break;
625114402Sru  case 'v':
626114402Sru    *v = scale(*v, curenv->get_vertical_spacing().to_units(), divisor);
627114402Sru    break;
628114402Sru  case 's':
629114402Sru    while (divisor > INT_MAX/(sizescale*72)) {
630114402Sru      divisor /= 10;
631114402Sru      *v /= 10;
632114402Sru    }
633114402Sru    *v = scale(*v, units_per_inch, divisor*sizescale*72);
634114402Sru    break;
635114402Sru  case 'z':
636114402Sru    *v = scale(*v, sizescale, divisor);
637114402Sru    break;
638114402Sru  default:
639114402Sru    assert(0);
640114402Sru  }
641114402Sru  if (do_next)
642114402Sru    tok.next();
643114402Sru  if (negative) {
644114402Sru    if (*v == INT_MIN) {
645114402Sru      error("numeric overflow");
646114402Sru      return 0;
647114402Sru    }
648114402Sru    *v = -*v;
649114402Sru  }
650114402Sru  return 1;
651114402Sru}
652114402Sru
653114402Sruunits scale(units n, units x, units y)
654114402Sru{
655114402Sru  assert(x >= 0 && y > 0);
656114402Sru  if (x == 0)
657114402Sru    return 0;
658114402Sru  if (n >= 0) {
659114402Sru    if (n <= INT_MAX/x)
660114402Sru      return (n*x)/y;
661114402Sru  }
662114402Sru  else {
663114402Sru    if (-(unsigned)n <= -(unsigned)INT_MIN/x)
664114402Sru      return (n*x)/y;
665114402Sru  }
666114402Sru  double res = n*double(x)/double(y);
667114402Sru  if (res > INT_MAX) {
668114402Sru    error("numeric overflow");
669114402Sru    return INT_MAX;
670114402Sru  }
671114402Sru  else if (res < INT_MIN) {
672114402Sru    error("numeric overflow");
673114402Sru    return INT_MIN;
674114402Sru  }
675114402Sru  return int(res);
676114402Sru}
677114402Sru
678114402Sruvunits::vunits(units x)
679114402Sru{
680114402Sru  // don't depend on the rounding direction for division of negative integers
681114402Sru  if (vresolution == 1)
682114402Sru    n = x;
683114402Sru  else
684114402Sru    n = (x < 0
685114402Sru	 ? -((-x + vresolution/2 - 1)/vresolution)
686114402Sru	 : (x + vresolution/2 - 1)/vresolution);
687114402Sru}
688114402Sru
689114402Sruhunits::hunits(units x)
690114402Sru{
691114402Sru  // don't depend on the rounding direction for division of negative integers
692114402Sru  if (hresolution == 1)
693114402Sru    n = x;
694114402Sru  else
695114402Sru    n = (x < 0
696114402Sru	 ? -((-x + hresolution/2 - 1)/hresolution)
697114402Sru	 : (x + hresolution/2 - 1)/hresolution);
698114402Sru}
699