2014-06-08 12:46:04 +00:00
|
|
|
/*
|
|
|
|
* $Id$
|
|
|
|
*
|
2019-06-06 05:28:23 +00:00
|
|
|
Copyright (c) 2006-2019 Chung, Hyung-Hwan. All rights reserved.
|
2014-06-08 12:46:04 +00:00
|
|
|
|
2014-11-19 14:42:24 +00:00
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in the
|
|
|
|
documentation and/or other materials provided with the distribution.
|
2014-06-08 12:46:04 +00:00
|
|
|
|
2014-11-19 14:42:24 +00:00
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
|
|
|
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
|
|
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
|
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
2014-06-08 12:46:04 +00:00
|
|
|
*/
|
|
|
|
|
2014-10-19 12:50:51 +00:00
|
|
|
#include "mod-math.h"
|
2014-06-08 12:46:04 +00:00
|
|
|
#include <qse/cmn/str.h>
|
|
|
|
#include <qse/cmn/chr.h>
|
2014-07-11 07:42:28 +00:00
|
|
|
#include <qse/cmn/alg.h>
|
|
|
|
#include <qse/cmn/time.h>
|
2016-04-29 03:55:42 +00:00
|
|
|
#include "../cmn/mem-prv.h"
|
2014-06-08 12:46:04 +00:00
|
|
|
#include "fnc.h"
|
|
|
|
|
2014-06-30 02:30:49 +00:00
|
|
|
#include <math.h>
|
|
|
|
#if defined(HAVE_QUADMATH_H)
|
|
|
|
# include <quadmath.h>
|
2018-10-29 08:45:31 +00:00
|
|
|
#elif defined(QSE_USE_AWK_FLTMAX) && (QSE_AWK_SIZEOF_FLT_T == 16) && defined(QSE_FLTMAX_REQUIRE_QUADMATH)
|
2015-11-03 13:40:56 +00:00
|
|
|
# error QUADMATH.H NOT AVAILABLE or NOT COMPILABLE
|
2014-06-30 02:30:49 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined(QSE_HAVE_CONFIG_H)
|
|
|
|
# if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
|
|
|
|
# define HAVE_CEIL
|
|
|
|
# define HAVE_FLOOR
|
2015-03-20 13:53:54 +00:00
|
|
|
# if !defined(__WATCOMC__) && !defined(__BORLANDC__)
|
2014-06-30 02:30:49 +00:00
|
|
|
# define HAVE_ROUND
|
2014-10-19 12:50:51 +00:00
|
|
|
# endif
|
2014-06-30 02:30:49 +00:00
|
|
|
# define HAVE_SINH
|
|
|
|
# define HAVE_COSH
|
|
|
|
# define HAVE_TANH
|
|
|
|
# define HAVE_ASIN
|
|
|
|
# define HAVE_ACOS
|
2014-07-01 15:27:27 +00:00
|
|
|
|
|
|
|
# define HAVE_SIN
|
|
|
|
# define HAVE_COS
|
|
|
|
# define HAVE_TAN
|
|
|
|
# define HAVE_ATAN
|
|
|
|
# define HAVE_ATAN2
|
|
|
|
# define HAVE_LOG
|
|
|
|
# define HAVE_LOG10
|
|
|
|
# define HAVE_EXP
|
|
|
|
# define HAVE_SQRT
|
2014-06-30 02:30:49 +00:00
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
2014-07-11 07:42:28 +00:00
|
|
|
typedef struct modctx_t
|
|
|
|
{
|
|
|
|
qse_awk_int_t seed;
|
|
|
|
qse_awk_uint_t prand; /* last random value returned */
|
|
|
|
} modctx_t;
|
|
|
|
|
2014-07-01 15:27:27 +00:00
|
|
|
static int fnc_math_1 (
|
|
|
|
qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi, qse_awk_math1_t f)
|
|
|
|
{
|
|
|
|
qse_size_t nargs;
|
|
|
|
qse_awk_val_t* a0;
|
|
|
|
qse_awk_flt_t rv;
|
|
|
|
qse_awk_val_t* r;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
nargs = qse_awk_rtx_getnargs (rtx);
|
|
|
|
QSE_ASSERT (nargs == 1);
|
|
|
|
|
|
|
|
a0 = qse_awk_rtx_getarg (rtx, 0);
|
|
|
|
|
|
|
|
n = qse_awk_rtx_valtoflt (rtx, a0, &rv);
|
|
|
|
if (n <= -1) return -1;
|
|
|
|
|
|
|
|
r = qse_awk_rtx_makefltval (rtx, f (qse_awk_rtx_getawk(rtx), rv));
|
|
|
|
if (r == QSE_NULL) return -1;
|
|
|
|
|
|
|
|
qse_awk_rtx_setretval (rtx, r);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fnc_math_2 (
|
|
|
|
qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi, qse_awk_math2_t f)
|
|
|
|
{
|
|
|
|
qse_size_t nargs;
|
|
|
|
qse_awk_val_t* a0, * a1;
|
|
|
|
qse_awk_flt_t rv0, rv1;
|
|
|
|
qse_awk_val_t* r;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
nargs = qse_awk_rtx_getnargs (rtx);
|
|
|
|
QSE_ASSERT (nargs == 2);
|
|
|
|
|
|
|
|
a0 = qse_awk_rtx_getarg (rtx, 0);
|
|
|
|
a1 = qse_awk_rtx_getarg (rtx, 1);
|
|
|
|
|
|
|
|
n = qse_awk_rtx_valtoflt (rtx, a0, &rv0);
|
|
|
|
if (n <= -1) return -1;
|
|
|
|
|
|
|
|
n = qse_awk_rtx_valtoflt (rtx, a1, &rv1);
|
|
|
|
if (n <= -1) return -1;
|
|
|
|
|
|
|
|
r = qse_awk_rtx_makefltval (rtx, f (qse_awk_rtx_getawk(rtx), rv0, rv1));
|
|
|
|
if (r == QSE_NULL) return -1;
|
|
|
|
|
|
|
|
qse_awk_rtx_setretval (rtx, r);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-30 02:30:49 +00:00
|
|
|
static qse_awk_flt_t math_ceil (qse_awk_t* awk, qse_awk_flt_t x)
|
|
|
|
{
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX) && defined(HAVE_CEILQ)
|
|
|
|
return ceilq (x);
|
|
|
|
#elif defined(HAVE_CEILL) && (QSE_SIZEOF_LONG_DOUBLE > QSE_SIZEOF_DOUBLE)
|
|
|
|
return ceill (x);
|
|
|
|
#elif defined(HAVE_CEIL)
|
|
|
|
return ceil (x);
|
|
|
|
#elif defined(HAVE_CEILF)
|
|
|
|
return ceilf (x);
|
|
|
|
#else
|
|
|
|
#error ### no ceil function available ###
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static qse_awk_flt_t math_floor (qse_awk_t* awk, qse_awk_flt_t x)
|
|
|
|
{
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX) && defined(HAVE_FLOORQ)
|
|
|
|
return floorq (x);
|
|
|
|
#elif defined(HAVE_FLOORL) && (QSE_SIZEOF_LONG_DOUBLE > QSE_SIZEOF_DOUBLE)
|
|
|
|
return floorl (x);
|
|
|
|
#elif defined(HAVE_FLOOR)
|
|
|
|
return floor (x);
|
|
|
|
#elif defined(HAVE_FLOORF)
|
|
|
|
return floorf (x);
|
|
|
|
#else
|
|
|
|
#error ### no floor function available ###
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static qse_awk_flt_t math_round (qse_awk_t* awk, qse_awk_flt_t x)
|
|
|
|
{
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX) && defined(HAVE_ROUNDQ)
|
|
|
|
return roundq (x);
|
|
|
|
#elif defined(HAVE_ROUNDL) && (QSE_SIZEOF_LONG_DOUBLE > QSE_SIZEOF_DOUBLE)
|
|
|
|
return roundl (x);
|
|
|
|
#elif defined(HAVE_ROUND)
|
|
|
|
return round (x);
|
|
|
|
#elif defined(HAVE_ROUNDF)
|
|
|
|
return roundf (x);
|
|
|
|
#else
|
2014-09-03 04:08:28 +00:00
|
|
|
|
|
|
|
qse_flt_t f, d;
|
|
|
|
|
|
|
|
f = math_floor (awk, x);
|
|
|
|
d = x - f; /* get fraction */
|
|
|
|
|
|
|
|
if (d > (qse_awk_flt_t)0.5)
|
|
|
|
{
|
|
|
|
/* round up to the nearest */
|
|
|
|
f = f + (qse_awk_flt_t)1.0;
|
|
|
|
}
|
|
|
|
else if (d == (qse_awk_flt_t)0.5)
|
|
|
|
{
|
|
|
|
#if 1
|
|
|
|
/* round half away from zero */
|
|
|
|
if (x >= 0)
|
|
|
|
{
|
|
|
|
f = x + (qse_awk_flt_t)0.5;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
f = x - (qse_awk_flt_t)0.5;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
/* round half to even - C99's rint() does this, i guess. */
|
|
|
|
d = f - (qse_awk_flt_t)2.0 * math_floor(awk, f * (qse_awk_flt_t)0.5);
|
|
|
|
if (d == (qse_awk_flt_t)1.0) f = f + (qse_awk_flt_t)1.0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* this implementation doesn't keep the signbit for -0.0.
|
|
|
|
* The signbit() function defined in C99 may get used to
|
|
|
|
* preserve the sign bit. but this is a fall-back rountine
|
|
|
|
* for a system without round also defined in C99.
|
|
|
|
* don't get annoyed by the lost sign bit for the value of 0.0.
|
|
|
|
*/
|
|
|
|
|
|
|
|
return f;
|
|
|
|
|
2014-06-30 02:30:49 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static qse_awk_flt_t math_sinh (qse_awk_t* awk, qse_awk_flt_t x)
|
|
|
|
{
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX) && defined(HAVE_SINHQ)
|
|
|
|
return sinhq (x);
|
|
|
|
#elif defined(HAVE_SINHL) && (QSE_SIZEOF_LONG_DOUBLE > QSE_SIZEOF_DOUBLE)
|
|
|
|
return sinhl (x);
|
|
|
|
#elif defined(HAVE_SINH)
|
|
|
|
return sinh (x);
|
|
|
|
#elif defined(HAVE_SINHF)
|
|
|
|
return sinhf (x);
|
|
|
|
#else
|
|
|
|
#error ### no sinh function available ###
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static qse_awk_flt_t math_cosh (qse_awk_t* awk, qse_awk_flt_t x)
|
|
|
|
{
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX) && defined(HAVE_COSHQ)
|
|
|
|
return coshq (x);
|
|
|
|
#elif defined(HAVE_COSHL) && (QSE_SIZEOF_LONG_DOUBLE > QSE_SIZEOF_DOUBLE)
|
|
|
|
return coshl (x);
|
|
|
|
#elif defined(HAVE_COSH)
|
|
|
|
return cosh (x);
|
|
|
|
#elif defined(HAVE_COSHF)
|
|
|
|
return coshf (x);
|
|
|
|
#else
|
|
|
|
#error ### no cosh function available ###
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static qse_awk_flt_t math_tanh (qse_awk_t* awk, qse_awk_flt_t x)
|
|
|
|
{
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX) && defined(HAVE_TANHQ)
|
|
|
|
return tanhq (x);
|
|
|
|
#elif defined(HAVE_TANHL) && (QSE_SIZEOF_LONG_DOUBLE > QSE_SIZEOF_DOUBLE)
|
|
|
|
return tanhl (x);
|
|
|
|
#elif defined(HAVE_TANH)
|
|
|
|
return tanh (x);
|
|
|
|
#elif defined(HAVE_TANHF)
|
|
|
|
return tanhf (x);
|
|
|
|
#else
|
|
|
|
#error ### no tanh function available ###
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static qse_awk_flt_t math_asin (qse_awk_t* awk, qse_awk_flt_t x)
|
|
|
|
{
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX) && defined(HAVE_ASINQ)
|
|
|
|
return asinq (x);
|
|
|
|
#elif defined(HAVE_ASINL) && (QSE_SIZEOF_LONG_DOUBLE > QSE_SIZEOF_DOUBLE)
|
|
|
|
return asinl (x);
|
|
|
|
#elif defined(HAVE_ASIN)
|
|
|
|
return asin (x);
|
|
|
|
#elif defined(HAVE_ASINF)
|
|
|
|
return asinf (x);
|
|
|
|
#else
|
|
|
|
#error ### no asin function available ###
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static qse_awk_flt_t math_acos (qse_awk_t* awk, qse_awk_flt_t x)
|
|
|
|
{
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX) && defined(HAVE_ACOSQ)
|
|
|
|
return acosq (x);
|
|
|
|
#elif defined(HAVE_ACOSL) && (QSE_SIZEOF_LONG_DOUBLE > QSE_SIZEOF_DOUBLE)
|
|
|
|
return acosl (x);
|
|
|
|
#elif defined(HAVE_ACOS)
|
|
|
|
return acos (x);
|
|
|
|
#elif defined(HAVE_ACOSF)
|
|
|
|
return acosf (x);
|
|
|
|
#else
|
|
|
|
#error ### no acos function available ###
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-07-01 15:27:27 +00:00
|
|
|
/* ----------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
|
|
static qse_awk_flt_t math_sin (qse_awk_t* awk, qse_awk_flt_t x)
|
|
|
|
{
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX) && defined(HAVE_SINQ)
|
|
|
|
return sinq (x);
|
|
|
|
#elif defined(HAVE_SINL) && (QSE_SIZEOF_LONG_DOUBLE > QSE_SIZEOF_DOUBLE)
|
|
|
|
return sinl (x);
|
|
|
|
#elif defined(HAVE_SIN)
|
|
|
|
return sin (x);
|
|
|
|
#elif defined(HAVE_SINF)
|
|
|
|
return sinf (x);
|
|
|
|
#else
|
|
|
|
#error ### no sin function available ###
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static qse_awk_flt_t math_cos (qse_awk_t* awk, qse_awk_flt_t x)
|
|
|
|
{
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX) && defined(HAVE_COSQ)
|
|
|
|
return cosq (x);
|
|
|
|
#elif defined(HAVE_COSL) && (QSE_SIZEOF_LONG_DOUBLE > QSE_SIZEOF_DOUBLE)
|
|
|
|
return cosl (x);
|
|
|
|
#elif defined(HAVE_COS)
|
|
|
|
return cos (x);
|
|
|
|
#elif defined(HAVE_COSF)
|
|
|
|
return cosf (x);
|
|
|
|
#else
|
|
|
|
#error ### no cos function available ###
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static qse_awk_flt_t math_tan (qse_awk_t* awk, qse_awk_flt_t x)
|
|
|
|
{
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX) && defined(HAVE_TANQ)
|
|
|
|
return tanq (x);
|
|
|
|
#elif defined(HAVE_TANL) && (QSE_SIZEOF_LONG_DOUBLE > QSE_SIZEOF_DOUBLE)
|
|
|
|
return tanl (x);
|
|
|
|
#elif defined(HAVE_TAN)
|
|
|
|
return tan (x);
|
|
|
|
#elif defined(HAVE_TANF)
|
|
|
|
return tanf (x);
|
|
|
|
#else
|
|
|
|
#error ### no tan function available ###
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static qse_awk_flt_t math_atan (qse_awk_t* awk, qse_awk_flt_t x)
|
|
|
|
{
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX) && defined(HAVE_ATANQ)
|
|
|
|
return atanq (x);
|
|
|
|
#elif defined(HAVE_ATANL) && (QSE_SIZEOF_LONG_DOUBLE > QSE_SIZEOF_DOUBLE)
|
|
|
|
return atanl (x);
|
|
|
|
#elif defined(HAVE_ATAN)
|
|
|
|
return atan (x);
|
|
|
|
#elif defined(HAVE_ATANF)
|
|
|
|
return atanf (x);
|
|
|
|
#else
|
|
|
|
#error ### no atan function available ###
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static qse_awk_flt_t math_atan2 (qse_awk_t* awk, qse_awk_flt_t x, qse_awk_flt_t y)
|
|
|
|
{
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX) && defined(HAVE_ATAN2Q)
|
|
|
|
return atan2q (x, y);
|
|
|
|
#elif defined(HAVE_ATAN2L) && (QSE_SIZEOF_LONG_DOUBLE > QSE_SIZEOF_DOUBLE)
|
|
|
|
return atan2l (x, y);
|
|
|
|
#elif defined(HAVE_ATAN2)
|
|
|
|
return atan2 (x, y);
|
|
|
|
#elif defined(HAVE_ATAN2F)
|
|
|
|
return atan2f (x, y);
|
|
|
|
#else
|
|
|
|
#error ### no atan2 function available ###
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2017-09-16 09:48:00 +00:00
|
|
|
static QSE_INLINE qse_awk_flt_t math_log (qse_awk_t* awk, qse_awk_flt_t x)
|
2014-07-01 15:27:27 +00:00
|
|
|
{
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX) && defined(HAVE_LOGQ)
|
|
|
|
return logq (x);
|
|
|
|
#elif defined(HAVE_LOGL) && (QSE_SIZEOF_LONG_DOUBLE > QSE_SIZEOF_DOUBLE)
|
|
|
|
return logl (x);
|
|
|
|
#elif defined(HAVE_LOG)
|
|
|
|
return log (x);
|
|
|
|
#elif defined(HAVE_LOGF)
|
|
|
|
return logf (x);
|
|
|
|
#else
|
|
|
|
#error ### no log function available ###
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2017-09-16 07:01:03 +00:00
|
|
|
static qse_awk_flt_t math_log2 (qse_awk_t* awk, qse_awk_flt_t x)
|
|
|
|
{
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX) && defined(HAVE_LOG2Q)
|
|
|
|
return log2q (x);
|
|
|
|
#elif defined(HAVE_LOG2L) && (QSE_SIZEOF_LONG_DOUBLE > QSE_SIZEOF_DOUBLE)
|
|
|
|
return log2l (x);
|
|
|
|
#elif defined(HAVE_LOG2)
|
|
|
|
return log2 (x);
|
|
|
|
#elif defined(HAVE_LOG2F)
|
|
|
|
return log2f (x);
|
|
|
|
#else
|
2017-09-16 10:41:25 +00:00
|
|
|
return math_log(awk, x) / math_log(awk, 2.0);
|
2017-09-16 07:01:03 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-07-01 15:27:27 +00:00
|
|
|
static qse_awk_flt_t math_log10 (qse_awk_t* awk, qse_awk_flt_t x)
|
|
|
|
{
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX) && defined(HAVE_LOG10Q)
|
|
|
|
return log10q (x);
|
|
|
|
#elif defined(HAVE_LOG10L) && (QSE_SIZEOF_LONG_DOUBLE > QSE_SIZEOF_DOUBLE)
|
|
|
|
return log10l (x);
|
|
|
|
#elif defined(HAVE_LOG10)
|
|
|
|
return log10 (x);
|
|
|
|
#elif defined(HAVE_LOG10F)
|
|
|
|
return log10f (x);
|
|
|
|
#else
|
|
|
|
#error ### no log10 function available ###
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static qse_awk_flt_t math_exp (qse_awk_t* awk, qse_awk_flt_t x)
|
|
|
|
{
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX) && defined(HAVE_EXPQ)
|
|
|
|
return expq (x);
|
|
|
|
#elif defined(HAVE_EXPL) && (QSE_SIZEOF_LONG_DOUBLE > QSE_SIZEOF_DOUBLE)
|
|
|
|
return expl (x);
|
|
|
|
#elif defined(HAVE_EXP)
|
|
|
|
return exp (x);
|
|
|
|
#elif defined(HAVE_EXPF)
|
|
|
|
return expf (x);
|
|
|
|
#else
|
|
|
|
#error ### no exp function available ###
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static qse_awk_flt_t math_sqrt (qse_awk_t* awk, qse_awk_flt_t x)
|
|
|
|
{
|
|
|
|
#if defined(QSE_USE_AWK_FLTMAX) && defined(HAVE_SQRTQ)
|
|
|
|
return sqrtq (x);
|
|
|
|
#elif defined(HAVE_SQRTL) && (QSE_SIZEOF_LONG_DOUBLE > QSE_SIZEOF_DOUBLE)
|
|
|
|
return sqrtl (x);
|
|
|
|
#elif defined(HAVE_SQRT)
|
|
|
|
return sqrt (x);
|
|
|
|
#elif defined(HAVE_SQRTF)
|
|
|
|
return sqrtf (x);
|
|
|
|
#else
|
|
|
|
#error ### no sqrt function available ###
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
|
|
|
2014-06-30 02:30:49 +00:00
|
|
|
static int fnc_ceil (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
2014-07-01 15:27:27 +00:00
|
|
|
return fnc_math_1 (rtx, fi, math_ceil);
|
2014-06-30 02:30:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int fnc_floor (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
2014-07-01 15:27:27 +00:00
|
|
|
return fnc_math_1 (rtx, fi, math_floor);
|
2014-06-30 02:30:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int fnc_round (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
2014-07-01 15:27:27 +00:00
|
|
|
return fnc_math_1 (rtx, fi, math_round);
|
2014-06-30 02:30:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int fnc_sinh (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
2014-07-01 15:27:27 +00:00
|
|
|
return fnc_math_1 (rtx, fi, math_sinh);
|
2014-06-30 02:30:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int fnc_cosh (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
2014-07-01 15:27:27 +00:00
|
|
|
return fnc_math_1 (rtx, fi, math_cosh);
|
2014-06-30 02:30:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int fnc_tanh (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
2014-07-01 15:27:27 +00:00
|
|
|
return fnc_math_1 (rtx, fi, math_tanh);
|
2014-06-30 02:30:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int fnc_asin (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
2014-07-01 15:27:27 +00:00
|
|
|
return fnc_math_1 (rtx, fi, math_asin);
|
2014-06-30 02:30:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int fnc_acos (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
2014-07-01 15:27:27 +00:00
|
|
|
return fnc_math_1 (rtx, fi, math_acos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ----------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
static int fnc_sin (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
|
|
|
return fnc_math_1 (rtx, fi, math_sin);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fnc_cos (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
|
|
|
return fnc_math_1 (rtx, fi, math_cos);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fnc_tan (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
|
|
|
return fnc_math_1 (rtx, fi, math_tan);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fnc_atan (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
|
|
|
return fnc_math_1 (rtx, fi, math_atan);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fnc_atan2 (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
|
|
|
return fnc_math_2 (rtx, fi, math_atan2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fnc_log (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
|
|
|
return fnc_math_1 (rtx, fi, math_log);
|
|
|
|
}
|
|
|
|
|
2017-09-16 07:01:03 +00:00
|
|
|
static int fnc_log2 (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
|
|
|
return fnc_math_1 (rtx, fi, math_log2);
|
|
|
|
}
|
|
|
|
|
2014-07-01 15:27:27 +00:00
|
|
|
static int fnc_log10 (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
|
|
|
return fnc_math_1 (rtx, fi, math_log10);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fnc_exp (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
|
|
|
return fnc_math_1 (rtx, fi, math_exp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fnc_sqrt (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
|
|
|
return fnc_math_1 (rtx, fi, math_sqrt);
|
2014-06-30 02:30:49 +00:00
|
|
|
}
|
|
|
|
|
2014-07-11 07:42:28 +00:00
|
|
|
/* ----------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
static int fnc_rand (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
|
|
|
#define RANDV_MAX QSE_TYPE_MAX(qse_awk_int_t)
|
|
|
|
qse_awk_val_t* r;
|
|
|
|
qse_awk_int_t randv;
|
|
|
|
modctx_t* modctx;
|
|
|
|
|
|
|
|
modctx = (modctx_t*)fi->mod->ctx;
|
|
|
|
|
|
|
|
#if defined(QSE_USE_AWK_INTMAX)
|
|
|
|
modctx->prand = qse_randxsuintmax (modctx->prand);
|
|
|
|
#else
|
|
|
|
modctx->prand = qse_randxsulong (modctx->prand);
|
|
|
|
#endif
|
|
|
|
randv = modctx->prand % RANDV_MAX;
|
|
|
|
|
|
|
|
r = qse_awk_rtx_makefltval (rtx, (qse_awk_flt_t)randv / RANDV_MAX);
|
|
|
|
if (r == QSE_NULL) return -1;
|
|
|
|
|
|
|
|
qse_awk_rtx_setretval (rtx, r);
|
|
|
|
return 0;
|
|
|
|
#undef RANDV_MAX
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fnc_srand (qse_awk_rtx_t* rtx, const qse_awk_fnc_info_t* fi)
|
|
|
|
{
|
|
|
|
qse_size_t nargs;
|
|
|
|
qse_awk_val_t* a0;
|
|
|
|
qse_awk_int_t lv;
|
|
|
|
qse_awk_val_t* r;
|
|
|
|
int n;
|
|
|
|
qse_awk_int_t prev;
|
|
|
|
qse_ntime_t now;
|
|
|
|
modctx_t* modctx;
|
|
|
|
|
|
|
|
modctx = (modctx_t*)fi->mod->ctx;
|
|
|
|
nargs = qse_awk_rtx_getnargs (rtx);
|
|
|
|
QSE_ASSERT (nargs == 0 || nargs == 1);
|
|
|
|
|
|
|
|
prev = modctx->seed;
|
|
|
|
|
|
|
|
if (nargs <= 0)
|
|
|
|
{
|
|
|
|
modctx->seed = (qse_gettime (&now) <= -1)?
|
|
|
|
(modctx->seed * modctx->seed): ((qse_awk_int_t)now.sec + (qse_awk_int_t)now.nsec);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
a0 = qse_awk_rtx_getarg (rtx, 0);
|
|
|
|
n = qse_awk_rtx_valtoint (rtx, a0, &lv);
|
|
|
|
if (n <= -1) return -1;
|
|
|
|
modctx->seed = lv;
|
|
|
|
}
|
|
|
|
/* i don't care if the seed becomes negative or overflows.
|
|
|
|
* i just convert the signed value to the unsigned one. */
|
|
|
|
modctx->prand = (qse_awk_uint_t)(modctx->seed * modctx->seed * modctx->seed);
|
|
|
|
/* make sure that the actual seeding is not 0 */
|
|
|
|
if (modctx->prand == 0) modctx->prand++;
|
|
|
|
|
|
|
|
r = qse_awk_rtx_makeintval (rtx, prev);
|
|
|
|
if (r == QSE_NULL) return -1;
|
|
|
|
|
|
|
|
qse_awk_rtx_setretval (rtx, r);
|
|
|
|
return 0;
|
|
|
|
}
|
2014-06-30 02:30:49 +00:00
|
|
|
|
2014-07-01 15:27:27 +00:00
|
|
|
/* ----------------------------------------------------------------------- */
|
2014-06-30 02:30:49 +00:00
|
|
|
|
2014-06-08 12:46:04 +00:00
|
|
|
typedef struct fnctab_t fnctab_t;
|
|
|
|
struct fnctab_t
|
|
|
|
{
|
|
|
|
const qse_char_t* name;
|
|
|
|
qse_awk_mod_sym_fnc_t info;
|
|
|
|
};
|
|
|
|
|
|
|
|
static fnctab_t fnctab[] =
|
|
|
|
{
|
|
|
|
/* keep this table sorted for binary search in query(). */
|
2014-07-01 15:27:27 +00:00
|
|
|
{ QSE_T("acos"), { { 1, 1, QSE_NULL }, fnc_acos, 0 } },
|
|
|
|
{ QSE_T("asin"), { { 1, 1, QSE_NULL }, fnc_asin, 0 } },
|
|
|
|
{ QSE_T("atan"), { { 1, 1, QSE_NULL }, fnc_atan, 0 } },
|
|
|
|
{ QSE_T("atan2"), { { 2, 2, QSE_NULL }, fnc_atan2, 0 } },
|
|
|
|
{ QSE_T("ceil"), { { 1, 1, QSE_NULL }, fnc_ceil, 0 } },
|
|
|
|
{ QSE_T("cos"), { { 1, 1, QSE_NULL }, fnc_cos, 0 } },
|
|
|
|
{ QSE_T("cosh"), { { 1, 1, QSE_NULL }, fnc_cosh, 0 } },
|
|
|
|
{ QSE_T("exp"), { { 1, 1, QSE_NULL }, fnc_exp, 0 } },
|
|
|
|
{ QSE_T("floor"), { { 1, 1, QSE_NULL }, fnc_floor, 0 } },
|
|
|
|
{ QSE_T("log"), { { 1, 1, QSE_NULL }, fnc_log, 0 } },
|
|
|
|
{ QSE_T("log10"), { { 1, 1, QSE_NULL }, fnc_log10, 0 } },
|
2017-09-16 07:01:03 +00:00
|
|
|
{ QSE_T("log2"), { { 1, 1, QSE_NULL }, fnc_log2, 0 } },
|
2014-07-11 07:42:28 +00:00
|
|
|
{ QSE_T("rand"), { { 0, 0, QSE_NULL }, fnc_rand, 0 } },
|
2014-07-01 15:27:27 +00:00
|
|
|
{ QSE_T("round"), { { 1, 1, QSE_NULL }, fnc_round, 0 } },
|
|
|
|
{ QSE_T("sin"), { { 1, 1, QSE_NULL }, fnc_sin, 0 } },
|
|
|
|
{ QSE_T("sinh"), { { 1, 1, QSE_NULL }, fnc_sinh, 0 } },
|
|
|
|
{ QSE_T("sqrt"), { { 1, 1, QSE_NULL }, fnc_sqrt, 0 } },
|
2014-07-11 07:42:28 +00:00
|
|
|
{ QSE_T("srand"), { { 0, 1, QSE_NULL }, fnc_srand, 0 } },
|
2014-07-01 15:27:27 +00:00
|
|
|
{ QSE_T("tan"), { { 1, 1, QSE_NULL }, fnc_tan, 0 } },
|
|
|
|
{ QSE_T("tanh"), { { 1, 1, QSE_NULL }, fnc_tanh, 0 } }
|
2014-06-08 12:46:04 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static int query (qse_awk_mod_t* mod, qse_awk_t* awk, const qse_char_t* name, qse_awk_mod_sym_t* sym)
|
|
|
|
{
|
2014-07-08 14:30:42 +00:00
|
|
|
qse_cstr_t ea;
|
2014-06-08 12:46:04 +00:00
|
|
|
int left, right, mid, n;
|
|
|
|
|
|
|
|
left = 0; right = QSE_COUNTOF(fnctab) - 1;
|
|
|
|
|
|
|
|
while (left <= right)
|
|
|
|
{
|
2017-05-02 01:14:32 +00:00
|
|
|
mid = left + (right - left) / 2;
|
2014-06-08 12:46:04 +00:00
|
|
|
|
|
|
|
n = qse_strcmp (fnctab[mid].name, name);
|
|
|
|
if (n > 0) right = mid - 1;
|
|
|
|
else if (n < 0) left = mid + 1;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sym->type = QSE_AWK_MOD_FNC;
|
|
|
|
sym->u.fnc = fnctab[mid].info;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
left = 0; right = QSE_COUNTOF(inttab) - 1;
|
|
|
|
while (left <= right)
|
|
|
|
{
|
2017-05-02 01:14:32 +00:00
|
|
|
mid = left + (right - left) / 2;
|
2014-06-08 12:46:04 +00:00
|
|
|
|
|
|
|
n = qse_strcmp (inttab[mid].name, name);
|
|
|
|
if (n > 0) right = mid - 1;
|
|
|
|
else if (n < 0) left = mid + 1;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sym->type = QSE_AWK_MOD_INT;
|
|
|
|
sym->u.in = inttab[mid].info;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-07-02 12:28:58 +00:00
|
|
|
ea.ptr = (qse_char_t*)name;
|
2014-06-08 12:46:04 +00:00
|
|
|
ea.len = qse_strlen(name);
|
|
|
|
qse_awk_seterror (awk, QSE_AWK_ENOENT, &ea, QSE_NULL);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: proper resource management */
|
|
|
|
|
|
|
|
static int init (qse_awk_mod_t* mod, qse_awk_rtx_t* rtx)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fini (qse_awk_mod_t* mod, qse_awk_rtx_t* rtx)
|
|
|
|
{
|
2014-07-11 07:42:28 +00:00
|
|
|
/* TODO: anything */
|
2014-06-08 12:46:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void unload (qse_awk_mod_t* mod, qse_awk_t* awk)
|
|
|
|
{
|
2014-07-11 07:42:28 +00:00
|
|
|
modctx_t* modctx;
|
|
|
|
|
|
|
|
modctx = (modctx_t*)mod->ctx;
|
|
|
|
qse_awk_freemem (awk, modctx);
|
2014-06-08 12:46:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int qse_awk_mod_math (qse_awk_mod_t* mod, qse_awk_t* awk)
|
|
|
|
{
|
2014-07-11 07:42:28 +00:00
|
|
|
modctx_t* modctx;
|
|
|
|
qse_ntime_t now;
|
|
|
|
|
|
|
|
modctx = qse_awk_allocmem (awk, QSE_SIZEOF(*modctx));
|
|
|
|
if (modctx == QSE_NULL)
|
|
|
|
{
|
|
|
|
qse_awk_seterrnum (awk, QSE_AWK_ENOMEM, QSE_NULL);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
QSE_MEMSET (modctx, 0, QSE_SIZEOF(*modctx));
|
|
|
|
|
|
|
|
modctx->seed = (qse_gettime (&now) <= -1)? 0u: ((qse_awk_int_t)now.sec + (qse_awk_int_t)now.nsec);
|
|
|
|
/* i don't care if the seed becomes negative or overflows.
|
|
|
|
* i just convert the signed value to the unsigned one. */
|
|
|
|
modctx->prand = (qse_awk_uint_t)(modctx->seed * modctx->seed * modctx->seed);
|
|
|
|
/* make sure that the actual seeding is not 0 */
|
|
|
|
if (modctx->prand == 0) modctx->prand++;
|
|
|
|
|
2014-06-08 12:46:04 +00:00
|
|
|
mod->query = query;
|
|
|
|
mod->unload = unload;
|
|
|
|
|
|
|
|
mod->init = init;
|
|
|
|
mod->fini = fini;
|
2014-07-11 07:42:28 +00:00
|
|
|
mod->ctx = modctx;
|
2014-06-08 12:46:04 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|