[루아2.1] Mathlib.c

5 분 소요

Mathlib.c

Mathlib.c 파일은 파일 이름이 안에 어떤 기능을 구현하고 있는지 설명하고 있습니다. 말그대로 수학 라이브러리를 구현한 파일입니다. 이 수학 라이브러리는 루아 구현에서 사용하는 것이 아니고 루아 언어에서 기본 라이브러리 형태로 제공합니다. 빌트인 라이브러리 혹은 빌트인 함수 정도로 생각하면 될것 같습니다.

#define PI          3.14159265358979323846
#define TODEGREE(a) ((a)*180.0/PI)
#define TORAD(a)    ((a)*PI/180.0)

원주율 파이는 미리 값을 써 놓고 씁니다. 그리고 360분법 각도 계산과 라디안 값 계산 매크로도 만들어 놨습니다.

static void math_abs (void)
{
 double d;
 lua_Object o = lua_getparam (1);
 if (o == LUA_NOOBJECT)
   lua_error ("too few arguments to function `abs'");
 if (!lua_isnumber(o))
   lua_error ("incorrect arguments to function `abs'");
 d = lua_getnumber(o);
 if (d < 0) d = -d;
 lua_pushnumber (d);
}

절대값 구하는 함수입니다. lua_getparam() 함수와 lua_getnumber() 함수는 루아 VM 스택에서 값을 가져오는 함수입니다. 그래서 실제 함수 본체는 에러 처리를 제외하고 마지막 부분에 if 문 하나입니다. 음수이면 -를 취해서 양수로 만들어주고 양수면 그대로 그 값을 다시 스택에 넣습니다. 이러면 루아 코드 입장에서는 리턴값으로 읽을 수 있습니다.

static void math_sin (void)
{
 double d;
 lua_Object o = lua_getparam (1);
 if (o == LUA_NOOBJECT)
   lua_error ("too few arguments to function `sin'");
 if (!lua_isnumber(o))
   lua_error ("incorrect arguments to function `sin'");
 d = lua_getnumber(o);
 lua_pushnumber (sin(TORAD(d)));
}

삼각함수 sin(x)를 구하는 함수입니다. 루아 스택에는 360분법 각도를 저장합니다. 그러면 C 표준 수학 라이브러리에서 sin() 함수를 호출합니다. 표준 수학 라이브러리의 sin() 함수는 라디안 값을 파라메터로 받으므로 라디안으로 값을 바꿔서 넘깁니다.

static void math_cos (void)
{
 double d;
 lua_Object o = lua_getparam (1);
 if (o == LUA_NOOBJECT)
   lua_error ("too few arguments to function `cos'");
 if (!lua_isnumber(o))
   lua_error ("incorrect arguments to function `cos'");
 d = lua_getnumber(o);
 lua_pushnumber (cos(TORAD(d)));
}

삼각함수 cos(x)를 구하는 함수입니다. 나머지는 sin() 함수와 같습니다.

static void math_tan (void)
{
 double d;
 lua_Object o = lua_getparam (1);
 if (o == LUA_NOOBJECT)
   lua_error ("too few arguments to function `tan'");
 if (!lua_isnumber(o))
   lua_error ("incorrect arguments to function `tan'");
 d = lua_getnumber(o);
 lua_pushnumber (tan(TORAD(d)));
}

삼각함수 tan(x)를 구하는 함수입니다. 역시 sin(), cos() 함수와 구현 패턴은 같습니다.

static void math_asin (void)
{
 double d;
 lua_Object o = lua_getparam (1);
 if (o == LUA_NOOBJECT)
   lua_error ("too few arguments to function `asin'");
 if (!lua_isnumber(o))
   lua_error ("incorrect arguments to function `asin'");
 d = lua_getnumber(o);
 lua_pushnumber (TODEGREE(asin(d)));
}


