/ src / gmpy2_mpq_misc.c
gmpy2_mpq_misc.c
  1  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2   * gmpy2_mpq_misc.c                                                        *
  3   * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  4   * Python interface to the GMP or MPIR, MPFR, and MPC multiple precision   *
  5   * libraries.                                                              *
  6   *                                                                         *
  7   * Copyright 2000 - 2009 Alex Martelli                                     *
  8   *                                                                         *
  9   * Copyright 2008 - 2021 Case Van Horsen                                   *
 10   *                                                                         *
 11   * This file is part of GMPY2.                                             *
 12   *                                                                         *
 13   * GMPY2 is free software: you can redistribute it and/or modify it under  *
 14   * the terms of the GNU Lesser General Public License as published by the  *
 15   * Free Software Foundation, either version 3 of the License, or (at your  *
 16   * option) any later version.                                              *
 17   *                                                                         *
 18   * GMPY2 is distributed in the hope that it will be useful, but WITHOUT    *
 19   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or   *
 20   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public    *
 21   * License for more details.                                               *
 22   *                                                                         *
 23   * You should have received a copy of the GNU Lesser General Public        *
 24   * License along with GMPY2; if not, see <http://www.gnu.org/licenses/>    *
 25   * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 26  
 27  static PyObject *
 28  GMPy_MPQ_Attrib_GetNumer(MPQ_Object *self, void *closure)
 29  {
 30      MPZ_Object *result;
 31      CTXT_Object *context = NULL;
 32  
 33      if ((result = GMPy_MPZ_New(context)))
 34          mpz_set(result->z, mpq_numref(self->q));
 35      return (PyObject*)result;
 36  }
 37  
 38  static PyObject *
 39  GMPy_MPQ_Attrib_GetReal(MPQ_Object *self, void *closure)
 40  {
 41      Py_INCREF((PyObject*)self);
 42      return (PyObject*)self;
 43  }
 44  
 45  static PyObject *
 46  GMPy_MPQ_Attrib_GetDenom(MPQ_Object *self, void *closure)
 47  {
 48      MPZ_Object *result;
 49      CTXT_Object *context = NULL;
 50  
 51      if ((result = GMPy_MPZ_New(context)))
 52          mpz_set(result->z, mpq_denref(self->q));
 53      return (PyObject*)result;
 54  }
 55  
 56  static PyObject *
 57  GMPy_MPQ_Attrib_GetImag(MPQ_Object *self, void *closure)
 58  {
 59      MPZ_Object *result;
 60      CTXT_Object *context = NULL;
 61  
 62      if ((result = GMPy_MPZ_New(context)))
 63          mpz_set_ui(result->z, 0);
 64      return (PyObject*)result;
 65  }
 66  
 67  PyDoc_STRVAR(GMPy_doc_mpq_function_numer,
 68  "numer(x) -> mpz\n\n"
 69  "Return the numerator of x.");
 70  
 71  static PyObject *
 72  GMPy_MPQ_Function_Numer(PyObject *self, PyObject *other)
 73  {
 74      MPZ_Object *result;
 75      MPQ_Object *tempq;
 76      CTXT_Object *context = NULL;
 77  
 78      if (!(result = (MPZ_Object*)GMPy_MPZ_New(context)))
 79          return NULL;
 80  
 81      if (!(tempq = GMPy_MPQ_From_Rational(other, context))) {
 82          Py_DECREF((PyObject*)result);
 83          return NULL;
 84      }
 85  
 86      mpz_set(result->z, mpq_numref(tempq->q));
 87      Py_DECREF((PyObject*)tempq);
 88      return (PyObject*)result;
 89  }
 90  
 91  PyDoc_STRVAR(GMPy_doc_mpq_function_denom,
 92  "denom(x) -> mpz\n\n"
 93  "Return the denominator of x.");
 94  
 95  static PyObject *
 96  GMPy_MPQ_Function_Denom(PyObject *self, PyObject *other)
 97  {
 98      MPZ_Object *result;
 99      MPQ_Object *tempq;
100      CTXT_Object *context = NULL;
101  
102      if (!(result = (MPZ_Object*)GMPy_MPZ_New(context)))
103          return NULL;
104  
105      if (!(tempq = GMPy_MPQ_From_Rational(other, context))) {
106          Py_DECREF((PyObject*)result);
107          return NULL;
108      }
109  
110      mpz_set(result->z, mpq_denref(tempq->q));
111      Py_DECREF((PyObject*)tempq);
112      return (PyObject*)result;
113  }
114  
115  PyDoc_STRVAR(GMPy_doc_function_qdiv,
116  "qdiv(x[, y=1]) -> number\n\n"
117  "Return x/y as 'mpz' if possible, or as 'mpq' if x is not exactly\n"
118  "divisible by y.");
119  
120  static PyObject *
121  GMPy_MPQ_Function_Qdiv(PyObject *self, PyObject *args)
122  {
123      Py_ssize_t argc;
124      PyObject *result = NULL, *x, *y;
125      MPQ_Object *tempx = NULL, *tempy = NULL, *tempr = NULL;
126      CTXT_Object *context = NULL;
127  
128      CHECK_CONTEXT(context);
129  
130      /* Validate the argument(s). */
131  
132      /* If there is only one argument, it should be either an integer or
133       * rational type. If it is an integer, then immediately return an mpz().
134       * If it is a rational type, convert it to an mpq and check the denominator.
135       */
136  
137      argc = PyTuple_GET_SIZE(args);
138      if (argc == 1) {
139          x = PyTuple_GET_ITEM(args, 0);
140  
141          if (!IS_RATIONAL(x)) {
142              goto arg_error;
143          }
144  
145          if (IS_INTEGER(x)) {
146              return (PyObject*)GMPy_MPZ_From_Integer(x, context);
147          }
148  
149          if (!(tempx = GMPy_MPQ_From_Rational(x, context))) {
150              return NULL;
151          }
152  
153          if (mpz_cmp_ui(mpq_denref(tempx->q), 1) == 0) {
154              if ((result = (PyObject*)GMPy_MPZ_New(context))) {
155                  mpz_set(MPZ(result), mpq_numref(tempx->q));
156              }
157              Py_DECREF((PyObject*)tempx);
158              return result;
159          }
160          else {
161              return (PyObject*)tempx;
162          }
163      }
164  
165      /* If there are two rational arguments, just convert them both to mpq,
166       * divide, and then check the denominator.
167       */
168  
169      if (argc == 2) {
170          x = PyTuple_GET_ITEM(args, 0);
171          y = PyTuple_GET_ITEM(args, 1);
172  
173          if (!IS_RATIONAL(x) || !IS_RATIONAL(y)) {
174              goto arg_error;
175          }
176  
177          if (!(tempx = GMPy_MPQ_From_Rational(x, context)) ||
178              !(tempy = GMPy_MPQ_From_Rational(y, context))) {
179              Py_XDECREF((PyObject*)tempx);
180              Py_XDECREF((PyObject*)tempy);
181              return NULL;
182          }
183  
184          if (mpq_sgn(tempy->q) == 0) {
185              Py_DECREF((PyObject*)tempx);
186              Py_DECREF((PyObject*)tempy);
187              ZERO_ERROR("qdiv() division by zero");
188              return NULL;
189          }
190  
191          /* tempr contains the result of the division. */
192  
193          if (!(tempr = GMPy_MPQ_New(context))) {
194              Py_DECREF((PyObject*)tempx);
195              Py_DECREF((PyObject*)tempy);
196              return NULL;
197          }
198  
199          mpq_div(tempr->q, tempx->q, tempy->q);
200          Py_DECREF((PyObject*)tempx);
201          Py_DECREF((PyObject*)tempy);
202  
203          if (mpz_cmp_ui(mpq_denref(tempr->q), 1) == 0) {
204              if ((result = (PyObject*)GMPy_MPZ_New(context))) {
205                  mpz_set(MPZ(result), mpq_numref(tempr->q));
206              }
207              Py_DECREF((PyObject*)tempr);
208              return result;
209          }
210          else {
211              return (PyObject*)tempr;
212          };
213      }
214  
215    arg_error:
216      TYPE_ERROR("qdiv() requires 1 or 2 integer or rational arguments");
217      return NULL;
218  }
219  
220  PyDoc_STRVAR(GMPy_doc_mpq_method_floor,
221  "Return greatest integer less than or equal to an mpq.");
222  
223  static PyObject *
224  GMPy_MPQ_Method_Floor(PyObject *self, PyObject *other)
225  {
226      MPZ_Object *result;
227      CTXT_Object *context = NULL;
228  
229      CHECK_CONTEXT(context);
230  
231      if ((result = GMPy_MPZ_New(context))) {
232          mpz_fdiv_q(result->z, mpq_numref(MPQ(self)), mpq_denref(MPQ(self)));
233      }
234  
235      return (PyObject*)result;
236  }
237  
238  PyDoc_STRVAR(GMPy_doc_mpq_method_ceil,
239  "Return least integer greater than or equal to an mpq.");
240  
241  static PyObject *
242  GMPy_MPQ_Method_Ceil(PyObject *self, PyObject *other)
243  {
244      MPZ_Object *result;
245      CTXT_Object *context = NULL;
246  
247      CHECK_CONTEXT(context);
248  
249      if ((result = GMPy_MPZ_New(context))) {
250          mpz_cdiv_q(result->z, mpq_numref(MPQ(self)), mpq_denref(MPQ(self)));
251      }
252  
253      return (PyObject*)result;
254  }
255  
256  PyDoc_STRVAR(GMPy_doc_mpq_method_trunc,
257  "Return integer portion of an mpq.");
258  
259  static PyObject *
260  GMPy_MPQ_Method_Trunc(PyObject *self, PyObject *other)
261  {
262      MPZ_Object *result;
263      CTXT_Object *context = NULL;
264  
265      CHECK_CONTEXT(context);
266  
267      if ((result = GMPy_MPZ_New(context))) {
268          mpz_tdiv_q(result->z, mpq_numref(MPQ(self)), mpq_denref(MPQ(self)));
269      }
270  
271      return (PyObject*)result;
272  }
273  
274  PyDoc_STRVAR(GMPy_doc_mpq_method_round, "Round an mpq to power of 10.");
275  
276  static PyObject *
277  GMPy_MPQ_Method_Round(PyObject *self, PyObject *args)
278  {
279      Py_ssize_t round_digits = 0;
280      MPQ_Object *resultq;
281      MPZ_Object *resultz;
282      mpz_t temp, rem;
283      CTXT_Object *context = NULL;
284  
285      CHECK_CONTEXT(context);
286  
287      /* If args is NULL or the size of args is 0, we just return an mpz. */
288  
289      if (!args || PyTuple_GET_SIZE(args) == 0) {
290          if (!(resultz = GMPy_MPZ_New(context))) {
291              return NULL;
292          }
293  
294          mpz_init(rem);
295          mpz_fdiv_qr(resultz->z, rem, mpq_numref(MPQ(self)), mpq_denref(MPQ(self)));
296          mpz_mul_2exp(rem, rem, 1);
297          if (mpz_cmp(rem, mpq_denref(MPQ(self))) > 0) {
298              mpz_add_ui(resultz->z, resultz->z, 1);
299          }
300          else if (mpz_cmp(rem, mpq_denref(MPQ(self))) == 0) {
301              if (mpz_odd_p(resultz->z)) {
302                  mpz_add_ui(resultz->z, resultz->z, 1);
303              }
304          }
305          mpz_clear(rem);
306          return (PyObject*)resultz;
307      }
308  
309      if (PyTuple_GET_SIZE(args) > 1) {
310          TYPE_ERROR("Too many arguments for __round__()");
311          return NULL;
312      }
313  
314      if (PyTuple_GET_SIZE(args) == 1) {
315          round_digits = PyIntOrLong_AsSsize_t(PyTuple_GET_ITEM(args, 0));
316          if (round_digits == -1 && PyErr_Occurred()) {
317              TYPE_ERROR("__round__() requires 'int' argument");
318              return NULL;
319          }
320      }
321  
322      if (!(resultq = GMPy_MPQ_New(context))) {
323          return NULL;
324      }
325  
326      mpz_init(temp);
327      mpz_ui_pow_ui(temp, 10, round_digits > 0 ? round_digits : -round_digits);
328  
329      mpq_set(resultq->q, MPQ(self));
330      if (round_digits > 0) {
331          mpz_mul(mpq_numref(resultq->q), mpq_numref(resultq->q), temp);
332          mpq_canonicalize(resultq->q);
333          if (!(resultz = (MPZ_Object*)GMPy_MPQ_Method_Round((PyObject*)resultq, NULL))) {
334              mpz_clear(temp);
335              return NULL;
336          }
337          mpz_set(mpq_numref(resultq->q), resultz->z);
338          Py_DECREF((PyObject*)resultz);
339          mpz_set(mpq_denref(resultq->q), temp);
340          mpz_clear(temp);
341          mpq_canonicalize(resultq->q);
342      }
343      else {
344          mpz_mul(mpq_denref(resultq->q), mpq_denref(resultq->q), temp);
345          mpq_canonicalize(resultq->q);
346          if (!(resultz = (MPZ_Object*)GMPy_MPQ_Method_Round((PyObject*)resultq, NULL))) {
347              mpz_clear(temp);
348              return NULL;
349          }
350          mpq_set_ui(resultq->q, 0, 1);
351          mpz_mul(mpq_numref(resultq->q), resultz->z, temp);
352          Py_DECREF((PyObject*)resultz);
353          mpz_clear(temp);
354          mpq_canonicalize(resultq->q);
355      }
356      return (PyObject*)resultq;
357  }
358  
359  static int
360  GMPy_MPQ_NonZero_Slot(MPQ_Object *self)
361  {
362      return mpq_sgn(self->q) != 0;
363  }
364  
365  PyDoc_STRVAR(GMPy_doc_mpq_method_sizeof,
366  "x.__sizeof__()\n\n"
367  "Returns the amount of memory consumed by x. Note: deleted mpq objects\n"
368  "are reused and may or may not be resized when a new value is assigned.");
369  
370  static PyObject *
371  GMPy_MPQ_Method_Sizeof(PyObject *self, PyObject *other)
372  {
373      return PyIntOrLong_FromSize_t(sizeof(MPQ_Object) + \
374          (mpq_numref(MPQ(self))->_mp_alloc * sizeof(mp_limb_t)) + \
375          (mpq_denref(MPQ(self))->_mp_alloc * sizeof(mp_limb_t)));
376  }
377