1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2  * gmpy_convert.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 /* This file contains all the conversion functions for gmpy2.
28  *
29  * Overview
30  * --------
31  * gmpy2 tries to optimize the performance and accuracy of conversions from
32  * other numeric types. gmpy2 uses a LBYL (Look Before You Leap) approach and
33  * identifies the numeric type before conversion before conversion to a gmpy2
34  * type. The basic operations (+, -, *, /) are optimized to directly work with
35  * some basic types such as C longs or doubles.
36  */
37 
38 /* ======================================================================== *
39  * Miscellaneous helper functions.                                          *
40  * ======================================================================== */
41 
42 /* Since macros are used in gmpy2's codebase, these functions are skipped
43  * until they are needed for the C API in the future.
44  */
45 
46 #if 0
47 static int GMPy_isInteger(PyObject *obj)
48 {
49     return IS_INTEGER(obj) ? 1 : 0;
50 }
51 
52 static int GMPy_isFraction(PyObject *obj)
53 {
54     return (!strcmp(Py_TYPE(obj)->tp_name, "Fraction")) ? 1 : 0;
55 }
56 
57 static int GMPy_isRational(PyObject *obj)
58 {
59     return IS_RATIONAL(obj) ? 1 : 0;
60 }
61 
62 static int GMPy_isReal(PyObject *obj)
63 {
64     return IS_REAL(obj) ? 1 : 0;
65 }
66 
67 static int GMPy_isComplex(PyObject *obj)
68 {
69     return IS_COMPLEX(obj) ? 1 : 0;
70 }
71 #endif
72 
73 /* GMPy_ObjectType(PyObject *obj) returns an integer that identifies the
74  * object's type. See gmpy2_convert.h for details.
75  *
76  * Exceptions are never raised.
77  */
78 
GMPy_ObjectType(PyObject * obj)79 static int GMPy_ObjectType(PyObject *obj)
80 {
81     /* Tests are sorted by order by (best guess of) most common argument type.
82      * Tests that require attribute lookups are done last.
83      */
84 
85     if (MPZ_Check(obj)) return OBJ_TYPE_MPZ;
86 
87     if (MPFR_Check(obj)) return OBJ_TYPE_MPFR;
88 
89     if (MPC_Check(obj))  return OBJ_TYPE_MPC;
90 
91     if (MPQ_Check(obj))  return OBJ_TYPE_MPQ;
92 
93     if (XMPZ_Check(obj)) return OBJ_TYPE_XMPZ;
94 
95     if (PyIntOrLong_Check(obj)) return OBJ_TYPE_PyInteger;
96 
97     if (PyFloat_Check(obj)) return OBJ_TYPE_PyFloat;
98 
99     if (PyComplex_Check(obj)) return OBJ_TYPE_PyComplex;
100 
101     if (IS_FRACTION(obj)) return OBJ_TYPE_PyFraction;
102 
103     /* Now we look for the presence of __mpz__, __mpq__, __mpfr__, and __mpc__.
104      * Since a type may define more than one of the special methods, we perform
105      * the checks in reverse order.
106      */
107 
108     if (HAS_MPC_CONVERSION(obj)) return OBJ_TYPE_HAS_MPC;
109 
110     if (HAS_MPFR_CONVERSION(obj)) return OBJ_TYPE_HAS_MPFR;
111 
112     if (HAS_MPQ_CONVERSION(obj)) return OBJ_TYPE_HAS_MPQ;
113 
114     if (HAS_MPZ_CONVERSION(obj)) return OBJ_TYPE_HAS_MPZ;
115 
116     return OBJ_TYPE_UNKNOWN;
117 }
118 
119 static PyObject *
GMPy_RemoveUnderscoreASCII(PyObject * s)120 GMPy_RemoveUnderscoreASCII(PyObject *s)
121 {
122     PyObject *ascii_str = NULL, *temp = NULL, *filtered = NULL, *under = NULL, *blank = NULL;
123 
124     if (PyBytes_CheckExact(s)) {
125         temp = PyUnicode_DecodeASCII(PyBytes_AS_STRING(s), PyBytes_GET_SIZE(s), "strict");
126         if (!temp) {
127             VALUE_ERROR("string contains non-ASCII characters");
128             return NULL;
129         }
130     }
131     else if (PyUnicode_Check(s)) {
132         Py_INCREF(s);
133         temp = s;
134     }
135     else {
136         TYPE_ERROR("object is not string or Unicode");
137         return NULL;
138     }
139 
140     if ((under = PyUnicode_FromString("_")) &&
141         (blank = PyUnicode_FromString(""))) {
142         filtered = PyUnicode_Replace(temp, under, blank, -1);
143     }
144     Py_XDECREF(under);
145     Py_XDECREF(blank);
146     Py_XDECREF(temp);
147 
148     if (!filtered) {
149         return NULL;
150     }
151 
152     ascii_str = PyUnicode_AsASCIIString(filtered);
153     Py_DECREF(filtered);
154     if (!ascii_str) {
155         VALUE_ERROR("string contains non-ASCII characters");
156         return NULL;
157     }
158 
159     return ascii_str;
160 }
161 
162 /* mpz_set_PyStr converts a Python "string" into a mpz_t structure. It accepts
163  * a sequence of bytes (i.e. str in Python 2, bytes in Python 3) or a Unicode
164  * string (i.e. unicode in Python 3, str in Python 3). Returns -1 on error,
165  * 1 if successful.
166  */
167 
168 static int
mpz_set_PyStr(mpz_ptr z,PyObject * s,int base)169 mpz_set_PyStr(mpz_ptr z, PyObject *s, int base)
170 {
171     char *cp;
172     Py_ssize_t len;
173     PyObject *ascii_str = ascii_str = GMPy_RemoveUnderscoreASCII(s);
174 
175     if (!ascii_str) return -1;
176 
177     len = PyBytes_Size(ascii_str);
178     cp = PyBytes_AsString(ascii_str);
179 
180 
181     /* Check for leading base indicators. */
182     if (base == 0) {
183         if (len > 2 && cp[0] == '0') {
184             if (cp[1] == 'b')      { base = 2;  cp += 2; }
185             else if (cp[1] == 'o') { base = 8;  cp += 2; }
186             else if (cp[1] == 'x') { base = 16; cp += 2; }
187             else                   { base = 10; }
188         }
189         else {
190             base = 10;
191         }
192     }
193     else if (cp[0] == '0') {
194         /* If the specified base matches the leading base indicators, then
195          * we need to skip the base indicators.
196          */
197         if (cp[1] =='b' && base == 2)       { cp += 2; }
198         else if (cp[1] =='o' && base == 8)  { cp += 2; }
199         else if (cp[1] =='x' && base == 16) { cp += 2; }
200     }
201 
202     /* delegate rest to GMP's _set_str function */
203     if (-1 == mpz_set_str(z, cp, base)) {
204         VALUE_ERROR("invalid digits");
205         Py_DECREF(ascii_str);
206         return -1;
207     }
208     Py_DECREF(ascii_str);
209     return 1;
210 }
211 
212 /* Format an mpz into any base (2 to 62). Bits in the option parameter
213  * control various behaviors:
214  *   bit 0: if set, output is wrapped with mpz(...) or xmpz(...)
215  *   bit 1: if set, a '+' is included for positive numbers
216  *   bit 2: if set, a ' ' is included for positive nubmers
217  *   bit 3: if set, a '0b', '0o', or '0x' is included for binary, octal, hex
218  *   bit 4: if set, no prefix is included for binary, octal, hex
219  *
220  * Note: if neither bit 3 or 4 is set, prefixes that match the platform
221  * default are included.
222  *
223  * If base < 0, capital letters are used.
224  *
225  * If which = 0, then mpz formatting is used (if bit 0 set). Otherwise xmpz
226  * formatting is used (if bit 0 is set).
227  */
228 
229 static char* _ztag = "mpz(";
230 static char* _xztag = "xmpz(";
231 
232 static PyObject *
mpz_ascii(mpz_t z,int base,int option,int which)233 mpz_ascii(mpz_t z, int base, int option, int which)
234 {
235     PyObject *result;
236     char *buffer, *p;
237     int negative = 0;
238     size_t size;
239 
240     if (
241         !(
242           ((base >= -36) && (base <= -2)) ||
243           ((base >= 2) && (base <= 62))
244          )
245        ) {
246         VALUE_ERROR("base must be in the interval 2 ... 62");
247         return NULL;
248     }
249 
250     /* Allocate extra space for:
251      *
252      * minus sign and trailing NULL byte (2)
253      * 'xmpz()' tag                      (6)
254      * '0x' prefix                       (2)
255      *                                  -----
256      *                                   10
257      *
258      * And add one more to be sure...
259      */
260 
261     size = mpz_sizeinbase(z, (base < 0 ? -base : base)) + 11;
262     TEMP_ALLOC(buffer, size);
263 
264     if (mpz_sgn(z) < 0) {
265         negative = 1;
266         mpz_neg(z, z);
267     }
268 
269     p = buffer;
270     if (option & 1) {
271         if (which)
272             strcpy(p, _xztag);
273         else
274             strcpy(p, _ztag);
275         p += strlen(p);
276     }
277 
278     if (negative) {
279         *(p++) = '-';
280     }
281     else {
282         if (option & 2)
283             *(p++) = '+';
284         else if (option & 4)
285             *(p++) = ' ';
286     }
287 
288     if (option & 8) {
289         if (base == 2)        { *(p++) = '0'; *(p++) = 'b'; }
290         else if (base == 8)   { *(p++) = '0'; *(p++) = 'o'; }
291         else if (base == 16)  { *(p++) = '0'; *(p++) = 'x'; }
292         else if (base == -16) { *(p++) = '0'; *(p++) = 'X'; }
293     }
294     else if (!(option & 24)) {
295     #ifdef PY2
296         if (base == 8)        { *(p++) = '0'; }
297     #else
298         if (base == 2)        { *(p++) = '0'; *(p++) = 'b'; }
299         else if (base == 8)   { *(p++) = '0'; *(p++) = 'o'; }
300     #endif
301         else if (base == 16)  { *(p++) = '0'; *(p++) = 'x'; }
302         else if (base == -16) { *(p++) = '0'; *(p++) = 'X'; }
303     }
304 
305     /* Call GMP. */
306     mpz_get_str(p, base, z);
307     p = buffer + strlen(buffer);
308 
309     if (option & 1)
310         *(p++) = ')';
311     *(p++) = '\00';
312 
313     result = Py_BuildValue("s", buffer);
314     if (negative == 1) {
315         mpz_neg(z, z);
316     }
317     TEMP_FREE(buffer, size);
318     return result;
319 }
320