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