PennMUSH Community

root/1.8.3/trunk/src/funmath.c

Revision 1170, 50.2 kB (checked in by shawnw, 8 months ago)

Ran make indent

Line 
1 /**
2  * \file funmath.c
3  *
4  * \brief Mathematical functions for mushcode.
5  *
6  *
7  */
8
9 #include "copyrite.h"
10
11 #include "config.h"
12 #include <math.h>
13 #include <string.h>
14 #include <ctype.h>
15 #include <errno.h>
16 #include "conf.h"
17 #include "externs.h"
18 #include "sort.h"
19 #include "parse.h"
20 #include "confmagic.h"
21
22 #ifdef WIN32
23 #pragma warning( disable : 4761)        /* NJG: disable warning re conversion */
24 #endif
25
26 #ifndef M_PI
27 /** The ratio of the circumference of a circle to its denominator. */
28 #define M_PI 3.14159265358979323846264338327
29 #endif
30
31 #define EPSILON 0.000000001  /**< limit of precision for float equality */
32 #define EQ(x,y) (fabs(x-y) < EPSILON)  /**< floating point equality macro */
33
34 static void do_spellnum(char *num, unsigned int len, char **buff, char ***bp);
35 static void do_ordinalize(char **buff, char ***bp);
36 static NVAL find_median(NVAL *, int);
37
38 static NVAL angle_to_rad(NVAL angle, const char *from);
39 static NVAL rad_to_angle(NVAL angle, const char *to);
40 static double frac(double v, double *RESTRICT n, double *RESTRICT d,
41                    double error);
42
43 int format_long(intmax_t n, char *buff, char **bp, int maxlen, int base);
44
45 /* Generated by gperf */
46 #include "lmathtab.c"
47
48 /* Functions for testing and parsing IVALs and UIVALs, the types of
49  * arguments to math functions that work on integers instead of
50  *  floating-point numbers. No matter what IVAL is (32-bit or 64-bit),
51  *  they can be passed to safe_integer()/safe_uinteger().
52  *
53  * Math functions that operate on IVALs: div(), floordiv(), modulo(),
54  *  remainder()
55  * Math functions that operate on UIVALS: shl(), shr(), band(), bnot(), bor()
56  *  bxor(), bnand()
57  *
58  * Other functions work on NVALs or accept plain ints
59  */
60
61 static IVAL
62 parse_ival_full(const char *str, char **end, int base)
63 {
64 #if SIZEOF_IVAL == 4
65   return parse_int32(str, end, base);
66 #else
67 #error "Unsupported IVAL size"
68 #endif
69 }
70
71 static IVAL
72 parse_ival(const char *str)
73 {
74   return parse_ival_full(str, NULL, 10);
75 }
76
77 static UIVAL
78 parse_uival_full(const char *str, char **end, int base)
79 {
80 #if SIZEOF_IVAL == 4
81   return parse_uint32(str, end, base);
82 #else
83 #error "Unsupported IVAL size"
84 #endif
85 }
86
87 static UIVAL
88 parse_uival(const char *str)
89 {
90   return parse_uival_full(str, NULL, 10);
91 }
92
93
94 /** Is string an integer suitable for a math function?
95  * To TinyMUSH, any string is an integer. To PennMUSH, a string that
96  * passes strtol is an integer, and a blank string is an integer
97  * if NULL_EQ_ZERO is turned on.
98  * \param str string to check.
99  * \retval 1 string is an integer.
100  * \retval 0 string is not an integer.
101  */
102 static bool
103 is_ival(char const *str)
104 {
105   char *end;
106
107   /* If we're emulating Tiny, anything is an integer */
108   if (TINY_MATH)
109     return 1;
110   if (!str)
111     return 0;
112   while (isspace((unsigned char) *str))
113     str++;
114   if (*str == '\0')
115     return NULL_EQ_ZERO;
116   errno = 0;
117   parse_ival_full(str, &end, 10);
118   if (errno == ERANGE || *end != '\0')
119     return 0;
120   return 1;
121 }
122
123 /** Is string a UIVAL?
124  * To TinyMUSH, any string is an uinteger. To PennMUSH, a string that
125  * passes strtoul is an uinteger, and a blank string is an uinteger
126  * if NULL_EQ_ZERO is turned on.
127  * \param str string to check.
128  * \retval 1 string is an uinteger.
129  * \retval 0 string is not an uinteger.
130  */
131 static bool
132 is_uival(char const *str)
133 {
134   char *end;
135
136   /* If we're emulating Tiny, anything is an integer */
137   if (TINY_MATH)
138     return 1;
139   if (!str)
140     return 0;
141   /* strtoul() accepts negative numbers, so we still have to do this check */
142   while (isspace((unsigned char) *str))
143     str++;
144   if (*str == '\0')
145     return NULL_EQ_ZERO;
146   if (!(isdigit((unsigned char) *str) || *str == '+'))
147     return 0;
148   errno = 0;
149   parse_uival_full(str, &end, 10);
150   if (errno == ERANGE || *end != '\0')
151     return 0;
152   return 1;
153 }
154
155 /* ARGSUSED */
156 FUNCTION(fun_ctu)
157 {
158   NVAL angle;
159   if (!is_number(args[0])) {
160     safe_str(T(e_num), buff, bp);
161     return;
162   }
163
164   if (!args[1] || !args[2]) {
165     safe_str(T("#-1 INVALID ANGLE TYPE"), buff, bp);
166     return;
167   }
168   angle = angle_to_rad(parse_number(args[0]), args[1]);
169   safe_number(rad_to_angle(angle, args[2]), buff, bp);
170 }
171
172 /* ARGSUSED */
173 FUNCTION(fun_add)
174 {
175   math_add(args, nargs, buff, bp);
176 }
177
178 /* ARGSUSED */
179 FUNCTION(fun_sub)
180 {
181   math_sub(args, nargs, buff, bp);
182 }
183
184 /* ARGSUSED */
185 FUNCTION(fun_mul)
186 {
187   math_mul(args, nargs, buff, bp);
188 }
189
190 /* TO-DO: I have better code for comparing floating-point numbers
191    lying around somewhere. The idea is that numbers that are very
192    close can be 'close enough' to be equal or whatever, without having
193    to be exactly the same. */
194
195 /* ARGSUSED */
196 FUNCTION(fun_gt)
197 {
198   if (!is_number(args[0]) || !is_number(args[1])) {
199     safe_str(T(e_nums), buff, bp);
200     return;
201   }
202   safe_boolean(parse_number(args[0]) > parse_number(args[1]), buff, bp);
203 }
204
205 /* ARGSUSED */
206 FUNCTION(fun_gte)
207 {
208   if (!is_number(args[0]) || !is_number(args[1])) {
209     safe_str(T(e_nums), buff, bp);
210     return;
211   }
212   safe_boolean(parse_number(args[0]) >= parse_number(args[1]), buff, bp);
213 }
214
215 /* ARGSUSED */
216 FUNCTION(fun_lt)
217 {
218   if (!is_number(args[0]) || !is_number(args[1])) {
219     safe_str(T(e_nums), buff, bp);
220     return;
221   }
222   safe_boolean(parse_number(args[0]) < parse_number(args[1]), buff, bp);
223 }
224
225 /* ARGSUSED */
226 FUNCTION(fun_lte)
227 {
228   if (!is_number(args[0]) || !is_number(args[1])) {
229     safe_str(T(e_nums), buff, bp);
230     return;
231   }
232   safe_boolean(parse_number(args[0]) <= parse_number(args[1]), buff, bp);
233 }
234
235 /* ARGSUSED */
236 FUNCTION(fun_eq)
237 {
238   if (!is_number(args[0]) || !is_number(args[1])) {
239     safe_str(T(e_nums), buff, bp);
240     return;
241   }
242   safe_boolean(EQ(parse_number(args[0]), parse_number(args[1])), buff, bp);
243 }
244
245 /* ARGSUSED */
246 FUNCTION(fun_neq)
247 {
248   if (!is_number(args[0]) || !is_number(args[1])) {
249     safe_str(T(e_nums), buff, bp);
250     return;
251   }
252   safe_boolean(!EQ(parse_number(args[0]), parse_number(args[1])), buff, bp);
253 }
254
255 /* ARGSUSED */
256 FUNCTION(fun_max)
257 {
258   math_max(args, nargs, buff, bp);
259 }
260
261 /* ARGSUSED */
262 FUNCTION(fun_min)
263 {
264   math_min(args, nargs, buff, bp);
265 }
266
267 /* ARGSUSED */
268 FUNCTION(fun_sign)
269 {
270   NVAL x;
271
272   if (!is_number(args[0])) {
273     safe_str(T(e_num), buff, bp);
274     return;
275   }
276   x = parse_number(args[0]);
277   if (EQ(x, 0))
278     safe_chr('0', buff, bp);
279   else if (x > 0)
280     safe_chr('1', buff, bp);
281   else
282     safe_str("-1", buff, bp);
283 }
284
285 /* ARGSUSED */
286 FUNCTION(fun_shl)
287 {
288   if (!is_uival(args[0]) || !is_uival(args[1])) {
289     safe_str(T(e_uints), buff, bp);
290     return;
291   }
292   safe_uinteger(parse_uival(args[0]) << parse_uival(args[1]), buff, bp);
293 }
294
295 /* ARGSUSED */
296 FUNCTION(fun_shr)
297 {
298   if (!is_uival(args[0]) || !is_uival(args[1])) {
299     safe_str(T(e_uints), buff, bp);
300     return;
301   }
302   safe_uinteger(parse_uival(args[0]) >> parse_uival(args[1]), buff, bp);
303 }
304
305 /* ARGSUSED */
306 FUNCTION(fun_inc)
307 {
308   int num;
309   char *p;
310   /* Handle the case of a pure number */
311   if (is_strict_integer(args[0])) {
312     safe_integer(parse_integer(args[0]) + 1, buff, bp);
313     return;
314   }
315   /* Handle a null string */
316   if (!*args[0]) {
317     safe_str(NULL_EQ_ZERO ? "1" : T("#-1 ARGUMENT MUST END IN AN INTEGER"),
318              buff, bp);
319     return;
320   }
321   p = args[0] + arglens[0] - 1;
322   if (!isdigit((unsigned char) *p)) {
323     if (NULL_EQ_ZERO) {
324       safe_str(args[0], buff, bp);
325       safe_str("1", buff, bp);
326     } else
327       safe_str(T("#-1 ARGUMENT MUST END IN AN INTEGER"), buff, bp);
328     return;
329   }
330   while ((isdigit((unsigned char) *p) || (*p == '-')) && p != args[0]) {
331     if (*p == '-') {
332       p--;
333       break;
334     }
335     p--;
336   }
337   /* p now points to the last non-numeric character in the string */
338   if (p == args[0] && (isdigit((unsigned char) *p) || (*p == '-'))) {
339     /* Special case - it's all digits, but out of range. */
340     safe_str(T(e_range), buff, bp);
341     return;
342   }
343
344   /* Move it to the first numeric character */
345   p++;
346   num = parse_integer(p) + 1;
347   *p = '\0';
348   safe_str(args[0], buff, bp);
349   safe_integer(num, buff, bp);
350 }
351
352 /* ARGSUSED */
353 FUNCTION(fun_dec)
354 {
355   int num;
356   char *p;
357   /* Handle the case of a pure number */
358   if (is_strict_integer(args[0])) {
359     safe_integer(parse_integer(args[0]) - 1, buff, bp);
360     return;
361   }
362   /* Handle a null string */
363   if (!*args[0]) {
364     safe_str(NULL_EQ_ZERO ? "-1" : T("#-1 ARGUMENT MUST END IN AN INTEGER"),
365              buff, bp);
366     return;
367   }
368   p = args[0] + arglens[0] - 1;
369   if (!isdigit((unsigned char) *p)) {
370     if (NULL_EQ_ZERO) {
371       safe_str(args[0], buff, bp);
372       safe_str("-1", buff, bp);
373     } else
374       safe_str(T("#-1 ARGUMENT MUST END IN AN INTEGER"), buff, bp);
375     return;
376   }
377   while ((isdigit((unsigned char) *p) || (*p == '-')) && p != args[0]) {
378     if (*p == '-') {
379       p--;
380       break;
381     }
382     p--;
383   }
384   /* p now points to the last non-numeric character in the string */
385   if (p == args[0] && (isdigit((unsigned char) *p) || (*p == '-'))) {
386     /* Special case - it's all digits, but out of range. */
387     safe_str(T(e_range), buff, bp);
388     return;
389   }
390   /* Move it to the first numeric character */
391   p++;
392   num = parse_integer(p) - 1;
393   *p = '\0';
394   safe_str(args[0], buff, bp);
395   safe_integer(num, buff, bp);
396 }
397
398 /* ARGSUSED */
399 FUNCTION(fun_trunc)
400 {
401   /* This function does not have the non-number check because
402    * the help file explicitly states that this function can
403    * be used to turn "101dalmations" into "101".
404    */
405   safe_integer(parse_integer(args[0]), buff, bp);
406 }
407
408 /* ARGSUSED */
409 FUNCTION(fun_div)
410 {
411   math_div(args, nargs, buff, bp);
412 }
413
414 /* ARGSUSED */
415 FUNCTION(fun_floordiv)
416 {
417   math_floordiv(args, nargs, buff, bp);
418 }
419
420 /* ARGSUSED */
421 FUNCTION(fun_modulo)
422 {
423   math_modulo(args, nargs, buff, bp);
424 }
425
426 /* ARGSUSED */
427 FUNCTION(fun_remainder)
428 {
429   math_remainder(args, nargs, buff, bp);
430 }
431
432
433 /* ARGSUSED */
434 FUNCTION(fun_abs)
435 {
436   if (!is_number(args[0])) {
437     safe_str(T(e_num), buff, bp);
438     return;
439   }
440   safe_number(fabs(parse_number(args[0])), buff, bp);
441 }
442
443 /* ARGSUSED */
444 FUNCTION(fun_dist2d)
445 {
446   math_dist2d(args, nargs, buff, bp);
447 }
448
449 FUNCTION(fun_dist3d)
450 {
451   math_dist3d(args, nargs, buff, bp);
452 }
453
454 /* ------------------------------------------------------------------------
455  * Dune's vector functions: VADD, VSUB, VMUL, VCROSS, VMAG, VUNIT, VDIM
456  *  VCRAMER?
457  * Vectors are space-separated numbers.
458  */
459
460 /* ARGSUSED */
461 FUNCTION(fun_vmax)
462 {
463   char *p1, *p2;
464   char *start;
465   char sep;
466   NVAL a, b;
467
468   /* return if a list is empty */
469   if (!args[0] || !args[1]) {
470     safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp);
471     return;
472   }
473
474   if (!delim_check(buff, bp, nargs, args, 3, &sep))
475     return;
476   p1 = trim_space_sep(args[0], sep);
477   p2 = trim_space_sep(args[1], sep);
478
479   /* return if a list is empty */
480   if (!*p1 || !*p2) {
481     safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp);
482     return;
483   }
484
485   /* max the vectors */
486   start = *bp;
487   a = parse_number(split_token(&p1, sep));
488   b = parse_number(split_token(&p2, sep));
489   safe_number((a > b) ? a : b, buff, bp);
490
491   while (p1 && p2) {
492     safe_chr(sep, buff, bp);
493     a = parse_number(split_token(&p1, sep));
494     b = parse_number(split_token(&p2, sep));
495     safe_number((a > b) ? a : b, buff, bp);
496   }
497
498   /* make sure vectors were the same length */
499   if (p1 || p2) {
500     *bp = start;
501     safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp);
502     return;
503   }
504 }
505
506 /* ARGSUSED */
507 FUNCTION(fun_vmin)
508 {
509   char *p1, *p2;
510   char *start;
511   char sep;
512   NVAL a, b;
513
514   /* return if a list is empty */
515   if (!args[0] || !args[1]) {
516     safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp);
517     return;
518   }
519
520   if (!delim_check(buff, bp, nargs, args, 3, &sep))
521     return;
522   p1 = trim_space_sep(args[0], sep);
523   p2 = trim_space_sep(args[1], sep);
524
525   /* return if a list is empty */
526   if (!*p1 || !*p2)
527     return;
528
529   /* max the vectors */
530   start = *bp;
531   a = parse_number(split_token(&p1, sep));
532   b = parse_number(split_token(&p2, sep));
533   safe_number((a < b) ? a : b, buff, bp);
534
535   while (p1 && p2) {
536     safe_chr(sep, buff, bp);
537     a = parse_number(split_token(&p1, sep));
538     b = parse_number(split_token(&p2, sep));
539     safe_number((a < b) ? a : b, buff, bp);
540   }
541
542   /* make sure vectors were the same length */
543   if (p1 || p2) {
544     *bp = start;
545     safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp);
546     return;
547   }
548 }
549
550
551 /* ARGSUSED */
552 FUNCTION(fun_vadd)
553 {
554   char *p1, *p2;
555   char *start;
556   char sep;
557
558   /* return if a list is empty */
559   if (!args[0] || !args[1]) {
560     safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp);
561     return;
562   }
563
564   if (!delim_check(buff, bp, nargs, args, 3, &sep))
565     return;
566   p1 = trim_space_sep(args[0], sep);
567   p2 = trim_space_sep(args[1], sep);
568
569   /* return if a list is empty */
570   if (!*p1 || !*p2)