]> jfr.im git - irc/evilnet/x3.git/blame - src/math.c
last command, plus a few fixes n tweaks
[irc/evilnet/x3.git] / src / math.c
CommitLineData
e5a8f7cd 1/* math.c - Mathematics functions for chanserv.calc
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version. Important limitations are
7 * listed in the COPYING file that accompanies this software.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, email srvx-maintainers@srvx.net.
16 *
17 * $Id$
18 */
19
20#include "conf.h"
21#include "helpfile.h"
22#include "nickserv.h"
23#include "modcmd.h"
24#include "saxdb.h"
25#include "timeq.h"
23475fc6 26
27#ifdef HAVE_TGMATH_H
28#include <tgmath.h>
29#else
30#include <math.h>
31#include <complex.h>
32#endif
33
e5a8f7cd 34#include <ctype.h>
d8cf9c21 35#include <stdio.h>
e5a8f7cd 36#include <stdlib.h>
37#include <string.h>
e5a8f7cd 38#include <time.h>
39
40#ifdef WIN32
41#define strcasecmp stricmp
42#endif
43
44enum MathType {
45 mEnd,
46 mPlus,
47 mMinus,
48 mMult,
49 mDiv,
50 mPower,
51 mLt,
52 mRt,
53 mOp,
54 mOpEnd,
55 mNumber
56};
57
58enum MathOpType {
59 otExp,
60 otLog,
61 otSin,
62 otASin,
63 otSinH,
64 otCos,
65 otACos,
66 otCosH,
67 otTan,
68 otATan,
69 otTanH,
70 otSqrt,
71 otAbs,
72 otCeil,
73 otFloor
74};
75
76/* This table is needed for the translation from infix to postfix. */
77static const int TranslateTable[][10] = {
78/*List: _ + - * / ^ ( ) [ ] */
79/*Stack: */
80/* _ */ {4, 1, 1, 1, 1, 1, 1, 5, 1, 5},
81/* + */ {2, 2, 2, 1, 1, 1, 1, 2, 1, 2},
82 {2, 2, 2, 1, 1, 1, 1, 2, 1, 2},
83 {2, 2, 2, 2, 2, 1, 1, 2, 1, 2},
84 {2, 2, 2, 2, 2, 1, 1, 2, 1, 2},
85 {2, 2, 2, 2, 2, 2, 1, 2, 1, 2},
86 {5, 1, 1, 1, 1, 1, 1, 3, 1, 5},
87 {5, 5, 5, 5, 5, 5, 5, 5, 5, 5},
88 {5, 1, 1, 1, 1, 1, 1, 5, 1, 6},
89 {5, 5, 5, 5, 5, 5, 5, 5, 5, 5}
90};
91
92typedef struct MathToken {
93 enum MathType Type;
94 double Value;
95 enum MathOpType OpType;
96 struct MathToken *Next;
97 struct MathToken *Previous; /* Previous is used in the stack */
98} *MathList;
99
100typedef struct Values {
101 double Value;
102 struct Values *Next;
103 struct Values *Previous;
104} *ValueList;
105
106void do_math(char *Buffer, char *Math)
107{
108 int HasDot;
109 double Value = 0.0;
110 double Floating; /* = 10^number of digits after the dot */
111 int ValueExpected = 1;
112 char Identifier[512];
113
114 MathList ListFirst = NULL;
115 MathList StackFirst = NULL;
116 MathList PostFixFirst = NULL;
117 ValueList TheValuesFirst = NULL;
118 MathList List;
119 MathList Stack;
120 MathList PostFix;
121 ValueList TheValues;
122 int Minus = 0;
123
124 void *Temp;
125 int Index;
126 int OEndExpected;
127
128 if (!(List = ListFirst = malloc(sizeof(struct MathToken)))) goto MemError;
129 if (!(Stack = StackFirst = malloc(sizeof(struct MathToken)))) goto MemError;
130 if (!(PostFix = PostFixFirst = malloc(sizeof(struct MathToken)))) goto MemError;
131 if (!(TheValues = TheValuesFirst = malloc(sizeof(struct Values)))) goto MemError;
132
133 List->Next = NULL;
134 Stack->Next = NULL;
135 PostFix->Next = NULL;
136 TheValues->Next = NULL;
137
138 StackFirst->Type = mEnd;
139
140 /* First tokenize the buffer */
141
142 while (*Math) {
143 if (isdigit(*Math) || *Math == '.') {
144 if (!ValueExpected) {
145 strcpy(Buffer, "Unexpected value");
146 goto End;
147 }
148 HasDot = 0;
149 Value = 0;
150 Floating = 1;
151 while (isdigit(*Math) || *Math == '.') {
152 if (*Math == '.')
153 if (HasDot) {
154 strcpy(Buffer, "Error in constant");
155 goto End;
156 }
157 else
158 HasDot = 1;
159 else
160 if (!HasDot)
161 Value = Value * 10 + *Math - '0';
162 else {
163 Floating = Floating * 10;
164 Value = Value + (*Math - '0') / Floating;
165 }
166 ++Math;
167 }
168 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
169 List->Next = NULL;
170 List->Type = mNumber;
171 List->Value = Value;
172 if (Minus) {
173 List->Value = -Value;
174 Minus = 0;
175 }
176 ValueExpected = 0;
177 }
178 else switch (*Math) {
179 case ' ': case '\t':
180 ++Math;
181 break;
182 case '+': case '-':
183 case '*': case '/':
184 case '^': case '(':
185 case ')': case ']':
186 if (*Math == '-' && ValueExpected) {
187 Minus = !Minus;
188 ++Math;
189 break;
190 }
191 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto End;
192 List->Next = NULL;
193 switch (*Math) {
194 case '+': List->Type = mPlus; break;
195 case '-': List->Type = mMinus; break;
196 case '*': List->Type = mMult; break;
197 case '/': List->Type = mDiv; break;
198 case '^': List->Type = mPower; break;
199 case '(': List->Type = mLt; break;
200 case ')': List->Type = mRt; break;
201 case ']': List->Type = mOpEnd; break;
202 }
203 if (*Math != '(' && ValueExpected) {
204 strcpy(Buffer, "Value expected");
205 goto End;
206 }
207 if (*Math != ')' && *Math != ']')
208 ValueExpected = 1;
209 ++Math;
210 break;
211 default:
212 if (isalpha(*Math)) {
213 Index = 0;
214 while (isalpha(*Math))
215 Identifier[Index++] = *Math++;
216 Identifier[Index] = '\0';
217 OEndExpected = 0;
218 if (!ValueExpected) {
219 strcpy(Buffer, "Unexpected value");
220 goto End;
221 }
222 if (!strcasecmp(Identifier, "e")) {
223 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
224 List->Next = NULL;
225 List->Type = mNumber;
226 List->Value = exp(1);
227 ValueExpected = 0;
228 }
229 else if (!strcasecmp(Identifier, "pi")) {
230 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
231 List->Next = NULL;
232 List->Type = mNumber;
233 List->Value = 4 * atan(1);
234 ValueExpected = 0;
235 }
236 else if (!strcasecmp(Identifier, "rand")) {
237 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
238 List->Next = NULL;
239 List->Type = mNumber;
240 List->Value = (double)rand() / (double)RAND_MAX;
241 ValueExpected = 0;
242 }
243 else if (!strcasecmp(Identifier, "exp")) {
244 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
245 List->Next = NULL;
246 List->Type = mOp;
247 List->OpType = otExp;
248 OEndExpected = 1;
249 }
250 else if (!strcasecmp(Identifier, "log")) {
251 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
252 List->Next = NULL;
253 List->Type = mOp;
254 List->OpType = otLog;
255 OEndExpected = 1;
256 }
257 else if (!strcasecmp(Identifier, "sin")) {
258 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
259 List->Next = NULL;
260 List->Type = mOp;
261 List->OpType = otSin;
262 OEndExpected = 1;
263 }
264 else if (!strcasecmp(Identifier, "asin")) {
265 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
266 List->Next = NULL;
267 List->Type = mOp;
268 List->OpType = otASin;
269 OEndExpected = 1;
270 }
271 else if (!strcasecmp(Identifier, "sinh")) {
272 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
273 List->Next = NULL;
274 List->Type = mOp;
275 List->OpType = otSinH;
276 OEndExpected = 1;
277 }
278 else if (!strcasecmp(Identifier, "cos")) {
279 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
280 List->Next = NULL;
281 List->Type = mOp;
282 List->OpType = otCos;
283 OEndExpected = 1;
284 }
285 else if (!strcasecmp(Identifier, "acos")) {
286 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
287 List->Next = NULL;
288 List->Type = mOp;
289 List->OpType = otACos;
290 OEndExpected = 1;
291 }
292 else if (!strcasecmp(Identifier, "cosh")) {
293 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
294 List->Next = NULL;
295 List->Type = mOp;
296 List->OpType = otCosH;
297 OEndExpected = 1;
298 }
299 else if (!strcasecmp(Identifier, "tan")) {
300 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
301 List->Next = NULL;
302 List->Type = mOp;
303 List->OpType = otTan;
304 OEndExpected = 1;
305 }
306 else if (!strcasecmp(Identifier, "atan")) {
307 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
308 List->Next = NULL;
309 List->Type = mOp;
310 List->OpType = otATan;
311 OEndExpected = 1;
312 }
313 else if (!strcasecmp(Identifier, "tanh")) {
314 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
315 List->Next = NULL;
316 List->Type = mOp;
317 List->OpType = otTanH;
318 OEndExpected = 1;
319 }
320 else if (!strcasecmp(Identifier, "sqrt")) {
321 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
322 List->Next = NULL;
323 List->Type = mOp;
324 List->OpType = otSqrt;
325 OEndExpected = 1;
326 }
327 else if (!strcasecmp(Identifier, "abs")) {
328 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
329 List->Next = NULL;
330 List->Type = mOp;
331 List->OpType = otAbs;
332 OEndExpected = 1;
333 }
334 else if (!strcasecmp(Identifier, "ceil")) {
335 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
336 List->Next = NULL;
337 List->Type = mOp;
338 List->OpType = otCeil;
339 OEndExpected = 1;
340 }
341 else if (!strcasecmp(Identifier, "floor")) {
342 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
343 List->Next = NULL;
344 List->Type = mOp;
345 List->OpType = otFloor;
346 OEndExpected = 1;
347 }
348 else {
349 strcpy(Buffer, "Unexpected Identifier");
350 goto End;
351 }
352 if (OEndExpected) {
353 while (*Math == ' ' || *Math == '\t')
354 ++Math;
355 if (*Math != '[') {
356 strcpy(Buffer, "'[' expected");
357 goto End;
358 }
359 ++Math;
360 }
361 }
362 else {
363 strcpy(Buffer, "Unknown character in expression");
364 goto End;
365 }
366 }
367 }
368 if (ValueExpected) {
369 strcpy(Buffer, "Value expected");
370 goto End;
371 }
372 if (!(List = List->Next = malloc(sizeof(struct MathToken)))) goto MemError;
373 List->Next = NULL;
374 List->Type = mEnd;
375
376 /* We've got them tokenized now... now convert it
377 from infix notation to postfix notation */
378
379 List = ListFirst->Next;
380 while (List) {
381 if (List->Type == mNumber) {
382 PostFix = PostFix->Next = List;
383 ListFirst = List = List->Next;
384 PostFix->Next = NULL;
385 }
386 else switch (TranslateTable[Stack->Type][List->Type]) {
387 case 1:
388 List->Previous = Stack;
389 Stack = Stack->Next = List;
390 ListFirst = List = List->Next;
391 Stack->Next = NULL;
392 break;
393 case 2:
394 PostFix = PostFix->Next = Stack;
395 Stack = Stack->Previous;
396 Stack->Next = NULL;
397 break;
398 case 3:
399 Stack = Stack->Previous;
400 free(Stack->Next);
401 Stack->Next = NULL;
402 Temp = List;
403 ListFirst = List = List->Next;
404 free(Temp);
405 break;
406 case 4:
407 PostFix = PostFix->Next = List;
408 ListFirst = List = List->Next;
409 break;
410 case 5:
411 strcpy(Buffer, "Error in expression");
412 goto End;
413 case 6:
414 PostFix = PostFix->Next = Stack;
415 Stack = Stack->Previous;
416 Stack->Next = NULL;
417 Temp = List;
418 ListFirst = List = List->Next;
419 free(Temp);
420 break;
421 default:
422 strcpy(Buffer, "Internal error");
423 goto End;
424 }
425 }
426
427 /* Now we've got everything in Postfix notation... calculate it now */
428
429 PostFix = PostFixFirst->Next;
430 while (PostFix) {
431 switch (PostFix->Type) {
432 case mNumber:
433 if (!(TheValues->Next = malloc(sizeof(struct Values)))) goto MemError;
434 TheValues->Next->Previous = TheValues;
435 TheValues = TheValues->Next;
436 TheValues->Next = NULL;
437 TheValues->Value = PostFix->Value;
438 break;
439 case mPlus:
440 TheValues->Previous->Value += TheValues->Value;
441 TheValues = TheValues->Previous;
442 free(TheValues->Next);
443 TheValues->Next = NULL;
444 break;
445 case mMinus:
446 TheValues->Previous->Value -= TheValues->Value;
447 TheValues = TheValues->Previous;
448 free(TheValues->Next);
449 TheValues->Next = NULL;
450 break;
451 case mMult:
452 TheValues->Previous->Value *= TheValues->Value;
453 TheValues = TheValues->Previous;
454 free(TheValues->Next);
455 TheValues->Next = NULL;
456 break;
457 case mDiv:
458 if (TheValues->Value == 0) {
459 strcpy(Buffer, "Division by zero error!");
460 goto End;
461 }
462 TheValues->Previous->Value /= TheValues->Value;
463 TheValues = TheValues->Previous;
464 free(TheValues->Next);
465 TheValues->Next = NULL;
466 break;
467 case mPower:
468 TheValues->Previous->Value = pow(TheValues->Previous->Value, TheValues->Value);
469 TheValues = TheValues->Previous;
470 free(TheValues->Next);
471 TheValues->Next = NULL;
472 break;
473 case mEnd:
474 Value = TheValues->Value;
475 break;
476 case mOp:
477 switch (PostFix->OpType) {
478 case otExp:
479 TheValues->Value = exp(TheValues->Value);
480 break;
481 case otLog:
482 if (TheValues->Value <= 0) {
483 strcpy(Buffer, "Log of non-positive value error");
484 goto End;
485 }
486 TheValues->Value = log(TheValues->Value);
487 break;
488 case otSin:
489 TheValues->Value = sin(TheValues->Value);
490 break;
491 case otASin:
492 if (TheValues->Value < -1 || TheValues->Value > 1) {
493 strcpy(Buffer, "Domain error");
494 goto End;
495 }
496 TheValues->Value = asin(TheValues->Value);
497 break;
498 case otSinH:
499 TheValues->Value = sinh(TheValues->Value);
500 case otCos:
501 TheValues->Value = cos(TheValues->Value);
502 break;
503 case otACos:
504 if (TheValues->Value < -1 || TheValues->Value > 1) {
505 strcpy(Buffer, "Domain error");
506 goto End;
507 }
508 TheValues->Value = acos(TheValues->Value);
509 break;
510 case otCosH:
511 TheValues->Value = cosh(TheValues->Value);
512 break;
513 case otTan:
514 TheValues->Value = tan(TheValues->Value);
515 break;
516 case otATan:
517 TheValues->Value = atan(TheValues->Value);
518 break;
519 case otTanH:
520 TheValues->Value = tanh(TheValues->Value);
521 break;
522 case otSqrt:
523 if (TheValues->Value < 0) {
524 strcpy(Buffer, "Sqrt from number < 0");
525 goto End;
526 }
527 TheValues->Value = sqrt(TheValues->Value);
528 break;
529 case otAbs:
530 TheValues->Value = fabs(TheValues->Value);
531 break;
532 case otCeil:
533 TheValues->Value = ceil(TheValues->Value);
534 break;
535 case otFloor:
536 TheValues->Value = floor(TheValues->Value);
537 break;
538 }
539 break;
540 /* The following three do not occur. They are here to prevent compiler warnings */
541 case mLt:
542 case mRt:
543 case mOpEnd:
544 break;
545 }
546 PostFix = PostFix->Next;
547 }
548
549 if (fabs(Value) < 1000000 && (fabs(Value) > 0.001 || Value == 0.0))
550 {
551 if (fabs(Value - floor(Value + 0.5)) < 0.00001) {
552 sprintf(Buffer, "%.0f", Value);
553 }
554 else {
555 sprintf(Buffer, "%f", Value);
556 }
557 }
558 else {
559 sprintf(Buffer, "%E", Value);
560 }
561End:
562 /* Free up memory here */
563
564 List = ListFirst;
565 while (List) {
566 Temp = List;
567 List = List->Next;
568 free(Temp);
569 }
570
571 Stack = StackFirst;
572 while (Stack) {
573 Temp = Stack;
574 Stack = Stack->Next;
575 free(Temp);
576 }
577
578 PostFix = PostFixFirst;
579 while (PostFix) {
580 Temp = PostFix;
581 PostFix = PostFix->Next;
582 free(Temp);
583 }
584
585 TheValues = TheValuesFirst;
586 while (TheValues) {
587 Temp = TheValues;
588 TheValues = TheValues->Next;
589 free(Temp);
590 }
591
592 return;
593
594MemError:
595 strcpy(Buffer, "Couldn't allocate enough memory");
596 goto End;
597}