static void math_acos (void)
{
 double d;
 lua_Object o = lua_getparam (1);
 if (o == LUA_NOOBJECT)
   lua_error ("too few arguments to function `acos'");
 if (!lua_isnumber(o))
   lua_error ("incorrect arguments to function `acos'");
 d = lua_getnumber(o);
 lua_pushnumber (TODEGREE(acos(d)));
}



static void math_atan (void)
{
 double d;
 lua_Object o = lua_getparam (1);
 if (o == LUA_NOOBJECT)
   lua_error ("too few arguments to function `atan'");
 if (!lua_isnumber(o))
   lua_error ("incorrect arguments to function `atan'");
 d = lua_getnumber(o);
 lua_pushnumber (TODEGREE(atan(d)));
}

역시 삼각함수 처리 코드와 같은 패턴으로 구현한 역삼각함수 구현입니다. 표준 수학 라이브러리의 역삼각함수는 리턴값이 라디안이므로 이 값을 360분법 값으로 바꾼다음 루아 스택에 다시 넣습니다.

static void math_ceil (void)
{
 double d;
 lua_Object o = lua_getparam (1);
 if (o == LUA_NOOBJECT)
   lua_error ("too few arguments to function `ceil'");
 if (!lua_isnumber(o))
   lua_error ("incorrect arguments to function `ceil'");
 d = lua_getnumber(o);
 lua_pushnumber (ceil(d));
}


static void math_floor (void)
{
 double d;
 lua_Object o = lua_getparam (1);
 if (o == LUA_NOOBJECT)
   lua_error ("too few arguments to function `floor'");
 if (!lua_isnumber(o))
   lua_error ("incorrect arguments to function `floor'");
 d = lua_getnumber(o);
 lua_pushnumber (floor(d));
}

ceil, floor 함수 역시 삼각함수와 같은 패턴으로 C 표준 수학 라이브러리 함수를 그대로 사용하는 랩퍼입니다. ceil 함수는 가장 가까운 정수로 올림, floor 함수는 가장 가까운 정수로 내리는 함수입니다.

static void math_mod (void)
{
 int d1, d2;
 lua_Object o1 = lua_getparam (1);
 lua_Object o2 = lua_getparam (2);
 if (!lua_isnumber(o1) || !lua_isnumber(o2))
   lua_error ("incorrect arguments to function `mod'");
 d1 = (int) lua_getnumber(o1);
 d2 = (int) lua_getnumber(o2);
 lua_pushnumber (d1%d2);
}

그냥 나머지 연산하는 함수입니다. 어? 루아에 나머지 연산자가 없던가요? 다시 코드를 보겠습니다. 진짜 없네요. 연산자 하나 만드는 것 별거 아닐텐데 굳이 연산자로 만들지 않고 라이브러리 형태로 나머지 연산을 처리하는 이유가 궁금하군요.

static void math_sqrt (void)
{
 double d;
 lua_Object o = lua_getparam (1);
 if (o == LUA_NOOBJECT)
   lua_error ("too few arguments to function `sqrt'");
 if (!lua_isnumber(o))
   lua_error ("incorrect arguments to function `sqrt'");
 d = lua_getnumber(o);
 lua_pushnumber (sqrt(d));
}

다시 C 표준 수학 라이브러리 함수 랩퍼입니다.

static int old_pow;

static void math_pow (void)
{
 lua_Object o1 = lua_getparam (1);
 lua_Object o2 = lua_getparam (2);
 lua_Object op = lua_getparam(3);
 if (!lua_isnumber(o1) || !lua_isnumber(o2) || *(lua_getstring(op)) != 'p')
 {
   lua_Object old = lua_getlocked(old_pow);
   lua_pushobject(o1);
   lua_pushobject(o2);
   lua_pushobject(op);
   if (lua_callfunction(old) != 0)
     lua_error(NULL);
 }
 else
 {
   double d1 = lua_getnumber(o1);
   double d2 = lua_getnumber(o2);
   lua_pushnumber (pow(d1,d2));
 }
}

