(root)/
gawk-5.2.2/
extension/
intdiv.c
       1  /*
       2   * intdiv.c - Provide integer div/mod for MPFR.
       3   */
       4  
       5  /*
       6   * Copyright (C) 2017, 2018, 2021, 2022, the Free Software Foundation, Inc.
       7   *
       8   * This file is part of GAWK, the GNU implementation of the
       9   * AWK Programming Language.
      10   *
      11   * GAWK is free software; you can redistribute it and/or modify
      12   * it under the terms of the GNU General Public License as published by
      13   * the Free Software Foundation; either version 3 of the License, or
      14   * (at your option) any later version.
      15   *
      16   * GAWK is distributed in the hope that it will be useful,
      17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      19   * GNU General Public License for more details.
      20   *
      21   * You should have received a copy of the GNU General Public License
      22   * along with this program; if not, write to the Free Software
      23   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
      24   */
      25  
      26  #ifdef HAVE_CONFIG_H
      27  #include <config.h>
      28  #endif
      29  
      30  #include <stdio.h>
      31  #include <assert.h>
      32  #include <stdlib.h>
      33  #include <string.h>
      34  #include <unistd.h>
      35  #include <math.h>
      36  
      37  #include <sys/types.h>
      38  #include <sys/stat.h>
      39  
      40  #include "gawkapi.h"
      41  
      42  #ifdef HAVE_MPFR
      43  #include <gmp.h>
      44  #include <mpfr.h>
      45  #ifndef MPFR_RNDZ
      46  /* for compatibility with MPFR 2.X */
      47  #define MPFR_RNDZ GMP_RNDZ
      48  #endif
      49  #endif
      50  
      51  #include "gettext.h"
      52  #define _(msgid)  gettext(msgid)
      53  #define N_(msgid) msgid
      54  
      55  static const gawk_api_t *api;	/* for convenience macros to work */
      56  static awk_ext_id_t ext_id;
      57  static const char *ext_version = "intdiv extension: version 1.0";
      58  static awk_bool_t (*init_func)(void) = NULL;
      59  
      60  int plugin_is_GPL_compatible;
      61  
      62  /* double_to_int --- get the integer part of a double */
      63  
      64  static double
      65  double_to_int(double d)
      66  {
      67  	if (d >= 0)
      68  		d = floor(d);
      69  	else
      70  		d = ceil(d);
      71  	return d;
      72  }
      73  
      74  /* array_set_number --- set an array element to a numeric value */
      75  
      76  static void
      77  array_set_number(awk_array_t array, const char *sub, size_t sublen, double num)
      78  {
      79  	awk_value_t index, tmp;
      80  
      81  	set_array_element(array, make_const_string(sub, sublen, & index), make_number(num, & tmp));
      82  }
      83  
      84  #ifdef HAVE_MPFR
      85  
      86  /* mpz_conv --- convert an awk_value_t to an MPZ value */
      87  
      88  static mpz_ptr
      89  mpz_conv(const awk_value_t *arg, mpz_ptr tmp)
      90  {
      91  	switch (arg->num_type) {
      92  	case AWK_NUMBER_TYPE_MPZ:
      93  		return arg->num_ptr;
      94  	case AWK_NUMBER_TYPE_MPFR:
      95  		if (! mpfr_number_p(arg->num_ptr))
      96  			return NULL;
      97  		mpz_init(tmp);
      98  		mpfr_get_z(tmp, arg->num_ptr, MPFR_RNDZ);
      99  		return tmp;
     100  	case AWK_NUMBER_TYPE_DOUBLE:	/* can this happen? */
     101  		mpz_init(tmp);
     102  		mpz_set_d(tmp, double_to_int(arg->num_value));
     103  		return tmp;
     104  	default:	/* should never happen */
     105  		fatal(ext_id, _("intdiv: invalid numeric type `%d'"), arg->num_type);
     106  		return NULL;
     107  	}
     108  }
     109  
     110  /* array_set_mpz --- set an array element to an MPZ value */
     111  
     112  static void
     113  array_set_mpz(awk_array_t array, const char *sub, size_t sublen, mpz_ptr num)
     114  {
     115  	awk_value_t index, tmp;
     116  
     117  	set_array_element(array, make_const_string(sub, sublen, & index), make_number_mpz(num, & tmp));
     118  }
     119  
     120  #endif
     121  
     122  /* do_intdiv --- do integer division, return quotient and remainder in dest array */
     123  
     124  /*
     125   * We define the semantics as:
     126   * 	numerator = int(numerator)
     127   *	denominator = int(denonmator)
     128   *	quotient = int(numerator / denomator)
     129   *	remainder = int(numerator % denomator)
     130   */
     131  
     132  static awk_value_t *
     133  do_intdiv(int nargs, awk_value_t *result, struct awk_ext_func *unused)
     134  {
     135  	awk_value_t nv, dv, array_param;
     136  	awk_array_t array;
     137  
     138  	if (! get_argument(0, AWK_NUMBER, & nv)) {
     139  		warning(ext_id, _("intdiv: first argument must be numeric"));
     140  		return make_number(-1, result);
     141  	}
     142  	if (! get_argument(1, AWK_NUMBER, & dv)) {
     143  		warning(ext_id, _("intdiv: second argument must be numeric"));
     144  		return make_number(-1, result);
     145  	}
     146  	if (! get_argument(2, AWK_ARRAY, & array_param)) {
     147  		warning(ext_id, _("intdiv: third argument must be an array"));
     148  		return make_number(-1, result);
     149  	}
     150  	array = array_param.array_cookie;
     151  	clear_array(array);
     152  
     153  #ifdef HAVE_MPFR
     154  	if (nv.num_type == AWK_NUMBER_TYPE_DOUBLE && dv.num_type == AWK_NUMBER_TYPE_DOUBLE)
     155  #endif
     156  	{
     157  		/* regular precision */
     158  		double num, denom, quotient, remainder;
     159  
     160  #ifndef HAVE_MPFR
     161  		if (nv.num_type != AWK_NUMBER_TYPE_DOUBLE || dv.num_type != AWK_NUMBER_TYPE_DOUBLE) {
     162  			static int warned = 0;
     163  			if (!warned) {
     164  				warning(ext_id, _("intdiv: MPFR arguments converted to IEEE because this extension was not compiled with MPFR support; loss of precision may occur"));
     165  				warned = 1;
     166  			}
     167  		}
     168  #endif
     169  		num = double_to_int(nv.num_value);
     170  		denom = double_to_int(dv.num_value);
     171  
     172  		if (denom == 0.0) {
     173  			warning(ext_id, _("intdiv: division by zero attempted"));
     174  			return make_number(-1, result);
     175  		}
     176  
     177  		quotient = double_to_int(num / denom);
     178  #ifdef HAVE_FMOD
     179  		remainder = fmod(num, denom);
     180  #else	/* ! HAVE_FMOD */
     181  		(void) modf(num / denom, & remainder);
     182  		remainder = num - remainder * denom;
     183  #endif	/* ! HAVE_FMOD */
     184  		remainder = double_to_int(remainder);
     185  
     186  		array_set_number(array, "quotient", 8, quotient);
     187  		array_set_number(array, "remainder", 9, remainder);
     188  	}
     189  #ifdef HAVE_MPFR
     190  	else {
     191  		/* extended precision */
     192  		mpz_ptr numer, denom;
     193  		mpz_t numer_tmp, denom_tmp;
     194  		mpz_t quotient, remainder;
     195  
     196  		/* convert numerator and denominator to integer */
     197  		if (!(numer = mpz_conv(&nv, numer_tmp))) {
     198  			warning(ext_id, _("intdiv: numerator is not finite"));
     199  			return make_number(-1, result);
     200  		}
     201  		if (!(denom = mpz_conv(&dv, denom_tmp))) {
     202  			warning(ext_id, _("intdiv: denominator is not finite"));
     203  			if (numer == numer_tmp)
     204  				mpz_clear(numer);
     205  			return make_number(-1, result);
     206  		}
     207  		if (mpz_sgn(denom) == 0) {
     208  			warning(ext_id, _("intdiv: division by zero attempted"));
     209  			if (numer == numer_tmp)
     210  				mpz_clear(numer);
     211  			if (denom == denom_tmp)
     212  				mpz_clear(denom);
     213  			return make_number(-1, result);
     214  		}
     215  
     216  		mpz_init(quotient);
     217  		mpz_init(remainder);
     218  
     219  		/* do the division */
     220  		mpz_tdiv_qr(quotient, remainder, numer, denom);
     221  
     222  		array_set_mpz(array, "quotient", 8, quotient);
     223  		array_set_mpz(array, "remainder", 9, remainder);
     224  
     225  		/* release temporary variables */
     226  		if (numer == numer_tmp)
     227  			mpz_clear(numer);
     228  		if (denom == denom_tmp)
     229  			mpz_clear(denom);
     230  	}
     231  #endif
     232  
     233  	return make_number(0, result);
     234  }
     235  
     236  static awk_ext_func_t func_table[] = {
     237  	{ "intdiv", do_intdiv, 3, 3, awk_false, NULL },
     238  };
     239  
     240  /* define the dl_load function using the boilerplate macro */
     241  
     242  dl_load_func(func_table, intdiv, "")