|
Packit Service |
f629e6 |
#ifdef HAVE_CONFIG_H
|
|
Packit Service |
f629e6 |
#include <config.h>
|
|
Packit Service |
f629e6 |
#endif
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
#include <stdio.h>
|
|
Packit Service |
f629e6 |
#include <assert.h>
|
|
Packit Service |
f629e6 |
#include <stdlib.h>
|
|
Packit Service |
f629e6 |
#include <string.h>
|
|
Packit Service |
f629e6 |
#include <unistd.h>
|
|
Packit Service |
f629e6 |
#include <math.h>
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
#include <sys/types.h>
|
|
Packit Service |
f629e6 |
#include <sys/stat.h>
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
#include "gawkapi.h"
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
#ifdef HAVE_MPFR
|
|
Packit Service |
f629e6 |
#include <gmp.h>
|
|
Packit Service |
f629e6 |
#include <mpfr.h>
|
|
Packit Service |
f629e6 |
#ifndef MPFR_RNDZ
|
|
Packit Service |
f629e6 |
/* for compatibility with MPFR 2.X */
|
|
Packit Service |
f629e6 |
#define MPFR_RNDZ GMP_RNDZ
|
|
Packit Service |
f629e6 |
#endif
|
|
Packit Service |
f629e6 |
#endif
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
#include "gettext.h"
|
|
Packit Service |
f629e6 |
#define _(msgid) gettext(msgid)
|
|
Packit Service |
f629e6 |
#define N_(msgid) msgid
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
static const gawk_api_t *api; /* for convenience macros to work */
|
|
Packit Service |
f629e6 |
static awk_ext_id_t ext_id;
|
|
Packit Service |
f629e6 |
static const char *ext_version = "intdiv extension: version 1.0";
|
|
Packit Service |
f629e6 |
static awk_bool_t (*init_func)(void) = NULL;
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
int plugin_is_GPL_compatible;
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
/* double_to_int --- get the integer part of a double */
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
static double
|
|
Packit Service |
f629e6 |
double_to_int(double d)
|
|
Packit Service |
f629e6 |
{
|
|
Packit Service |
f629e6 |
if (d >= 0)
|
|
Packit Service |
f629e6 |
d = floor(d);
|
|
Packit Service |
f629e6 |
else
|
|
Packit Service |
f629e6 |
d = ceil(d);
|
|
Packit Service |
f629e6 |
return d;
|
|
Packit Service |
f629e6 |
}
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
/* array_set_number --- set an array element to a numeric value */
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
static void
|
|
Packit Service |
f629e6 |
array_set_number(awk_array_t array, const char *sub, size_t sublen, double num)
|
|
Packit Service |
f629e6 |
{
|
|
Packit Service |
f629e6 |
awk_value_t index, tmp;
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
set_array_element(array, make_const_string(sub, sublen, & index), make_number(num, & tmp));
|
|
Packit Service |
f629e6 |
}
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
#ifdef HAVE_MPFR
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
/* mpz_conv --- convert an awk_value_t to an MPZ value */
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
static mpz_ptr
|
|
Packit Service |
f629e6 |
mpz_conv(const awk_value_t *arg, mpz_ptr tmp)
|
|
Packit Service |
f629e6 |
{
|
|
Packit Service |
f629e6 |
switch (arg->num_type) {
|
|
Packit Service |
f629e6 |
case AWK_NUMBER_TYPE_MPZ:
|
|
Packit Service |
f629e6 |
return arg->num_ptr;
|
|
Packit Service |
f629e6 |
case AWK_NUMBER_TYPE_MPFR:
|
|
Packit Service |
f629e6 |
if (! mpfr_number_p(arg->num_ptr))
|
|
Packit Service |
f629e6 |
return NULL;
|
|
Packit Service |
f629e6 |
mpz_init(tmp);
|
|
Packit Service |
f629e6 |
mpfr_get_z(tmp, arg->num_ptr, MPFR_RNDZ);
|
|
Packit Service |
f629e6 |
return tmp;
|
|
Packit Service |
f629e6 |
case AWK_NUMBER_TYPE_DOUBLE: /* can this happen? */
|
|
Packit Service |
f629e6 |
mpz_init(tmp);
|
|
Packit Service |
f629e6 |
mpz_set_d(tmp, double_to_int(arg->num_value));
|
|
Packit Service |
f629e6 |
return tmp;
|
|
Packit Service |
f629e6 |
default: /* should never happen */
|
|
Packit Service |
f629e6 |
fatal(ext_id, _("intdiv: invalid numeric type `%d'"), arg->num_type);
|
|
Packit Service |
f629e6 |
return NULL;
|
|
Packit Service |
f629e6 |
}
|
|
Packit Service |
f629e6 |
}
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
/* array_set_mpz --- set an array element to an MPZ value */
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
static void
|
|
Packit Service |
f629e6 |
array_set_mpz(awk_array_t array, const char *sub, size_t sublen, mpz_ptr num)
|
|
Packit Service |
f629e6 |
{
|
|
Packit Service |
f629e6 |
awk_value_t index, tmp;
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
set_array_element(array, make_const_string(sub, sublen, & index), make_number_mpz(num, & tmp));
|
|
Packit Service |
f629e6 |
}
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
#endif
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
/* do_intdiv --- do integer division, return quotient and remainder in dest array */
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
/*
|
|
Packit Service |
f629e6 |
* We define the semantics as:
|
|
Packit Service |
f629e6 |
* numerator = int(numerator)
|
|
Packit Service |
f629e6 |
* denominator = int(denonmator)
|
|
Packit Service |
f629e6 |
* quotient = int(numerator / denomator)
|
|
Packit Service |
f629e6 |
* remainder = int(numerator % denomator)
|
|
Packit Service |
f629e6 |
*/
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
static awk_value_t *
|
|
Packit Service |
f629e6 |
do_intdiv(int nargs, awk_value_t *result, struct awk_ext_func *unused)
|
|
Packit Service |
f629e6 |
{
|
|
Packit Service |
f629e6 |
awk_value_t nv, dv, array_param;
|
|
Packit Service |
f629e6 |
awk_array_t array;
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
if (! get_argument(0, AWK_NUMBER, & nv)) {
|
|
Packit Service |
f629e6 |
warning(ext_id, _("intdiv: first argument must be numeric"));
|
|
Packit Service |
f629e6 |
return make_number(-1, result);
|
|
Packit Service |
f629e6 |
}
|
|
Packit Service |
f629e6 |
if (! get_argument(1, AWK_NUMBER, & dv)) {
|
|
Packit Service |
f629e6 |
warning(ext_id, _("intdiv: second argument must be numeric"));
|
|
Packit Service |
f629e6 |
return make_number(-1, result);
|
|
Packit Service |
f629e6 |
}
|
|
Packit Service |
f629e6 |
if (! get_argument(2, AWK_ARRAY, & array_param)) {
|
|
Packit Service |
f629e6 |
warning(ext_id, _("intdiv: third argument must be an array"));
|
|
Packit Service |
f629e6 |
return make_number(-1, result);
|
|
Packit Service |
f629e6 |
}
|
|
Packit Service |
f629e6 |
array = array_param.array_cookie;
|
|
Packit Service |
f629e6 |
clear_array(array);
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
#ifdef HAVE_MPFR
|
|
Packit Service |
f629e6 |
if (nv.num_type == AWK_NUMBER_TYPE_DOUBLE && dv.num_type == AWK_NUMBER_TYPE_DOUBLE)
|
|
Packit Service |
f629e6 |
#endif
|
|
Packit Service |
f629e6 |
{
|
|
Packit Service |
f629e6 |
/* regular precision */
|
|
Packit Service |
f629e6 |
double num, denom, quotient, remainder;
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
#ifndef HAVE_MPFR
|
|
Packit Service |
f629e6 |
if (nv.num_type != AWK_NUMBER_TYPE_DOUBLE || dv.num_type != AWK_NUMBER_TYPE_DOUBLE) {
|
|
Packit Service |
f629e6 |
static int warned = 0;
|
|
Packit Service |
f629e6 |
if (!warned) {
|
|
Packit Service |
f629e6 |
warning(ext_id, _("intdiv: MPFR arguments converted to IEEE because this extension was not compiled with MPFR support; loss of precision may occur"));
|
|
Packit Service |
f629e6 |
warned = 1;
|
|
Packit Service |
f629e6 |
}
|
|
Packit Service |
f629e6 |
}
|
|
Packit Service |
f629e6 |
#endif
|
|
Packit Service |
f629e6 |
num = double_to_int(nv.num_value);
|
|
Packit Service |
f629e6 |
denom = double_to_int(dv.num_value);
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
if (denom == 0.0) {
|
|
Packit Service |
f629e6 |
warning(ext_id, _("intdiv: division by zero attempted"));
|
|
Packit Service |
f629e6 |
return make_number(-1, result);
|
|
Packit Service |
f629e6 |
}
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
quotient = double_to_int(num / denom);
|
|
Packit Service |
f629e6 |
#ifdef HAVE_FMOD
|
|
Packit Service |
f629e6 |
remainder = fmod(num, denom);
|
|
Packit Service |
f629e6 |
#else /* ! HAVE_FMOD */
|
|
Packit Service |
f629e6 |
(void) modf(num / denom, & remainder);
|
|
Packit Service |
f629e6 |
remainder = num - remainder * denom;
|
|
Packit Service |
f629e6 |
#endif /* ! HAVE_FMOD */
|
|
Packit Service |
f629e6 |
remainder = double_to_int(remainder);
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
array_set_number(array, "quotient", 8, quotient);
|
|
Packit Service |
f629e6 |
array_set_number(array, "remainder", 9, remainder);
|
|
Packit Service |
f629e6 |
}
|
|
Packit Service |
f629e6 |
#ifdef HAVE_MPFR
|
|
Packit Service |
f629e6 |
else {
|
|
Packit Service |
f629e6 |
/* extended precision */
|
|
Packit Service |
f629e6 |
mpz_ptr numer, denom;
|
|
Packit Service |
f629e6 |
mpz_t numer_tmp, denom_tmp;
|
|
Packit Service |
f629e6 |
mpz_ptr quotient, remainder;
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
/* convert numerator and denominator to integer */
|
|
Packit Service |
f629e6 |
if (!(numer = mpz_conv(&nv, numer_tmp))) {
|
|
Packit Service |
f629e6 |
warning(ext_id, _("intdiv: numerator is not finite"));
|
|
Packit Service |
f629e6 |
return make_number(-1, result);
|
|
Packit Service |
f629e6 |
}
|
|
Packit Service |
f629e6 |
if (!(denom = mpz_conv(&dv, denom_tmp))) {
|
|
Packit Service |
f629e6 |
warning(ext_id, _("intdiv: denominator is not finite"));
|
|
Packit Service |
f629e6 |
if (numer == numer_tmp)
|
|
Packit Service |
f629e6 |
mpz_clear(numer);
|
|
Packit Service |
f629e6 |
return make_number(-1, result);
|
|
Packit Service |
f629e6 |
}
|
|
Packit Service |
f629e6 |
if (mpz_sgn(denom) == 0) {
|
|
Packit Service |
f629e6 |
warning(ext_id, _("intdiv: division by zero attempted"));
|
|
Packit Service |
f629e6 |
if (numer == numer_tmp)
|
|
Packit Service |
f629e6 |
mpz_clear(numer);
|
|
Packit Service |
f629e6 |
if (denom == denom_tmp)
|
|
Packit Service |
f629e6 |
mpz_clear(denom);
|
|
Packit Service |
f629e6 |
return make_number(-1, result);
|
|
Packit Service |
f629e6 |
}
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
/* ask gawk to allocate return values for us */
|
|
Packit Service |
f629e6 |
quotient = get_mpz_ptr();
|
|
Packit Service |
f629e6 |
remainder = get_mpz_ptr();
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
/* do the division */
|
|
Packit Service |
f629e6 |
mpz_tdiv_qr(quotient, remainder, numer, denom);
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
array_set_mpz(array, "quotient", 8, quotient);
|
|
Packit Service |
f629e6 |
array_set_mpz(array, "remainder", 9, remainder);
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
/* release temporary variables */
|
|
Packit Service |
f629e6 |
if (numer == numer_tmp)
|
|
Packit Service |
f629e6 |
mpz_clear(numer);
|
|
Packit Service |
f629e6 |
if (denom == denom_tmp)
|
|
Packit Service |
f629e6 |
mpz_clear(denom);
|
|
Packit Service |
f629e6 |
}
|
|
Packit Service |
f629e6 |
#endif
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
return make_number(0, result);
|
|
Packit Service |
f629e6 |
}
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
static awk_ext_func_t func_table[] = {
|
|
Packit Service |
f629e6 |
{ "intdiv", do_intdiv, 3, 3, awk_false, NULL },
|
|
Packit Service |
f629e6 |
};
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
/* define the dl_load function using the boilerplate macro */
|
|
Packit Service |
f629e6 |
|
|
Packit Service |
f629e6 |
dl_load_func(func_table, intdiv, "")
|