스택에서 파라메터를 읽어 그 타입이 number가 아니면 루아로 구현한 제곱 함수를 호출하고 number면 C 표준 수학 라이브러리 함수를 호출합니다.

static void math_min (void)
{
 int i=1;
 double d, dmin;
 lua_Object o;
 if ((o = lua_getparam(i++)) == LUA_NOOBJECT)
   lua_error ("too few arguments to function `min'");
 if (!lua_isnumber(o))
   lua_error ("incorrect arguments to function `min'");
 dmin = lua_getnumber (o);
 while ((o = lua_getparam(i++)) != LUA_NOOBJECT)
 {
  if (!lua_isnumber(o))
    lua_error ("incorrect arguments to function `min'");
  d = lua_getnumber (o);
  if (d < dmin) dmin = d;
 }
 lua_pushnumber (dmin);
}


static void math_max (void)
{
 int i=1;
 double d, dmax;
 lua_Object o;
 if ((o = lua_getparam(i++)) == LUA_NOOBJECT)
   lua_error ("too few arguments to function `max'");
 if (!lua_isnumber(o))
   lua_error ("incorrect arguments to function `max'");
 dmax = lua_getnumber (o);
 while ((o = lua_getparam(i++)) != LUA_NOOBJECT)
 {
  if (!lua_isnumber(o))
    lua_error ("incorrect arguments to function `max'");
  d = lua_getnumber (o);
  if (d > dmax) dmax = d;
 }
 lua_pushnumber (dmax);
}

최대값, 최소값 구하는 함수입니다. 파라메터를 하나씩 읽으면서 계속 작은 값 혹은 큰 값을 dmin과 dmax에 저장해 놓고 마지막에 dmin 혹은 dmax를 루아 스택에 넣어서 리턴합니다.

static void math_log (void)
{
 double d;
 lua_Object o = lua_getparam (1);
 if (o == LUA_NOOBJECT)
   lua_error ("too few arguments to function `log'");
 if (!lua_isnumber(o))
   lua_error ("incorrect arguments to function `log'");
 d = lua_getnumber(o);
 lua_pushnumber (log(d));
}


static void math_log10 (void)
{
 double d;
 lua_Object o = lua_getparam (1);
 if (o == LUA_NOOBJECT)
   lua_error ("too few arguments to function `log10'");
 if (!lua_isnumber(o))
   lua_error ("incorrect arguments to function `log10'");
 d = lua_getnumber(o);
 lua_pushnumber (log10(d));
}


static void math_exp (void)
{
 double d;
 lua_Object o = lua_getparam (1);
 if (o == LUA_NOOBJECT)
   lua_error ("too few arguments to function `exp'");
 if (!lua_isnumber(o))
   lua_error ("incorrect arguments to function `exp'");
 d = lua_getnumber(o);
 lua_pushnumber (exp(d));

역시 C 표준 수학 라이브러리 함수 log(), log10(), exp() 함수에 대한 랩퍼입니다.

static void math_deg (void)
{
 float d;
 lua_Object o = lua_getparam (1);
 if (o == LUA_NOOBJECT)
   lua_error ("too few arguments to function `deg'");
 if (!lua_isnumber(o))
   lua_error ("incorrect arguments to function `deg'");
 d = lua_getnumber(o);
 lua_pushnumber (d*180./PI);
}

static void math_rad (void)
{
 float d;
 lua_Object o = lua_getparam (1);
 if (o == LUA_NOOBJECT)
   lua_error ("too few arguments to function `rad'");
 if (!lua_isnumber(o))
   lua_error ("incorrect arguments to function `rad'");
 d = lua_getnumber(o);
 lua_pushnumber (d/180.*PI);
}

각도 변환 함수입니다. 맨 위에 매크로를 만들어 놓고 같은 코드를 왜 또 작성했을까요? 그냥 매크로 호출하면 될 것같은데.. 명백한 코드 중복입니다.

댓글남기기