/fluffos/packages/math.c
https://github.com/quixadhal/bloodlines · C · 380 lines · 304 code · 56 blank · 20 comment · 67 complexity · 484385387919e17769d78db87282276b MD5 · raw file
- /*
- math.c: this file contains the math efunctions called from
- inside eval_instruction() in interpret.c.
- -- coded by Truilkan 93/02/21
-
- Added norm, dotprod, distance, angle, log2.
- Also added round() which works on floats. If you do work with floats,
- you'll discover it's actually more useful than you'd think.
- Added int args to the efuns as apppropriate.
- - Hamlet 20090516
- */
- #include <math.h>
- #include <limits.h>
- #ifdef LATTICE
- #include "/lpc_incl.h"
- #else
- #include "../lpc_incl.h"
- #include "../efun_protos.h"
- #endif
- #define SQUARE(n) ((n)*(n))
- #ifdef F_COS
- void
- f_cos (void)
- {
- sp->u.real = cos(sp->u.real);
- }
- #endif
- #ifdef F_SIN
- void
- f_sin (void)
- {
- sp->u.real = sin(sp->u.real);
- }
- #endif
- #ifdef F_TAN
- void
- f_tan (void)
- {
- /*
- * maybe should try to check that tan won't blow up (x != (Pi/2 + N*Pi))
- */
- sp->u.real = tan(sp->u.real);
- }
- #endif
- #ifdef F_ASIN
- void
- f_asin (void)
- {
- if (sp->u.real < -1.0) {
- error("math: asin(x) with (x < -1.0)\n");
- return;
- } else if (sp->u.real > 1.0) {
- error("math: asin(x) with (x > 1.0)\n");
- return;
- }
- sp->u.real = asin(sp->u.real);
- }
- #endif
- #ifdef F_ACOS
- void
- f_acos (void)
- {
- if (sp->u.real < -1.0) {
- error("math: acos(x) with (x < -1.0)\n");
- return;
- } else if (sp->u.real > 1.0) {
- error("math: acos(x) with (x > 1.0)\n");
- return;
- }
- sp->u.real = acos(sp->u.real);
- }
- #endif
- #ifdef F_ATAN
- void
- f_atan (void)
- {
- sp->u.real = atan(sp->u.real);
- }
- #endif
- #ifdef F_SQRT
- void
- f_sqrt (void)
- { float val;
- if(sp->type == T_NUMBER)
- val = (float) sp->u.number;
- else
- val = sp->u.real;
-
- if (val < 0.0) {
- error("math: sqrt(x) with (x < 0.0)\n");
- return;
- }
- sp->u.real = (float) sqrt(val);
- sp->type = T_REAL;
- }
- #endif
- #ifdef F_LOG
- void
- f_log (void)
- {
- if (sp->u.real <= 0.0) {
- error("math: log(x) with (x <= 0.0)\n");
- return;
- }
- sp->u.real = log(sp->u.real);
- }
- #endif
- #ifdef F_LOG10
- void
- f_log10 (void)
- {
- float val;
- if(sp->type == T_NUMBER)
- val = (float) sp->u.number;
- else
- val = sp->u.real;
- if (val <= 0.0) {
- error("math: log10(x) with (x <= 0.0)\n");
- return;
- }
- sp->u.real = log10(val);
- sp->type = T_REAL;
- }
- #endif
- #ifdef F_LOG2
- void
- f_log2 (void)
- {
- float val;
- if(sp->type == T_NUMBER)
- val = (float) sp->u.number;
- else
- val = sp->u.real;
- if (val <= 0.0) {
- error("math: log2(x) with (x <= 0.0)\n");
- return;
- }
- sp->u.real = (float) log2((double)val);
- sp->type = T_REAL;
- }
- #endif
- #ifdef F_POW
- void
- f_pow (void)
- {
- float val, val2;
-
- if((sp-1)->type == T_NUMBER)
- val = (float) (sp-1)->u.number;
- else
- val = (sp-1)->u.real;
-
- if(sp->type == T_NUMBER)
- val2 = (float) sp->u.number;
- else
- val2 = sp->u.real;
-
- (sp - 1)->u.real = pow(val, val2);
- sp--;
- sp->type = T_REAL;
- }
- #endif
- #ifdef F_EXP
- void
- f_exp (void)
- {
- sp->u.real = exp(sp->u.real);
- }
- #endif
- #ifdef F_FLOOR
- void
- f_floor (void)
- {
- sp->u.real = floor(sp->u.real);
- }
- #endif
- #ifdef F_CEIL
- void
- f_ceil (void)
- {
- sp->u.real = ceil(sp->u.real);
- }
- #endif
- #ifdef F_ROUND
- void f_round (void)
- {
- sp->u.real = (float) round(sp->u.real);
- }
- #endif
- #ifdef F_NORM
- /* The norm (magnitude) of a vector.
- Yes, you could use dotprod() below to implement norm(), but in the interest
- of speed, norm() has less cases.
- */
- static float norm(array_t *a) {
- int len = sp->u.arr->size;
- float total = 0.0;
- while(len-- > 0)
- if(a->item[len].type == T_NUMBER)
- total += SQUARE((float) a->item[len].u.number);
- else if(a->item[len].type == T_REAL)
- total += SQUARE(a->item[len].u.real);
- else {
- return -INT_MAX + 1;
- }
- return (float) sqrt(total);
- }
- void f_norm(void) {
- float val = norm(sp->u.arr);
- if(val == (-INT_MAX + 1)) {
- pop_stack();
- error("norm: invalid argument 1.\n");
- return;
- }
- pop_stack();
- push_real(val);
- }
- #endif
- #if defined(F_DOTPROD) | defined(F_DISTANCE) | defined(F_ANGLE)
- static float vector_op(array_t *a, array_t *b,
- float (*func)(const float, const float)) {
- int len = a->size;
- float total = 0.0;
- if(b->size != len) {
- return -INT_MAX;
- }
- while(len-- > 0) {
- if(b->item[len].type == T_NUMBER) {
- if(a->item[len].type == T_NUMBER)
- total += func((float) a->item[len].u.number,
- (float) b->item[len].u.number);
- else if(a->item[len].type == T_REAL)
- total += func(a->item[len].u.real,
- (float) b->item[len].u.number);
- else {
- return -INT_MAX + 1;
- }
- }
- else if(b->item[len].type == T_REAL) {
- if(a->item[len].type == T_NUMBER)
- total += func((float) a->item[len].u.number,
- b->item[len].u.real);
-
- else if(a->item[len].type == T_REAL)
- total += func(a->item[len].u.real, b->item[len].u.real);
- else {
- return -INT_MAX + 1;
- }
- }
- else {
- return -INT_MAX + 2;
- }
- }
-
- return total;
- }
- #endif
- #ifdef F_DOTPROD
- static float dotprod_mult(const float a, const float b) {
- return a * b;
- }
- /* dot product of two vectors */
- static float dotprod(array_t *a, array_t *b) {
- return vector_op(a, b, dotprod_mult);
- }
- void f_dotprod(void) {
- float total = vector_op((sp-1)->u.arr, sp->u.arr, dotprod_mult);
-
- if(total == -INT_MAX) {
- pop_2_elems();
- error("dotprod: cannot take the dotprod of vectors of different sizes.\n");
- return;
- }
- if((total == (-INT_MAX + 1)) || (total == (-INT_MAX + 2))) {
- pop_2_elems();
- error("dotprod: invalid arg %d.\n", (total + INT_MAX));
- return;
- }
-
- pop_2_elems();
- push_real(total);
- }
- #endif
- #ifdef F_DISTANCE
- static float distance_mult(const float a, const float b) {
- return SQUARE(b - a);
- }
- /* The (Euclidian) distance between two points */
- void f_distance(void) {
- float total = vector_op((sp-1)->u.arr, sp->u.arr, distance_mult);
-
- if(total == -INT_MAX) {
- pop_2_elems();
- error("distance: cannot take the distance of vectors of different sizes.\n");
- return;
- }
- if((total == (-INT_MAX + 1)) || (total == (-INT_MAX + 2))) {
- pop_2_elems();
- error("distance: invalid arg %d.\n", (total + INT_MAX));
- return;
- }
-
- pop_2_elems();
- push_real((float)sqrt(total));
- }
- #endif
- #ifdef F_ANGLE
- void f_angle(void) {
- float dot, norma, normb;
-
- dot = dotprod((sp-1)->u.arr, sp->u.arr);
-
- if(dot <= (-INT_MAX + 2)) {
- pop_2_elems();
- if(dot == -INT_MAX)
- error("angle: cannot calculate the angle between vectors of different sizes.\n");
- else
- error("angle: invalid arg %d.\n", (dot + INT_MAX));
- return;
- }
-
- norma = norm((sp-1)->u.arr);
-
- if(norma <= (-INT_MAX + 1)) {
- pop_2_elems();
- error("angle: invalid argument 1.\n");
- return;
- }
- normb = norm(sp->u.arr);
-
- if(normb <= (-INT_MAX + 1)) {
- pop_2_elems();
- error("angle: invalid argument 2.\n");
- return;
- }
- pop_2_elems();
- push_real((float)acos( dot / (norma * normb) ));
- }
- #endif