diff options
56 files changed, 1588 insertions, 197 deletions
diff --git a/README.txt b/README.txt index b331ec019..f7a8e2a37 100644 --- a/README.txt +++ b/README.txt @@ -10,7 +10,7 @@ It derives from the old Numeric code base and can be used as a replacement for N More information can be found at the website: -http://scipy.org/NumPy +http://www.numpy.org After installation, tests can be run with: @@ -23,5 +23,3 @@ The most current development version is always available from our git repository: http://github.com/numpy/numpy - - @@ -18,6 +18,12 @@ import subprocess import __builtin__ __builtin__.__NUMPY_SETUP__ = True +import waflib + +from numpy.distutils.conv_template \ + import \ + process_str as process_c_str + from bento.commands import hooks from bento.utils.utils \ import \ @@ -81,3 +87,25 @@ def pre_sdist(context): @hooks.options def options(global_context): blas_lapack.add_options(global_context) + + +class CTemplateTask(waflib.Task.Task): + color = 'BLUE' + before = ['c'] + def run(self): + s = self.inputs[0] + cnt = s.read() + writestr = process_c_str(cnt) + o = self.outputs[0] + o.write(writestr) + +@waflib.TaskGen.extension(".src") +def c_template(self, node): + outs = [] + outs.append(node.change_ext("")) + + tsk = self.create_task('CTemplateTask', node, outs) + if "c" in self.features: + self.source.append(outs[0]) + + diff --git a/doc/release/1.8.0-notes.rst b/doc/release/1.8.0-notes.rst index 95bad26cc..c5c9d6e8d 100644 --- a/doc/release/1.8.0-notes.rst +++ b/doc/release/1.8.0-notes.rst @@ -113,6 +113,14 @@ causes the returned array to be inverted. C-API ~~~~~ +New ufuncs can now be registered with built in input types and a custom +output type. Before this change, NumPy wouldn't be able to find the right +ufunc loop function when the ufunc was called from Python, because the ufunc +loop signature matching logic wasn't looking at the output operand type. +Now the correct ufunc loop is found, as long as the user provides an output +argument with the correct output type. + + Changes ======= diff --git a/doc/sphinxext/numpydoc/docscrape_sphinx.py b/doc/sphinxext/numpydoc/docscrape_sphinx.py index 2061f3f10..03c37a4a7 100644 --- a/doc/sphinxext/numpydoc/docscrape_sphinx.py +++ b/doc/sphinxext/numpydoc/docscrape_sphinx.py @@ -1,6 +1,6 @@ from __future__ import division, absolute_import, print_function -import re, inspect, textwrap, pydoc +import sys, re, inspect, textwrap, pydoc import sphinx import collections from .docscrape import NumpyDocString, FunctionDoc, ClassDoc diff --git a/doc/sphinxext/numpydoc/numpydoc.py b/doc/sphinxext/numpydoc/numpydoc.py index b8a5e959c..4f5f716c4 100644 --- a/doc/sphinxext/numpydoc/numpydoc.py +++ b/doc/sphinxext/numpydoc/numpydoc.py @@ -150,6 +150,7 @@ class NumpyPythonDomain(ManglingDomainBase, PythonDomain): 'staticmethod': 'function', 'attribute': 'attribute', } + indices = [] class NumpyCDomain(ManglingDomainBase, CDomain): name = 'np-c' diff --git a/doc/sphinxext/numpydoc/tests/test_docscrape.py b/doc/sphinxext/numpydoc/tests/test_docscrape.py index 45bedc88a..7b7375a5a 100644 --- a/doc/sphinxext/numpydoc/tests/test_docscrape.py +++ b/doc/sphinxext/numpydoc/tests/test_docscrape.py @@ -136,7 +136,7 @@ def test_parameters(): assert_equal([n for n,_,_ in doc['Parameters']], ['mean','cov','shape']) arg, arg_type, desc = doc['Parameters'][1] - assert_equal(arg_type, '(N,N) ndarray') + assert_equal(arg_type, '(N, N) ndarray') assert desc[0].startswith('Covariance matrix') assert doc['Parameters'][0][-1][-2] == ' (1+2+3)/3' @@ -176,8 +176,8 @@ def test_index(): def non_blank_line_by_line_compare(a,b): a = textwrap.dedent(a) b = textwrap.dedent(b) - a = [l for l in a.split('\n') if l.strip()] - b = [l for l in b.split('\n') if l.strip()] + a = [l.rstrip() for l in a.split('\n') if l.strip()] + b = [l.rstrip() for l in b.split('\n') if l.strip()] for n,line in enumerate(a): if not line == b[n]: raise AssertionError("Lines %s of a and b differ: " @@ -313,7 +313,7 @@ of the one-dimensional normal distribution to higher dimensions. (1+2+3)/3 - **cov** : (N,N) ndarray + **cov** : (N, N) ndarray Covariance matrix of the distribution. @@ -563,10 +563,7 @@ def test_unicode(): """) assert isinstance(doc['Summary'][0], str) - if sys.version_info[0] >= 3: - assert doc['Summary'][0] == sixu('öäöäöäöäöåååå') - else: - assert doc['Summary'][0] == sixu('öäöäöäöäöåååå').encode('utf-8') + assert doc['Summary'][0] == 'öäöäöäöäöåååå' def test_plot_examples(): cfg = dict(use_plots=True) diff --git a/doc/sphinxext/numpydoc/traitsdoc.py b/doc/sphinxext/numpydoc/traitsdoc.py index c5f0e7912..596c54eb3 100644 --- a/doc/sphinxext/numpydoc/traitsdoc.py +++ b/doc/sphinxext/numpydoc/traitsdoc.py @@ -18,6 +18,7 @@ from __future__ import division, absolute_import, print_function import inspect import os import pydoc +import collections from . import docscrape from . import docscrape_sphinx diff --git a/numpy/core/bscript b/numpy/core/bscript index 3a2bb2340..44f7f14f0 100644 --- a/numpy/core/bscript +++ b/numpy/core/bscript @@ -15,9 +15,6 @@ waflib.Logs.verbose = 1 # context. import numpy.build_utils.waf -from numpy.distutils.conv_template \ - import \ - process_str as process_c_str from code_generators.numpy_api \ import \ multiarray_api, ufunc_api @@ -325,25 +322,6 @@ def post_configure(context): # FIXME: Should be handled in bento context conf.store() -class CTemplateTask(waflib.Task.Task): - color = 'BLUE' - before = ['c'] - def run(self): - s = self.inputs[0] - cnt = s.read() - writestr = process_c_str(cnt) - o = self.outputs[0] - o.write(writestr) - -@waflib.TaskGen.extension(".src") -def c_template(self, node): - outs = [] - outs.append(node.change_ext("")) - - tsk = self.create_task('CTemplateTask', node, outs) - if "c" in self.features: - self.source.append(outs[0]) - class numpy_api_generator(Task): vars = ["API_TUPLE"] color = "BLUE" diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py index 549431306..ad711f888 100644 --- a/numpy/core/code_generators/generate_umath.py +++ b/numpy/core/code_generators/generate_umath.py @@ -836,8 +836,7 @@ def make_arrays(funcdict): # code1list = [] code2list = [] - names = list(funcdict.keys()) - names.sort() + names = sorted(funcdict.keys()) for name in names: uf = funcdict[name] funclist = [] @@ -902,8 +901,7 @@ def make_arrays(funcdict): def make_ufuncs(funcdict): code3list = [] - names = list(funcdict.keys()) - names.sort() + names = sorted(funcdict.keys()) for name in names: uf = funcdict[name] mlist = [] diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py index 84e45a6a5..c34348e22 100644 --- a/numpy/core/fromnumeric.py +++ b/numpy/core/fromnumeric.py @@ -133,16 +133,23 @@ def reshape(a, newshape, order='C'): One shape dimension can be -1. In this case, the value is inferred from the length of the array and remaining dimensions. order : {'C', 'F', 'A'}, optional - Determines whether the array data should be viewed as in C - (row-major) order, FORTRAN (column-major) order, or the C/FORTRAN - order should be preserved. + Read the elements of `a` using this index order, and place the elements + into the reshaped array using this index order. 'C' means to + read / write the elements using C-like index order, with the last axis index + changing fastest, back to the first axis index changing slowest. 'F' + means to read / write the elements using Fortran-like index order, with + the first index changing fastest, and the last index changing slowest. + Note that the 'C' and 'F' options take no account of the memory layout + of the underlying array, and only refer to the order of indexing. 'A' + means to read / write the elements in Fortran-like index order if `a` is + Fortran *contiguous* in memory, C-like order otherwise. Returns ------- reshaped_array : ndarray This will be a new view object if possible; otherwise, it will - be a copy. - + be a copy. Note there is no guarantee of the *memory layout* (C- or + Fortran- contiguous) of the returned array. See Also -------- @@ -157,12 +164,39 @@ def reshape(a, newshape, order='C'): >>> a = np.zeros((10, 2)) # A transpose make the array non-contiguous >>> b = a.T - # Taking a view makes it possible to modify the shape without modiying the + # Taking a view makes it possible to modify the shape without modifying the # initial object. >>> c = b.view() >>> c.shape = (20) AttributeError: incompatible shape for a non-contiguous array + The `order` keyword gives the index ordering both for *fetching* the values + from `a`, and then *placing* the values into the output array. For example, + let's say you have an array: + + >>> a = np.arange(6).reshape((3, 2)) + >>> a + array([[0, 1], + [2, 3], + [4, 5]]) + + You can think of reshaping as first raveling the array (using the given + index order), then inserting the elements from the raveled array into the + new array using the same kind of index ordering as was used for the + raveling. + + >>> np.reshape(a, (2, 3)) # C-like index ordering + array([[0, 1, 2], + [3, 4, 5]]) + >>> np.reshape(np.ravel(a), (2, 3)) # equivalent to C ravel then C reshape + array([[0, 1, 2], + [3, 4, 5]]) + >>> np.reshape(a, (2, 3), order='F') # Fortran-like index ordering + array([[0, 4, 3], + [2, 1, 5]]) + >>> np.reshape(np.ravel(a, order='F'), (2, 3), order='F') + array([[0, 4, 3], + [2, 1, 5]]) Examples -------- @@ -176,7 +210,6 @@ def reshape(a, newshape, order='C'): array([[1, 2], [3, 4], [5, 6]]) - """ try: reshape = a.reshape @@ -1107,16 +1140,20 @@ def ravel(a, order='C'): Parameters ---------- a : array_like - Input array. The elements in ``a`` are read in the order specified by + Input array. The elements in `a` are read in the order specified by `order`, and packed as a 1-D array. order : {'C','F', 'A', 'K'}, optional - The elements of ``a`` are read in this order. 'C' means to view - the elements in C (row-major) order. 'F' means to view the elements - in Fortran (column-major) order. 'A' means to view the elements - in 'F' order if a is Fortran contiguous, 'C' order otherwise. - 'K' means to view the elements in the order they occur in memory, - except for reversing the data when strides are negative. - By default, 'C' order is used. + The elements of `a` are read using this index order. 'C' means to + index the elements in C-like order, with the last axis index changing + fastest, back to the first axis index changing slowest. 'F' means to + index the elements in Fortran-like index order, with the first index + changing fastest, and the last index changing slowest. Note that the 'C' + and 'F' options take no account of the memory layout of the underlying + array, and only refer to the order of axis indexing. 'A' means to read + the elements in Fortran-like index order if `a` is Fortran *contiguous* + in memory, C-like order otherwise. 'K' means to read the elements in + the order they occur in memory, except for reversing the data when + strides are negative. By default, 'C' index order is used. Returns ------- @@ -1131,11 +1168,11 @@ def ravel(a, order='C'): Notes ----- - In row-major order, the row index varies the slowest, and the column - index the quickest. This can be generalized to multiple dimensions, - where row-major order implies that the index along the first axis - varies slowest, and the index along the last quickest. The opposite holds - for Fortran-, or column-major, mode. + In C-like (row-major) order, in two dimensions, the row index varies the + slowest, and the column index the quickest. This can be generalized to + multiple dimensions, where row-major order implies that the index along the + first axis varies slowest, and the index along the last quickest. The + opposite holds for Fortran-like, or column-major, index ordering. Examples -------- @@ -1515,7 +1552,7 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=False): out[...] = res return out return res - elif not (type(a) is mu.ndarray): + elif type(a) is not mu.ndarray: try: sum = a.sum except AttributeError: @@ -1916,7 +1953,7 @@ def amax(a, axis=None, out=None, keepdims=False): 4.0 """ - if not (type(a) is mu.ndarray): + if type(a) is not mu.ndarray: try: amax = a.max except AttributeError: @@ -1987,7 +2024,7 @@ def amin(a, axis=None, out=None, keepdims=False): 0.0 """ - if not (type(a) is mu.ndarray): + if type(a) is not mu.ndarray: try: amin = a.min except AttributeError: @@ -2117,7 +2154,7 @@ def prod(a, axis=None, dtype=None, out=None, keepdims=False): True """ - if not (type(a) is mu.ndarray): + if type(a) is not mu.ndarray: try: prod = a.prod except AttributeError: @@ -2490,7 +2527,7 @@ def mean(a, axis=None, dtype=None, out=None, keepdims=False): 0.55000000074505806 """ - if not (type(a) is mu.ndarray): + if type(a) is not mu.ndarray: try: mean = a.mean return mean(axis=axis, dtype=dtype, out=out) @@ -2592,7 +2629,7 @@ def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): 0.44999999925552653 """ - if not (type(a) is mu.ndarray): + if type(a) is not mu.ndarray: try: std = a.std return std(axis=axis, dtype=dtype, out=out, ddof=ddof) @@ -2695,7 +2732,7 @@ def var(a, axis=None, dtype=None, out=None, ddof=0, 0.20250000000000001 """ - if not (type(a) is mu.ndarray): + if type(a) is not mu.ndarray: try: var = a.var return var(axis=axis, dtype=dtype, out=out, ddof=ddof) diff --git a/numpy/core/records.py b/numpy/core/records.py index 4fe1f8444..d0f82a25c 100644 --- a/numpy/core/records.py +++ b/numpy/core/records.py @@ -173,7 +173,7 @@ class format_parser: if (names): if (type(names) in [list, tuple]): pass - elif (type(names) == str): + elif isinstance(names, str): names = names.split(',') else: raise NameError("illegal input names %s" % repr(names)) diff --git a/numpy/core/setup.py b/numpy/core/setup.py index 0b2ecfe67..6df82b3fa 100644 --- a/numpy/core/setup.py +++ b/numpy/core/setup.py @@ -922,6 +922,13 @@ def configuration(parent_package='',top_path=None): sources = [join('src','umath', 'umath_tests.c.src')]) ####################################################################### + # custom rational dtype module # + ####################################################################### + + config.add_extension('test_rational', + sources = [join('src','umath', 'test_rational.c.src')]) + + ####################################################################### # multiarray_tests module # ####################################################################### diff --git a/numpy/core/src/multiarray/common.c b/numpy/core/src/multiarray/common.c index f0a6a761c..4f25dc913 100644 --- a/numpy/core/src/multiarray/common.c +++ b/numpy/core/src/multiarray/common.c @@ -147,9 +147,7 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims, int i, size; PyArray_Descr *dtype = NULL; PyObject *ip; -#if PY_VERSION_HEX >= 0x02060000 Py_buffer buffer_view; -#endif /* Check if it's an ndarray */ if (PyArray_Check(obj)) { @@ -309,7 +307,6 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims, goto promote_types; } -#if PY_VERSION_HEX >= 0x02060000 /* PEP 3118 buffer interface */ memset(&buffer_view, 0, sizeof(Py_buffer)); if (PyObject_GetBuffer(obj, &buffer_view, PyBUF_FORMAT|PyBUF_STRIDES) == 0 || @@ -334,7 +331,6 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims, else { PyErr_Clear(); } -#endif /* The array interface */ ip = PyObject_GetAttrString(obj, "__array_interface__"); diff --git a/numpy/core/src/umath/test_rational.c.src b/numpy/core/src/umath/test_rational.c.src new file mode 100644 index 000000000..aca3d21f3 --- /dev/null +++ b/numpy/core/src/umath/test_rational.c.src @@ -0,0 +1,1322 @@ +/* Fixed size rational numbers exposed to Python */ + +#define NPY_NO_DEPRECATED_API NPY_API_VERSION + +#include <stdint.h> +#include <math.h> +#include <Python.h> +#include <structmember.h> +#include <numpy/arrayobject.h> +#include <numpy/ufuncobject.h> +#include "numpy/npy_3kcompat.h" + +/* Relevant arithmetic exceptions */ + +/* Uncomment the following line to work around a bug in numpy */ +/* #define ACQUIRE_GIL */ + +static void +set_overflow(void) { +#ifdef ACQUIRE_GIL + /* Need to grab the GIL to dodge a bug in numpy */ + PyGILState_STATE state = PyGILState_Ensure(); +#endif + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_OverflowError, + "overflow in rational arithmetic"); + } +#ifdef ACQUIRE_GIL + PyGILState_Release(state); +#endif +} + +static void +set_zero_divide(void) { +#ifdef ACQUIRE_GIL + /* Need to grab the GIL to dodge a bug in numpy */ + PyGILState_STATE state = PyGILState_Ensure(); +#endif + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_ZeroDivisionError, + "zero divide in rational arithmetic"); + } +#ifdef ACQUIRE_GIL + PyGILState_Release(state); +#endif +} + +/* Integer arithmetic utilities */ + +static NPY_INLINE int32_t +safe_neg(int32_t x) { + if (x==(int32_t)1<<31) { + set_overflow(); + } + return -x; +} + +static NPY_INLINE int32_t +safe_abs32(int32_t x) { + if (x>=0) { + return x; + } + int32_t nx = -x; + if (nx<0) { + set_overflow(); + } + return nx; +} + +static NPY_INLINE int64_t +safe_abs64(int64_t x) { + if (x>=0) { + return x; + } + int64_t nx = -x; + if (nx<0) { + set_overflow(); + } + return nx; +} + +static NPY_INLINE int64_t +gcd(int64_t x, int64_t y) { + x = safe_abs64(x); + y = safe_abs64(y); + if (x < y) { + int64_t t = x; + x = y; + y = t; + } + while (y) { + x = x%y; + int64_t t = x; + x = y; + y = t; + } + return x; +} + +static NPY_INLINE int64_t +lcm(int64_t x, int64_t y) { + if (!x || !y) { + return 0; + } + x /= gcd(x,y); + int64_t lcm = x*y; + if (lcm/y!=x) { + set_overflow(); + } + return safe_abs64(lcm); +} + +/* Fixed precision rational numbers */ + +typedef struct { + /* numerator */ + int32_t n; + /* + * denominator minus one: numpy.zeros() uses memset(0) for non-object + * types, so need to ensure that rational(0) has all zero bytes + */ + int32_t dmm; +} rational; + +static NPY_INLINE rational +make_rational_int(int64_t n) { + rational r = {n,0}; + if (r.n != n) { + set_overflow(); + } + return r; +} + +static rational +make_rational_slow(int64_t n_, int64_t d_) { + rational r = {0}; + if (!d_) { + set_zero_divide(); + } + else { + int64_t g = gcd(n_,d_); + n_ /= g; + d_ /= g; + r.n = n_; + int32_t d = d_; + if (r.n!=n_ || d!=d_) { + set_overflow(); + } + else { + if (d <= 0) { + d = -d; + r.n = safe_neg(r.n); + } + r.dmm = d-1; + } + } + return r; +} + +static NPY_INLINE int32_t +d(rational r) { + return r.dmm+1; +} + +/* Assumes d_ > 0 */ +static rational +make_rational_fast(int64_t n_, int64_t d_) { + int64_t g = gcd(n_,d_); + n_ /= g; + d_ /= g; + rational r; + r.n = n_; + r.dmm = d_-1; + if (r.n!=n_ || r.dmm+1!=d_) { + set_overflow(); + } + return r; +} + +static NPY_INLINE rational +rational_negative(rational r) { + rational x; + x.n = safe_neg(r.n); + x.dmm = r.dmm; + return x; +} + +static NPY_INLINE rational +rational_add(rational x, rational y) { + /* + * Note that the numerator computation can never overflow int128_t, + * since each term is strictly under 2**128/4 (since d > 0). + */ + return make_rational_fast((int64_t)x.n*d(y)+(int64_t)d(x)*y.n, + (int64_t)d(x)*d(y)); +} + +static NPY_INLINE rational +rational_subtract(rational x, rational y) { + /* We're safe from overflow as with + */ + return make_rational_fast((int64_t)x.n*d(y)-(int64_t)d(x)*y.n, + (int64_t)d(x)*d(y)); +} + +static NPY_INLINE rational +rational_multiply(rational x, rational y) { + /* We're safe from overflow as with + */ + return make_rational_fast((int64_t)x.n*y.n,(int64_t)d(x)*d(y)); +} + +static NPY_INLINE rational +rational_divide(rational x, rational y) { + return make_rational_slow((int64_t)x.n*d(y),(int64_t)d(x)*y.n); +} + +static NPY_INLINE int64_t +rational_floor(rational x) { + /* Always round down */ + if (x.n>=0) { + return x.n/d(x); + } + /* + * This can be done without casting up to 64 bits, but it requires + * working out all the sign cases + */ + return -((-(int64_t)x.n+d(x)-1)/d(x)); +} + +static NPY_INLINE int64_t +rational_ceil(rational x) { + return -rational_floor(rational_negative(x)); +} + +static NPY_INLINE rational +rational_remainder(rational x, rational y) { + return rational_subtract(x, rational_multiply(y,make_rational_int( + rational_floor(rational_divide(x,y))))); +} + +static NPY_INLINE rational +rational_abs(rational x) { + rational y; + y.n = safe_abs32(x.n); + y.dmm = x.dmm; + return y; +} + +static NPY_INLINE int64_t +rational_rint(rational x) { + /* + * Round towards nearest integer, moving exact half integers towards + * zero + */ + int32_t d_ = d(x); + return (2*(int64_t)x.n+(x.n<0?-d_:d_))/(2*(int64_t)d_); +} + +static NPY_INLINE int +rational_sign(rational x) { + return x.n<0?-1:x.n==0?0:1; +} + +static NPY_INLINE rational +rational_inverse(rational x) { + rational y = {0}; + if (!x.n) { + set_zero_divide(); + } + else { + y.n = d(x); + int32_t d = x.n; + if (d <= 0) { + d = safe_neg(d); + y.n = -y.n; + } + y.dmm = d-1; + } + return y; +} + +static NPY_INLINE int +rational_eq(rational x, rational y) { + /* + * Since we enforce d > 0, and store fractions in reduced form, + * equality is easy. + */ + return x.n==y.n && x.dmm==y.dmm; +} + +static NPY_INLINE int +rational_ne(rational x, rational y) { + return !rational_eq(x,y); +} + +static NPY_INLINE int +rational_lt(rational x, rational y) { + return (int64_t)x.n*d(y) < (int64_t)y.n*d(x); +} + +static NPY_INLINE int +rational_gt(rational x, rational y) { + return rational_lt(y,x); +} + +static NPY_INLINE int +rational_le(rational x, rational y) { + return !rational_lt(y,x); +} + +static NPY_INLINE int +rational_ge(rational x, rational y) { + return !rational_lt(x,y); +} + +static NPY_INLINE int32_t +rational_int(rational x) { + return x.n/d(x); +} + +static NPY_INLINE double +rational_double(rational x) { + return (double)x.n/d(x); +} + +static NPY_INLINE int +rational_nonzero(rational x) { + return x.n!=0; +} + +static int +scan_rational(const char** s, rational* x) { + long n,d; + int offset; + if (sscanf(*s,"%ld%n",&n,&offset)<=0) { + return 0; + } + const char* ss = *s+offset; + if (*ss!='/') { + *s = ss; + *x = make_rational_int(n); + return 1; + } + ss++; + if (sscanf(ss,"%ld%n",&d,&offset)<=0 || d<=0) { + return 0; + } + *s = ss+offset; + *x = make_rational_slow(n,d); + return 1; +} + +/* Expose rational to Python as a numpy scalar */ + +typedef struct { + PyObject_HEAD; + rational r; +} PyRational; + +static PyTypeObject PyRational_Type; + +static NPY_INLINE int +PyRational_Check(PyObject* object) { + return PyObject_IsInstance(object,(PyObject*)&PyRational_Type); +} + +static PyObject* +PyRational_FromRational(rational x) { + PyRational* p = (PyRational*)PyRational_Type.tp_alloc(&PyRational_Type,0); + if (p) { + p->r = x; + } + return (PyObject*)p; +} + +static PyObject* +pyrational_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { + if (kwds && PyDict_Size(kwds)) { + PyErr_SetString(PyExc_TypeError, + "constructor takes no keyword arguments"); + return 0; + } + Py_ssize_t size = PyTuple_GET_SIZE(args); + if (size>2) { + PyErr_SetString(PyExc_TypeError, + "expected rational or numerator and optional denominator"); + return 0; + } + PyObject* x[2] = {PyTuple_GET_ITEM(args,0),PyTuple_GET_ITEM(args,1)}; + if (size==1) { + if (PyRational_Check(x[0])) { + Py_INCREF(x[0]); + return x[0]; + } + else if (PyString_Check(x[0])) { + const char* s = PyString_AS_STRING(x[0]); + rational x; + if (scan_rational(&s,&x)) { + const char* p; + for (p = s; *p; p++) { + if (!isspace(*p)) { + goto bad; + } + } + return PyRational_FromRational(x); + } + bad: + PyErr_Format(PyExc_ValueError, + "invalid rational literal '%s'",s); + return 0; + } + } + long n[2]={0,1}; + int i; + for (i=0;i<size;i++) { + n[i] = PyInt_AsLong(x[i]); + if (n[i]==-1 && PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + PyErr_Format(PyExc_TypeError, + "expected integer %s, got %s", + (i ? "denominator" : "numerator"), + x[i]->ob_type->tp_name); + } + return 0; + } + /* Check that we had an exact integer */ + PyObject* y = PyInt_FromLong(n[i]); + if (!y) { + return 0; + } + int eq = PyObject_RichCompareBool(x[i],y,Py_EQ); + Py_DECREF(y); + if (eq<0) { + return 0; + } + if (!eq) { + PyErr_Format(PyExc_TypeError, + "expected integer %s, got %s", + (i ? "denominator" : "numerator"), + x[i]->ob_type->tp_name); + return 0; + } + } + rational r = make_rational_slow(n[0],n[1]); + if (PyErr_Occurred()) { + return 0; + } + return PyRational_FromRational(r); +} + +/* + * Returns Py_NotImplemented on most conversion failures, or raises an + * overflow error for too long ints + */ +#define AS_RATIONAL(dst,object) \ + rational dst = {0}; \ + if (PyRational_Check(object)) { \ + dst = ((PyRational*)object)->r; \ + } \ + else { \ + long n_ = PyInt_AsLong(object); \ + if (n_==-1 && PyErr_Occurred()) { \ + if (PyErr_ExceptionMatches(PyExc_TypeError)) { \ + PyErr_Clear(); \ + Py_INCREF(Py_NotImplemented); \ + return Py_NotImplemented; \ + } \ + return 0; \ + } \ + PyObject* y_ = PyInt_FromLong(n_); \ + if (!y_) { \ + return 0; \ + } \ + int eq_ = PyObject_RichCompareBool(object,y_,Py_EQ); \ + Py_DECREF(y_); \ + if (eq_<0) { \ + return 0; \ + } \ + if (!eq_) { \ + Py_INCREF(Py_NotImplemented); \ + return Py_NotImplemented; \ + } \ + dst = make_rational_int(n_); \ + } + +static PyObject* +pyrational_richcompare(PyObject* a, PyObject* b, int op) { + AS_RATIONAL(x,a); + AS_RATIONAL(y,b); + int result = 0; + #define OP(py,op) case py: result = rational_##op(x,y); break; + switch (op) { + OP(Py_LT,lt) + OP(Py_LE,le) + OP(Py_EQ,eq) + OP(Py_NE,ne) + OP(Py_GT,gt) + OP(Py_GE,ge) + }; + #undef OP + return PyBool_FromLong(result); +} + +static PyObject* +pyrational_repr(PyObject* self) { + rational x = ((PyRational*)self)->r; + if (d(x)!=1) { + return PyUString_FromFormat( + "rational(%ld,%ld)",(long)x.n,(long)d(x)); + } + else { + return PyUString_FromFormat( + "rational(%ld)",(long)x.n); + } +} + +static PyObject* +pyrational_str(PyObject* self) { + rational x = ((PyRational*)self)->r; + if (d(x)!=1) { + return PyString_FromFormat( + "%ld/%ld",(long)x.n,(long)d(x)); + } + else { + return PyString_FromFormat( + "%ld",(long)x.n); + } +} + +static long +pyrational_hash(PyObject* self) { + rational x = ((PyRational*)self)->r; + /* Use a fairly weak hash as Python expects */ + long h = 131071*x.n+524287*x.dmm; + /* Never return the special error value -1 */ + return h==-1?2:h; +} + +#define RATIONAL_BINOP_2(name,exp) \ + static PyObject* \ + pyrational_##name(PyObject* a, PyObject* b) { \ + AS_RATIONAL(x,a); \ + AS_RATIONAL(y,b); \ + rational z = exp; \ + if (PyErr_Occurred()) { \ + return 0; \ + } \ + return PyRational_FromRational(z); \ + } +#define RATIONAL_BINOP(name) RATIONAL_BINOP_2(name,rational_##name(x,y)) +RATIONAL_BINOP(add) +RATIONAL_BINOP(subtract) +RATIONAL_BINOP(multiply) +RATIONAL_BINOP(divide) +RATIONAL_BINOP(remainder) +RATIONAL_BINOP_2(floor_divide, + make_rational_int(rational_floor(rational_divide(x,y)))) + +#define RATIONAL_UNOP(name,type,exp,convert) \ + static PyObject* \ + pyrational_##name(PyObject* self) { \ + rational x = ((PyRational*)self)->r; \ + type y = exp; \ + if (PyErr_Occurred()) { \ + return 0; \ + } \ + return convert(y); \ + } +RATIONAL_UNOP(negative,rational,rational_negative(x),PyRational_FromRational) +RATIONAL_UNOP(absolute,rational,rational_abs(x),PyRational_FromRational) +RATIONAL_UNOP(int,long,rational_int(x),PyInt_FromLong) +RATIONAL_UNOP(float,double,rational_double(x),PyFloat_FromDouble) + +static PyObject* +pyrational_positive(PyObject* self) { + Py_INCREF(self); + return self; +} + +static int +pyrational_nonzero(PyObject* self) { + rational x = ((PyRational*)self)->r; + return rational_nonzero(x); +} + +static PyNumberMethods pyrational_as_number = { + pyrational_add, /* nb_add */ + pyrational_subtract, /* nb_subtract */ + pyrational_multiply, /* nb_multiply */ + pyrational_divide, /* nb_divide */ + pyrational_remainder, /* nb_remainder */ + 0, /* nb_divmod */ + 0, /* nb_power */ + pyrational_negative, /* nb_negative */ + pyrational_positive, /* nb_positive */ + pyrational_absolute, /* nb_absolute */ + pyrational_nonzero, /* nb_nonzero */ + 0, /* nb_invert */ + 0, /* nb_lshift */ + 0, /* nb_rshift */ + 0, /* nb_and */ + 0, /* nb_xor */ + 0, /* nb_or */ + 0, /* nb_coerce */ + pyrational_int, /* nb_int */ + pyrational_int, /* nb_long */ + pyrational_float, /* nb_float */ + 0, /* nb_oct */ + 0, /* nb_hex */ + + 0, /* nb_inplace_add */ + 0, /* nb_inplace_subtract */ + 0, /* nb_inplace_multiply */ + 0, /* nb_inplace_divide */ + 0, /* nb_inplace_remainder */ + 0, /* nb_inplace_power */ + 0, /* nb_inplace_lshift */ + 0, /* nb_inplace_rshift */ + 0, /* nb_inplace_and */ + 0, /* nb_inplace_xor */ + 0, /* nb_inplace_or */ + + pyrational_floor_divide, /* nb_floor_divide */ + pyrational_divide, /* nb_true_divide */ + 0, /* nb_inplace_floor_divide */ + 0, /* nb_inplace_true_divide */ + 0, /* nb_index */ +}; + +static PyObject* +pyrational_n(PyObject* self, void* closure) { + return PyInt_FromLong(((PyRational*)self)->r.n); +} + +static PyObject* +pyrational_d(PyObject* self, void* closure) { + return PyInt_FromLong(d(((PyRational*)self)->r)); +} + +static PyGetSetDef pyrational_getset[] = { + {(char*)"n",pyrational_n,0,(char*)"numerator",0}, + {(char*)"d",pyrational_d,0,(char*)"denominator",0}, + {0} /* sentinel */ +}; + +static PyTypeObject PyRational_Type = { +#if defined(NPY_PY3K) + PyVarObject_HEAD_INIT(&PyType_Type, 0) +#else + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ +#endif + "rational", /* tp_name */ + sizeof(PyRational), /* tp_basicsize */ + 0, /* tp_itemsize */ + 0, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ +#if defined(NPY_PY3K) + 0, /* tp_reserved */ +#else + 0, /* tp_compare */ +#endif + pyrational_repr, /* tp_repr */ + &pyrational_as_number, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + pyrational_hash, /* tp_hash */ + 0, /* tp_call */ + pyrational_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Fixed precision rational numbers", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + pyrational_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + pyrational_getset, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + pyrational_new, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ +#if PY_VERSION_HEX >= 0x02060000 + 0, /* tp_version_tag */ +#endif +}; + +/* Numpy support */ + +static PyObject* +npyrational_getitem(void* data, void* arr) { + rational r; + memcpy(&r,data,sizeof(rational)); + return PyRational_FromRational(r); +} + +static int +npyrational_setitem(PyObject* item, void* data, void* arr) { + rational r; + if (PyRational_Check(item)) { + r = ((PyRational*)item)->r; + } + else { + long n = PyInt_AsLong(item); + if (n==-1 && PyErr_Occurred()) { + return -1; + } + PyObject* y = PyInt_FromLong(n); + if (!y) { + return -1; + } + int eq = PyObject_RichCompareBool(item,y,Py_EQ); + Py_DECREF(y); + if (eq<0) { + return -1; + } + if (!eq) { + PyErr_Format(PyExc_TypeError, + "expected rational, got %s", item->ob_type->tp_name); + return -1; + } + r = make_rational_int(n); + } + memcpy(data,&r,sizeof(rational)); + return 0; +} + +static NPY_INLINE void +byteswap(int32_t* x) { + char* p = (char*)x; + size_t i; + for (i = 0; i < sizeof(*x)/2; i++) { + int j = sizeof(*x)-1-i; + char t = p[i]; + p[i] = p[j]; + p[j] = t; + } +} + +static void +npyrational_copyswapn(void* dst_, npy_intp dstride, void* src_, + npy_intp sstride, npy_intp n, int swap, void* arr) { + char *dst = (char*)dst_, *src = (char*)src_; + if (!src) { + return; + } + npy_intp i; + if (swap) { + for (i = 0; i < n; i++) { + rational* r = (rational*)(dst+dstride*i); + memcpy(r,src+sstride*i,sizeof(rational)); + byteswap(&r->n); + byteswap(&r->dmm); + } + } + else if (dstride == sizeof(rational) && sstride == sizeof(rational)) { + memcpy(dst, src, n*sizeof(rational)); + } + else { + for (i = 0; i < n; i++) { + memcpy(dst + dstride*i, src + sstride*i, sizeof(rational)); + } + } +} + +static void +npyrational_copyswap(void* dst, void* src, int swap, void* arr) { + if (!src) { + return; + } + rational* r = (rational*)dst; + memcpy(r,src,sizeof(rational)); + if (swap) { + byteswap(&r->n); + byteswap(&r->dmm); + } +} + +static int +npyrational_compare(const void* d0, const void* d1, void* arr) { + rational x = *(rational*)d0, + y = *(rational*)d1; + return rational_lt(x,y)?-1:rational_eq(x,y)?0:1; +} + +#define FIND_EXTREME(name,op) \ + static int \ + npyrational_##name(void* data_, npy_intp n, \ + npy_intp* max_ind, void* arr) { \ + if (!n) { \ + return 0; \ + } \ + const rational* data = (rational*)data_; \ + npy_intp best_i = 0; \ + rational best_r = data[0]; \ + npy_intp i; \ + for (i = 1; i < n; i++) { \ + if (rational_##op(data[i],best_r)) { \ + best_i = i; \ + best_r = data[i]; \ + } \ + } \ + *max_ind = best_i; \ + return 0; \ + } +FIND_EXTREME(argmin,lt) +FIND_EXTREME(argmax,gt) + +static void +npyrational_dot(void* ip0_, npy_intp is0, void* ip1_, npy_intp is1, + void* op, npy_intp n, void* arr) { + rational r = {0}; + const char *ip0 = (char*)ip0_, *ip1 = (char*)ip1_; + npy_intp i; + for (i = 0; i < n; i++) { + r = rational_add(r,rational_multiply(*(rational*)ip0,*(rational*)ip1)); + ip0 += is0; + ip1 += is1; + } + *(rational*)op = r; +} + +static npy_bool +npyrational_nonzero(void* data, void* arr) { + rational r; + memcpy(&r,data,sizeof(r)); + return rational_nonzero(r)?NPY_TRUE:NPY_FALSE; +} + +static int +npyrational_fill(void* data_, npy_intp length, void* arr) { + rational* data = (rational*)data_; + rational delta = rational_subtract(data[1],data[0]); + rational r = data[1]; + npy_intp i; + for (i = 2; i < length; i++) { + r = rational_add(r,delta); + data[i] = r; + } + return 0; +} + +static int +npyrational_fillwithscalar(void* buffer_, npy_intp length, + void* value, void* arr) { + rational r = *(rational*)value; + rational* buffer = (rational*)buffer_; + npy_intp i; + for (i = 0; i < length; i++) { + buffer[i] = r; + } + return 0; +} + +static PyArray_ArrFuncs npyrational_arrfuncs; + +typedef struct { char c; rational r; } align_test; + +PyArray_Descr npyrational_descr = { + PyObject_HEAD_INIT(0) + &PyRational_Type, /* typeobj */ + 'V', /* kind */ + 'r', /* type */ + '=', /* byteorder */ + /* + * For now, we need NPY_NEEDS_PYAPI in order to make numpy detect our + * exceptions. This isn't technically necessary, + * since we're careful about thread safety, and hopefully future + * versions of numpy will recognize that. + */ + NPY_NEEDS_PYAPI | NPY_USE_GETITEM | NPY_USE_SETITEM, /* hasobject */ + 0, /* type_num */ + sizeof(rational), /* elsize */ + offsetof(align_test,r), /* alignment */ + 0, /* subarray */ + 0, /* fields */ + 0, /* names */ + &npyrational_arrfuncs, /* f */ +}; + +#define DEFINE_CAST(From,To,statement) \ + static void \ + npycast_##From##_##To(void* from_, void* to_, npy_intp n, \ + void* fromarr, void* toarr) { \ + const From* from = (From*)from_; \ + To* to = (To*)to_; \ + npy_intp i; \ + for (i = 0; i < n; i++) { \ + From x = from[i]; \ + statement \ + to[i] = y; \ + } \ + } +#define DEFINE_INT_CAST(bits) \ + DEFINE_CAST(int##bits##_t,rational,rational y = make_rational_int(x);) \ + DEFINE_CAST(rational,int##bits##_t,int32_t z = rational_int(x); \ + int##bits##_t y = z; if (y != z) set_overflow();) +DEFINE_INT_CAST(8) +DEFINE_INT_CAST(16) +DEFINE_INT_CAST(32) +DEFINE_INT_CAST(64) +DEFINE_CAST(rational,float,double y = rational_double(x);) +DEFINE_CAST(rational,double,double y = rational_double(x);) +DEFINE_CAST(npy_bool,rational,rational y = make_rational_int(x);) +DEFINE_CAST(rational,npy_bool,npy_bool y = rational_nonzero(x);) + +#define BINARY_UFUNC(name,intype0,intype1,outtype,exp) \ + void name(char** args, npy_intp* dimensions, \ + npy_intp* steps, void* data) { \ + npy_intp is0 = steps[0], is1 = steps[1], \ + os = steps[2], n = *dimensions; \ + char *i0 = args[0], *i1 = args[1], *o = args[2]; \ + int k; \ + for (k = 0; k < n; k++) { \ + intype0 x = *(intype0*)i0; \ + intype1 y = *(intype1*)i1; \ + *(outtype*)o = exp; \ + i0 += is0; i1 += is1; o += os; \ + } \ + } +#define RATIONAL_BINARY_UFUNC(name,type,exp) \ + BINARY_UFUNC(rational_ufunc_##name,rational,rational,type,exp) +RATIONAL_BINARY_UFUNC(add,rational,rational_add(x,y)) +RATIONAL_BINARY_UFUNC(subtract,rational,rational_subtract(x,y)) +RATIONAL_BINARY_UFUNC(multiply,rational,rational_multiply(x,y)) +RATIONAL_BINARY_UFUNC(divide,rational,rational_divide(x,y)) +RATIONAL_BINARY_UFUNC(remainder,rational,rational_remainder(x,y)) +RATIONAL_BINARY_UFUNC(floor_divide,rational, + make_rational_int(rational_floor(rational_divide(x,y)))) +PyUFuncGenericFunction rational_ufunc_true_divide = rational_ufunc_divide; +RATIONAL_BINARY_UFUNC(minimum,rational,rational_lt(x,y)?x:y) +RATIONAL_BINARY_UFUNC(maximum,rational,rational_lt(x,y)?y:x) +RATIONAL_BINARY_UFUNC(equal,npy_bool,rational_eq(x,y)) +RATIONAL_BINARY_UFUNC(not_equal,npy_bool,rational_ne(x,y)) +RATIONAL_BINARY_UFUNC(less,npy_bool,rational_lt(x,y)) +RATIONAL_BINARY_UFUNC(greater,npy_bool,rational_gt(x,y)) +RATIONAL_BINARY_UFUNC(less_equal,npy_bool,rational_le(x,y)) +RATIONAL_BINARY_UFUNC(greater_equal,npy_bool,rational_ge(x,y)) + +BINARY_UFUNC(gcd_ufunc,int64_t,int64_t,int64_t,gcd(x,y)) +BINARY_UFUNC(lcm_ufunc,int64_t,int64_t,int64_t,lcm(x,y)) + +#define UNARY_UFUNC(name,type,exp) \ + void rational_ufunc_##name(char** args, npy_intp* dimensions, \ + npy_intp* steps, void* data) { \ + npy_intp is = steps[0], os = steps[1], n = *dimensions; \ + char *i = args[0], *o = args[1]; \ + int k; \ + for (k = 0; k < n; k++) { \ + rational x = *(rational*)i; \ + *(type*)o = exp; \ + i += is; o += os; \ + } \ + } +UNARY_UFUNC(negative,rational,rational_negative(x)) +UNARY_UFUNC(absolute,rational,rational_abs(x)) +UNARY_UFUNC(floor,rational,make_rational_int(rational_floor(x))) +UNARY_UFUNC(ceil,rational,make_rational_int(rational_ceil(x))) +UNARY_UFUNC(trunc,rational,make_rational_int(x.n/d(x))) +UNARY_UFUNC(square,rational,rational_multiply(x,x)) +UNARY_UFUNC(rint,rational,make_rational_int(rational_rint(x))) +UNARY_UFUNC(sign,rational,make_rational_int(rational_sign(x))) +UNARY_UFUNC(reciprocal,rational,rational_inverse(x)) +UNARY_UFUNC(numerator,int64_t,x.n) +UNARY_UFUNC(denominator,int64_t,d(x)) + +static NPY_INLINE void +rational_matrix_multiply(char **args, npy_intp *dimensions, npy_intp *steps) +{ + /* pointers to data for input and output arrays */ + char *ip1 = args[0]; + char *ip2 = args[1]; + char *op = args[2]; + + /* lengths of core dimensions */ + npy_intp dm = dimensions[0]; + npy_intp dn = dimensions[1]; + npy_intp dp = dimensions[2]; + + /* striding over core dimensions */ + npy_intp is1_m = steps[0]; + npy_intp is1_n = steps[1]; + npy_intp is2_n = steps[2]; + npy_intp is2_p = steps[3]; + npy_intp os_m = steps[4]; + npy_intp os_p = steps[5]; + + /* core dimensions counters */ + npy_intp m, p; + + /* calculate dot product for each row/column vector pair */ + for (m = 0; m < dm; m++) { + for (p = 0; p < dp; p++) { + npyrational_dot(ip1, is1_n, ip2, is2_n, op, dn, NULL); + + /* advance to next column of 2nd input array and output array */ + ip2 += is2_p; + op += os_p; + } + + /* reset to first column of 2nd input array and output array */ + ip2 -= is2_p * p; + op -= os_p * p; + + /* advance to next row of 1st input array and output array */ + ip1 += is1_m; + op += os_m; + } +} + + +static void +rational_gufunc_matrix_multiply(char **args, npy_intp *dimensions, + npy_intp *steps, void *NPY_UNUSED(func)) +{ + /* outer dimensions counter */ + npy_intp N_; + + /* length of flattened outer dimensions */ + npy_intp dN = dimensions[0]; + + /* striding over flattened outer dimensions for input and output arrays */ + npy_intp s0 = steps[0]; + npy_intp s1 = steps[1]; + npy_intp s2 = steps[2]; + + /* + * loop through outer dimensions, performing matrix multiply on + * core dimensions for each loop + */ + for (N_ = 0; N_ < dN; N_++, args[0] += s0, args[1] += s1, args[2] += s2) { + rational_matrix_multiply(args, dimensions+1, steps+3); + } +} + + +static void +rational_ufunc_test_add(char** args, npy_intp* dimensions, + npy_intp* steps, void* data) { + npy_intp is0 = steps[0], is1 = steps[1], os = steps[2], n = *dimensions; + char *i0 = args[0], *i1 = args[1], *o = args[2]; + int k; + for (k = 0; k < n; k++) { + int64_t x = *(int64_t*)i0; + int64_t y = *(int64_t*)i1; + *(rational*)o = rational_add(make_rational_fast(x, 1), + make_rational_fast(y, 1)); + i0 += is0; i1 += is1; o += os; + } +} + + +PyMethodDef module_methods[] = { + {0} /* sentinel */ +}; + +#if defined(NPY_PY3K) +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "test_rational", + NULL, + -1, + module_methods, + NULL, + NULL, + NULL, + NULL +}; +#endif + +#if defined(NPY_PY3K) +#define RETVAL m +PyMODINIT_FUNC PyInit_test_rational(void) { +#else +#define RETVAL +PyMODINIT_FUNC inittest_rational(void) { +#endif + + PyObject *m = NULL; + + import_array(); + if (PyErr_Occurred()) { + goto fail; + } + import_umath(); + if (PyErr_Occurred()) { + goto fail; + } + PyObject* numpy_str = PyUString_FromString("numpy"); + if (!numpy_str) { + goto fail; + } + PyObject* numpy = PyImport_Import(numpy_str); + Py_DECREF(numpy_str); + if (!numpy) { + goto fail; + } + + /* Can't set this until we import numpy */ + PyRational_Type.tp_base = &PyGenericArrType_Type; + + /* Initialize rational type object */ + if (PyType_Ready(&PyRational_Type) < 0) { + goto fail; + } + + /* Initialize rational descriptor */ + PyArray_InitArrFuncs(&npyrational_arrfuncs); + npyrational_arrfuncs.getitem = npyrational_getitem; + npyrational_arrfuncs.setitem = npyrational_setitem; + npyrational_arrfuncs.copyswapn = npyrational_copyswapn; + npyrational_arrfuncs.copyswap = npyrational_copyswap; + npyrational_arrfuncs.compare = npyrational_compare; + npyrational_arrfuncs.argmin = npyrational_argmin; + npyrational_arrfuncs.argmax = npyrational_argmax; + npyrational_arrfuncs.dotfunc = npyrational_dot; + npyrational_arrfuncs.nonzero = npyrational_nonzero; + npyrational_arrfuncs.fill = npyrational_fill; + npyrational_arrfuncs.fillwithscalar = npyrational_fillwithscalar; + /* Left undefined: scanfunc, fromstr, sort, argsort */ + Py_TYPE(&npyrational_descr) = &PyArrayDescr_Type; + int npy_rational = PyArray_RegisterDataType(&npyrational_descr); + if (npy_rational<0) { + goto fail; + } + + /* Support dtype(rational) syntax */ + if (PyDict_SetItemString(PyRational_Type.tp_dict, "dtype", + (PyObject*)&npyrational_descr) < 0) { + goto fail; + } + + /* Register casts to and from rational */ + #define REGISTER_CAST(From,To,from_descr,to_typenum,safe) \ + PyArray_Descr* from_descr_##From##_##To = (from_descr); \ + if (PyArray_RegisterCastFunc(from_descr_##From##_##To, (to_typenum), \ + npycast_##From##_##To) < 0) { \ + goto fail; \ + } \ + if (safe && PyArray_RegisterCanCast(from_descr_##From##_##To, \ + (to_typenum), \ + NPY_NOSCALAR) < 0) { \ + goto fail; \ + } + #define REGISTER_INT_CASTS(bits) \ + REGISTER_CAST(int##bits##_t, rational, \ + PyArray_DescrFromType(NPY_INT##bits), npy_rational, 1) \ + REGISTER_CAST(rational, int##bits##_t, &npyrational_descr, \ + NPY_INT##bits, 0) + REGISTER_INT_CASTS(8) + REGISTER_INT_CASTS(16) + REGISTER_INT_CASTS(32) + REGISTER_INT_CASTS(64) + REGISTER_CAST(rational,float,&npyrational_descr,NPY_FLOAT,0) + REGISTER_CAST(rational,double,&npyrational_descr,NPY_DOUBLE,1) + REGISTER_CAST(npy_bool,rational, PyArray_DescrFromType(NPY_BOOL), + npy_rational,1) + REGISTER_CAST(rational,npy_bool,&npyrational_descr,NPY_BOOL,0) + + /* Register ufuncs */ + #define REGISTER_UFUNC(name,...) { \ + PyUFuncObject* ufunc = \ + (PyUFuncObject*)PyObject_GetAttrString(numpy, #name); \ + if (!ufunc) { \ + goto fail; \ + } \ + int _types[] = __VA_ARGS__; \ + if (sizeof(_types)/sizeof(int)!=ufunc->nargs) { \ + PyErr_Format(PyExc_AssertionError, \ + "ufunc %s takes %d arguments, our loop takes %ld", \ + #name, ufunc->nargs, sizeof(_types)/sizeof(int)); \ + goto fail; \ + } \ + if (PyUFunc_RegisterLoopForType((PyUFuncObject*)ufunc, npy_rational, \ + rational_ufunc_##name, _types, 0) < 0) { \ + goto fail; \ + } \ + } + #define REGISTER_UFUNC_BINARY_RATIONAL(name) \ + REGISTER_UFUNC(name, {npy_rational, npy_rational, npy_rational}) + #define REGISTER_UFUNC_BINARY_COMPARE(name) \ + REGISTER_UFUNC(name, {npy_rational, npy_rational, NPY_BOOL}) + #define REGISTER_UFUNC_UNARY(name) \ + REGISTER_UFUNC(name, {npy_rational, npy_rational}) + /* Binary */ + REGISTER_UFUNC_BINARY_RATIONAL(add) + REGISTER_UFUNC_BINARY_RATIONAL(subtract) + REGISTER_UFUNC_BINARY_RATIONAL(multiply) + REGISTER_UFUNC_BINARY_RATIONAL(divide) + REGISTER_UFUNC_BINARY_RATIONAL(remainder) + REGISTER_UFUNC_BINARY_RATIONAL(true_divide) + REGISTER_UFUNC_BINARY_RATIONAL(floor_divide) + REGISTER_UFUNC_BINARY_RATIONAL(minimum) + REGISTER_UFUNC_BINARY_RATIONAL(maximum) + /* Comparisons */ + REGISTER_UFUNC_BINARY_COMPARE(equal) + REGISTER_UFUNC_BINARY_COMPARE(not_equal) + REGISTER_UFUNC_BINARY_COMPARE(less) + REGISTER_UFUNC_BINARY_COMPARE(greater) + REGISTER_UFUNC_BINARY_COMPARE(less_equal) + REGISTER_UFUNC_BINARY_COMPARE(greater_equal) + /* Unary */ + REGISTER_UFUNC_UNARY(negative) + REGISTER_UFUNC_UNARY(absolute) + REGISTER_UFUNC_UNARY(floor) + REGISTER_UFUNC_UNARY(ceil) + REGISTER_UFUNC_UNARY(trunc) + REGISTER_UFUNC_UNARY(rint) + REGISTER_UFUNC_UNARY(square) + REGISTER_UFUNC_UNARY(reciprocal) + REGISTER_UFUNC_UNARY(sign) + + /* Create module */ +#if defined(NPY_PY3K) + m = PyModule_Create(&moduledef); +#else + m = Py_InitModule("test_rational", module_methods); +#endif + + if (!m) { + goto fail; + } + + /* Add rational type */ + Py_INCREF(&PyRational_Type); + PyModule_AddObject(m,"rational",(PyObject*)&PyRational_Type); + + /* Create matrix multiply generalized ufunc */ + PyObject* gufunc = PyUFunc_FromFuncAndDataAndSignature(0,0,0,0,2,1, + PyUFunc_None,(char*)"matrix_multiply", + (char*)"return result of multiplying two matrices of rationals", + 0,"(m,n),(n,p)->(m,p)"); + if (!gufunc) { + goto fail; + } + int types2[3] = {npy_rational,npy_rational,npy_rational}; + if (PyUFunc_RegisterLoopForType((PyUFuncObject*)gufunc, npy_rational, + rational_gufunc_matrix_multiply, types2, 0) < 0) { + goto fail; + } + PyModule_AddObject(m,"matrix_multiply",(PyObject*)gufunc); + + /* Create test ufunc with built in input types and rational output type */ + PyObject* ufunc = PyUFunc_FromFuncAndData(0,0,0,0,2,1, + PyUFunc_None,(char*)"test_add", + (char*)"add two matrices of int64 and return rational matrix",0); + if (!ufunc) { + goto fail; + } + int types3[3] = {NPY_INT64,NPY_INT64,npy_rational}; + if (PyUFunc_RegisterLoopForType((PyUFuncObject*)ufunc, npy_rational, + rational_ufunc_test_add, types3, 0) < 0) { + goto fail; + } + PyModule_AddObject(m,"test_add",(PyObject*)ufunc); + + /* Create numerator and denominator ufuncs */ + #define NEW_UNARY_UFUNC(name,type,doc) { \ + PyObject* ufunc = PyUFunc_FromFuncAndData(0,0,0,0,1,1, \ + PyUFunc_None,(char*)#name,(char*)doc,0); \ + if (!ufunc) { \ + goto fail; \ + } \ + int types[2] = {npy_rational,type}; \ + if (PyUFunc_RegisterLoopForType((PyUFuncObject*)ufunc, \ + npy_rational,rational_ufunc_##name,types,0)<0) { \ + goto fail; \ + } \ + PyModule_AddObject(m,#name,(PyObject*)ufunc); \ + } + NEW_UNARY_UFUNC(numerator,NPY_INT64,"rational number numerator"); + NEW_UNARY_UFUNC(denominator,NPY_INT64,"rational number denominator"); + + /* Create gcd and lcm ufuncs */ + #define GCD_LCM_UFUNC(name,type,doc) { \ + static const PyUFuncGenericFunction func[1] = {name##_ufunc}; \ + static const char types[3] = {type,type,type}; \ + static void* data[1] = {0}; \ + PyObject* ufunc = PyUFunc_FromFuncAndData( \ + (PyUFuncGenericFunction*)func, data,(char*)types, \ + 1,2,1,PyUFunc_One,(char*)#name,(char*)doc,0); \ + if (!ufunc) { \ + goto fail; \ + } \ + PyModule_AddObject(m,#name,(PyObject*)ufunc); \ + } + GCD_LCM_UFUNC(gcd,NPY_INT64,"greatest common denominator of two integers"); + GCD_LCM_UFUNC(lcm,NPY_INT64,"least common multiple of two integers"); + + return RETVAL; + +fail: + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_RuntimeError, + "cannot load test_rational module."); + } +#if defined(NPY_PY3K) + if (m) { + Py_DECREF(m); + m = NULL; + } +#endif + return RETVAL; +} diff --git a/numpy/core/src/umath/ufunc_type_resolution.c b/numpy/core/src/umath/ufunc_type_resolution.c index 5b090e88e..8344d73e5 100644 --- a/numpy/core/src/umath/ufunc_type_resolution.c +++ b/numpy/core/src/umath/ufunc_type_resolution.c @@ -1179,7 +1179,13 @@ find_userloop(PyUFuncObject *ufunc, /* Use this to try to avoid repeating the same userdef loop search */ int last_userdef = -1; - for (i = 0; i < nin; ++i) { + for (i = 0; i < nargs; ++i) { + + /* no more ufunc arguments to check */ + if (dtypes[i] == NULL) { + break; + } + int type_num = dtypes[i]->type_num; if (type_num != last_userdef && PyTypeNum_ISUSERDEF(type_num)) { PyObject *key, *obj; @@ -1581,13 +1587,19 @@ linear_search_userloop_type_resolver(PyUFuncObject *self, char *out_err_src_typecode, char *out_err_dst_typecode) { - npy_intp i, nin = self->nin; + npy_intp i, nop = self->nin + self->nout; PyUFunc_Loop1d *funcdata; /* Use this to try to avoid repeating the same userdef loop search */ int last_userdef = -1; - for (i = 0; i < nin; ++i) { + for (i = 0; i < nop; ++i) { + + /* no more ufunc arguments to check */ + if (op[i] == NULL) { + break; + } + int type_num = PyArray_DESCR(op[i])->type_num; if (type_num != last_userdef && PyTypeNum_ISUSERDEF(type_num)) { PyObject *key, *obj; diff --git a/numpy/core/tests/test_api.py b/numpy/core/tests/test_api.py index 8ab48f2d1..376097f7b 100644 --- a/numpy/core/tests/test_api.py +++ b/numpy/core/tests/test_api.py @@ -88,7 +88,7 @@ def test_array_astype(): b = a.astype('f4', subok=False, copy=False) assert_equal(a, b) assert_(not (a is b)) - assert_(type(b) != np.matrix) + assert_(type(b) is not np.matrix) # Make sure converting from string object to fixed length string # does not truncate. diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py index 9947a4660..3a6118f06 100644 --- a/numpy/core/tests/test_numeric.py +++ b/numpy/core/tests/test_numeric.py @@ -1270,14 +1270,14 @@ class TestIsclose(object): def test_masked_arrays(self): x = np.ma.masked_where([True, True, False], np.arange(3)) - assert_(type(x) == type(isclose(2, x))) + assert_(type(x) is type(isclose(2, x))) x = np.ma.masked_where([True, True, False], [nan, inf, nan]) - assert_(type(x) == type(isclose(inf, x))) + assert_(type(x) is type(isclose(inf, x))) x = np.ma.masked_where([True, True, False], [nan, nan, nan]) y = isclose(nan, x, equal_nan=True) - assert_(type(x) == type(y)) + assert_(type(x) is type(y)) # Ensure that the mask isn't modified... assert_array_equal([True, True, False], y.mask) @@ -1409,7 +1409,7 @@ class TestLikeFuncs(TestCase): assert_(type(b) is np.matrix) b = like_function(a, subok=False) - assert_(not (type(b) is np.matrix)) + assert_(type(b) is not np.matrix) def test_ones_like(self): self.check_like_function(np.ones_like, 1) diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index 4ae1f04a6..dbbd15397 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -6,6 +6,7 @@ import numpy as np from numpy.testing import * import numpy.core.umath_tests as umt from numpy.compat import asbytes +from numpy.core.test_rational import * class TestUfunc(TestCase): def test_pickle(self): @@ -771,5 +772,20 @@ class TestUfunc(TestCase): assert_no_warnings(np.add, a, 1.1, out=a, casting="unsafe") assert_array_equal(a, [4, 5, 6]) + def test_ufunc_custom_out(self): + # Test ufunc with built in input types and custom output type + + a = np.array([0, 1, 2], dtype='i8') + b = np.array([0, 1, 2], dtype='i8') + c = np.empty(3, dtype=rational) + + # Output must be specified so numpy knows what + # ufunc signature to look for + result = test_add(a, b, c) + assert_equal(result, np.array([0, 2, 4], dtype=rational)) + + # no output type should raise TypeError + assert_raises(TypeError, test_add, a, b) + if __name__ == "__main__": run_module_suite() diff --git a/numpy/ctypeslib.py b/numpy/ctypeslib.py index 4e00b1cbc..0dc9cd1a5 100644 --- a/numpy/ctypeslib.py +++ b/numpy/ctypeslib.py @@ -355,7 +355,7 @@ if ctypes is not None: shape = [] ob = array_type - while type(ob) == _ARRAY_TYPE: + while type(ob) is _ARRAY_TYPE: shape.append(ob._length_) ob = ob._type_ shape = tuple(shape) diff --git a/numpy/distutils/ccompiler.py b/numpy/distutils/ccompiler.py index af59d687e..51a349aea 100644 --- a/numpy/distutils/ccompiler.py +++ b/numpy/distutils/ccompiler.py @@ -403,7 +403,7 @@ def simple_version_match(pat=r'[-.\d]+', ignore='', start=''): if not m: return None pos = m.end() - while 1: + while True: m = re.search(pat, version_string[pos:]) if not m: return None diff --git a/numpy/distutils/command/build_ext.py b/numpy/distutils/command/build_ext.py index 48e578b66..97bfa1613 100644 --- a/numpy/distutils/command/build_ext.py +++ b/numpy/distutils/command/build_ext.py @@ -297,8 +297,8 @@ class build_ext (old_build_ext): else: # in case ext.language is c++, for instance fcompiler = self._f90_compiler or self._f77_compiler if fcompiler is not None: - fcompiler.extra_f77_compile_args = ext.extra_f77_compile_args or [] - fcompiler.extra_f90_compile_args = ext.extra_f90_compile_args or [] + fcompiler.extra_f77_compile_args = (ext.extra_f77_compile_args or []) if hasattr(ext,'extra_f77_compile_args') else [] + fcompiler.extra_f90_compile_args = (ext.extra_f90_compile_args or []) if hasattr(ext,'extra_f90_compile_args') else [] cxx_compiler = self._cxx_compiler # check for the availability of required compilers diff --git a/numpy/distutils/conv_template.py b/numpy/distutils/conv_template.py index 173853b6c..cb03fc7c3 100644 --- a/numpy/distutils/conv_template.py +++ b/numpy/distutils/conv_template.py @@ -120,7 +120,7 @@ def parse_structure(astr, level): ind = 0 line = 0 spanlist = [] - while 1: + while True: start = astr.find(loopbeg, ind) if start == -1: break diff --git a/numpy/distutils/cpuinfo.py b/numpy/distutils/cpuinfo.py index 775929fa1..64ad055d3 100644 --- a/numpy/distutils/cpuinfo.py +++ b/numpy/distutils/cpuinfo.py @@ -82,7 +82,7 @@ class CPUInfoBase(object): if not name.startswith('_'): if hasattr(self,'_'+name): attr = getattr(self,'_'+name) - if type(attr) is types.MethodType: + if isinstance(attr, types.MethodType): return lambda func=self._try_call,attr=attr : func(attr) else: return lambda : None @@ -499,7 +499,7 @@ class Win32CPUInfo(CPUInfoBase): "\s+stepping\s+(?P<STP>\d+)",re.IGNORECASE) chnd=winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, self.pkey) pnum=0 - while 1: + while True: try: proc=winreg.EnumKey(chnd,pnum) except winreg.error: diff --git a/numpy/distutils/fcompiler/ibm.py b/numpy/distutils/fcompiler/ibm.py index 5811be876..e5061bd18 100644 --- a/numpy/distutils/fcompiler/ibm.py +++ b/numpy/distutils/fcompiler/ibm.py @@ -45,8 +45,7 @@ class IBMFCompiler(FCompiler): # If the output of xlf does not contain version info # (that's the case with xlf 8.1, for instance) then # let's try another method: - l = os.listdir(xlf_dir) - l.sort() + l = sorted(os.listdir(xlf_dir)) l.reverse() l = [d for d in l if os.path.isfile(os.path.join(xlf_dir,d,'xlf.cfg'))] if l: diff --git a/numpy/distutils/from_template.py b/numpy/distutils/from_template.py index 5d6bea3ca..9052cf74e 100644 --- a/numpy/distutils/from_template.py +++ b/numpy/distutils/from_template.py @@ -65,13 +65,13 @@ def parse_structure(astr): spanlist = [] ind = 0 - while 1: + while True: m = routine_start_re.search(astr,ind) if m is None: break start = m.start() if function_start_re.match(astr,start,m.end()): - while 1: + while True: i = astr.rfind('\n',ind,start) if i==-1: break diff --git a/numpy/doc/__init__.py b/numpy/doc/__init__.py index 86d45f618..b6f1fa71c 100644 --- a/numpy/doc/__init__.py +++ b/numpy/doc/__init__.py @@ -4,9 +4,8 @@ import os ref_dir = os.path.join(os.path.dirname(__file__)) -__all__ = [f[:-3] for f in os.listdir(ref_dir) if f.endswith('.py') and - not f.startswith('__')] -__all__.sort() +__all__ = sorted(f[:-3] for f in os.listdir(ref_dir) if f.endswith('.py') and + not f.startswith('__')) for f in __all__: __import__(__name__ + '.' + f) diff --git a/numpy/f2py/auxfuncs.py b/numpy/f2py/auxfuncs.py index e391430f5..e835090f7 100644 --- a/numpy/f2py/auxfuncs.py +++ b/numpy/f2py/auxfuncs.py @@ -491,9 +491,9 @@ def getmultilineblock(rout,blockname,comment=1,counter=0): except KeyError: return if not r: return - if counter>0 and type(r) is type(''): + if counter > 0 and isinstance(r, str): return - if type(r) is type([]): + if isinstance(r, list): if counter>=len(r): return r = r[counter] if r[:3]=="'''": @@ -598,7 +598,7 @@ def gentitle(name): return '/*%s %s %s*/'%(l*'*',name,l*'*') def flatlist(l): - if type(l)==list: + if isinstance(l, list): return reduce(lambda x,y,f=flatlist:x+f(y),l,[]) return [l] @@ -607,9 +607,9 @@ def stripcomma(s): return s def replace(str,d,defaultsep=''): - if type(d)==list: + if isinstance(d, list): return [replace(str, _m, defaultsep) for _m in d] - if type(str)==list: + if isinstance(str, list): return [replace(_m, d, defaultsep) for _m in str] for k in 2*list(d.keys()): if k=='separatorsfor': @@ -618,14 +618,14 @@ def replace(str,d,defaultsep=''): sep=d['separatorsfor'][k] else: sep=defaultsep - if type(d[k])==list: + if isinstance(d[k], list): str=str.replace('#%s#'%(k),sep.join(flatlist(d[k]))) else: str=str.replace('#%s#'%(k),d[k]) return str def dictappend(rd,ar): - if type(ar)==list: + if isinstance(ar, list): for a in ar: rd=dictappend(rd,a) return rd @@ -633,15 +633,15 @@ def dictappend(rd,ar): if k[0]=='_': continue if k in rd: - if type(rd[k])==str: + if isinstance(rd[k], str): rd[k]=[rd[k]] - if type(rd[k])==list: - if type(ar[k])==list: + if isinstance(rd[k], list): + if isinstance(ar[k], list): rd[k]=rd[k]+ar[k] else: rd[k].append(ar[k]) - elif type(rd[k])==dict: - if type(ar[k])==dict: + elif isinstance(rd[k], dict): + if isinstance(ar[k], dict): if k=='separatorsfor': for k1 in ar[k].keys(): if k1 not in rd[k]: @@ -654,7 +654,7 @@ def dictappend(rd,ar): def applyrules(rules,d,var={}): ret={} - if type(rules)==list: + if isinstance(rules, list): for r in rules: rr=applyrules(r,d,var) ret=dictappend(ret,rr) @@ -671,9 +671,9 @@ def applyrules(rules,d,var={}): for k in rules.keys(): if k=='separatorsfor': ret[k]=rules[k]; continue - if type(rules[k])==str: + if isinstance(rules[k], str): ret[k]=replace(rules[k],d) - elif type(rules[k])==list: + elif isinstance(rules[k], list): ret[k]=[] for i in rules[k]: ar=applyrules({k:i},d,var) @@ -681,13 +681,13 @@ def applyrules(rules,d,var={}): ret[k].append(ar[k]) elif k[0]=='_': continue - elif type(rules[k])==dict: + elif isinstance(rules[k], dict): ret[k]=[] for k1 in rules[k].keys(): - if type(k1)==types.FunctionType and k1(var): - if type(rules[k][k1])==list: + if isinstance(k1, types.FunctionType) and k1(var): + if isinstance(rules[k][k1], list): for i in rules[k][k1]: - if type(i)==dict: + if isinstance(i, dict): res=applyrules({'supertext':i},d,var) if 'supertext' in res: i=res['supertext'] @@ -695,7 +695,7 @@ def applyrules(rules,d,var={}): ret[k].append(replace(i,d)) else: i=rules[k][k1] - if type(i)==dict: + if isinstance(i, dict): res=applyrules({'supertext':i},d) if 'supertext' in res: i=res['supertext'] @@ -703,7 +703,7 @@ def applyrules(rules,d,var={}): ret[k].append(replace(i,d)) else: errmess('applyrules: ignoring rule %s.\n'%repr(rules[k])) - if type(ret[k])==list: + if isinstance(ret[k], list): if len(ret[k])==1: ret[k]=ret[k][0] if ret[k]==[]: diff --git a/numpy/f2py/cb_rules.py b/numpy/f2py/cb_rules.py index 85e679060..f80ab06f0 100644 --- a/numpy/f2py/cb_rules.py +++ b/numpy/f2py/cb_rules.py @@ -466,7 +466,7 @@ def buildcallback(rout,um): if '_break' in r: break if 'args' in rd and 'optargs' in rd: - if type(rd['optargs'])==type([]): + if isinstance(rd['optargs'], list): rd['optargs']=rd['optargs']+[""" #ifndef F2PY_CB_RETURNCOMPLEX , @@ -482,7 +482,7 @@ def buildcallback(rout,um): , #endif """] - if type(rd['docreturn'])==list: + if isinstance(rd['docreturn'], list): rd['docreturn']=stripcomma(replace('#docreturn#',{'docreturn':rd['docreturn']})) optargs=stripcomma(replace('#docsignopt#', {'docsignopt':rd['docsignopt']} @@ -499,10 +499,10 @@ def buildcallback(rout,um): rd['docstrsigns']=[] rd['latexdocstrsigns']=[] for k in ['docstrreq','docstropt','docstrout','docstrcbs']: - if k in rd and type(rd[k])==list: + if k in rd and isinstance(rd[k], list): rd['docstrsigns']=rd['docstrsigns']+rd[k] k='latex'+k - if k in rd and type(rd[k])==list: + if k in rd and isinstance(rd[k], list): rd['latexdocstrsigns']=rd['latexdocstrsigns']+rd[k][0:1]+\ ['\\begin{description}']+rd[k][1:]+\ ['\\end{description}'] @@ -515,7 +515,7 @@ def buildcallback(rout,um): ar=applyrules(cb_routine_rules,rd) cfuncs.callbacks[rd['name']]=ar['body'] - if type(ar['need'])==str: + if isinstance(ar['need'], str): ar['need']=[ar['need']] if 'need' in rd: diff --git a/numpy/f2py/cfuncs.py b/numpy/f2py/cfuncs.py index a16a07d59..2229c3e24 100644 --- a/numpy/f2py/cfuncs.py +++ b/numpy/f2py/cfuncs.py @@ -1128,10 +1128,10 @@ def buildcfuncs(): def append_needs(need,flag=1): global outneeds,needs - if type(need)==list: + if isinstance(need, list): for n in need: append_needs(n,flag) - elif type(need)==str: + elif isinstance(need, str): if not need: return if need in includes0: n = 'includes0' @@ -1160,7 +1160,7 @@ def append_needs(need,flag=1): if need in needs: for nn in needs[need]: t=append_needs(nn,0) - if type(t)==dict: + if isinstance(t, dict): for nnn in t.keys(): if nnn in tmp: tmp[nnn]=tmp[nnn]+t[nnn] @@ -1176,7 +1176,7 @@ def append_needs(need,flag=1): if need in needs: for nn in needs[need]: t=append_needs(nn,flag) - if type(t)==dict: + if isinstance(t, dict): for nnn in t.keys(): if nnn in tmp: tmp[nnn]=t[nnn]+tmp[nnn] diff --git a/numpy/f2py/common_rules.py b/numpy/f2py/common_rules.py index bfeaf4c9b..dd44e3326 100644 --- a/numpy/f2py/common_rules.py +++ b/numpy/f2py/common_rules.py @@ -121,7 +121,7 @@ def buildhooks(m): dadd('\\item[]{{}\\verb@%s@{}}'%(capi_maps.getarrdocsign(n,vars[n]))) if hasnote(vars[n]): note = vars[n]['note'] - if type(note) is type([]): note='\n'.join(note) + if isinstance(note, list): note='\n'.join(note) dadd('--- %s'%(note)) dadd('\\end{description}') ret['docs'].append('"\t/%s/ %s\\n"'%(name,','.join(map(lambda v,d:v+d,inames,idims)))) diff --git a/numpy/f2py/crackfortran.py b/numpy/f2py/crackfortran.py index c86a15407..2ee8eb9a1 100755 --- a/numpy/f2py/crackfortran.py +++ b/numpy/f2py/crackfortran.py @@ -297,7 +297,7 @@ def readfortrancode(ffile,dowithline=show,istop=1): spacedigits=[' '] + [str(_m) for _m in range(10)] filepositiontext='' fin=fileinput.FileInput(ffile) - while 1: + while True: l=fin.readline() if not l: break if fin.isfirstline(): @@ -381,7 +381,7 @@ def readfortrancode(ffile,dowithline=show,istop=1): elif sourcecodeform=='free': if not cont and ext=='.pyf' and mline_mark.match(l): l = l + '\n' - while 1: + while True: lc = fin.readline() if not lc: errmess('Unexpected end of file when reading multiline\n') @@ -1167,7 +1167,7 @@ def analyzeline(m,case,line): groupcache[groupcounter]['f2pyenhancements'] = {} d = groupcache[groupcounter]['f2pyenhancements'] if m.group('this')=='usercode' and 'usercode' in d: - if type(d['usercode']) is type(''): + if isinstance(d['usercode'], str): d['usercode'] = [d['usercode']] d['usercode'].append(m.group('after')) else: @@ -1522,7 +1522,7 @@ def postcrack2(block,tab='',param_map=None): global f90modulevars if not f90modulevars: return block - if type(block)==list: + if isinstance(block, list): ret = [] for g in block: g = postcrack2(g,tab=tab+'\t',param_map=param_map) @@ -1559,7 +1559,7 @@ def postcrack(block,args=None,tab=''): determine expression types if in argument list """ global usermodules,onlyfunctions - if type(block)==list: + if isinstance(block, list): gret=[] uret=[] for g in block: @@ -1571,7 +1571,7 @@ def postcrack(block,args=None,tab=''): gret.append(g) return uret+gret setmesstext(block) - if (not type(block)==dict) and 'block' not in block: + if not isinstance(block, dict) and 'block' not in block: raise Exception('postcrack: Expected block dictionary instead of ' + \ str(block)) if 'name' in block and not block['name']=='unknown_interface': @@ -1819,12 +1819,12 @@ def getarrlen(dl,args,star='*'): except: edl.append(dl[0]) try: edl.append(myeval(dl[1],{},{})) except: edl.append(dl[1]) - if type(edl[0]) is type(0): + if isinstance(edl[0], int): p1 = 1-edl[0] if p1==0: d = str(dl[1]) elif p1<0: d = '%s-%s'%(dl[1],-p1) else: d = '%s+%s'%(dl[1],p1) - elif type(edl[1]) is type(0): + elif isinstance(edl[1], int): p1 = 1+edl[1] if p1==0: d='-(%s)' % (dl[0]) else: d='%s-(%s)' % (p1,dl[0]) @@ -2042,7 +2042,7 @@ def get_parameters(vars, global_params={}): params[n] = v #print params outmess('get_parameters: got "%s" on %s\n' % (msg,repr(v))) - if isstring(vars[n]) and type(params[n]) is type(0): + if isstring(vars[n]) and isinstance(params[n], int): params[n] = chr(params[n]) nl = n.lower() if nl!=n: @@ -2458,14 +2458,15 @@ determineexprtype_re_3 = re.compile(r'\A[+-]?[\d.]+[\d+-de.]*(_(P<name>[\w]+)|)\ determineexprtype_re_4 = re.compile(r'\A\(.*\)\Z',re.I) determineexprtype_re_5 = re.compile(r'\A(?P<name>\w+)\s*\(.*?\)\s*\Z',re.I) def _ensure_exprdict(r): - if type(r) is type(0): + if isinstance(r, int): return {'typespec':'integer'} - if type(r) is type(0.0): + if isinstance(r, float): return {'typespec':'real'} - if type(r) is type(0j): + if isinstance(r, complex): return {'typespec':'complex'} - assert type(r) is type({}),repr(r) - return r + if isinstance(r, dict): + return r + raise AssertionError(repr(r)) def determineexprtype(expr,vars,rules={}): if expr in vars: diff --git a/numpy/f2py/f2py2e.py b/numpy/f2py/f2py2e.py index 98f58e333..64c13fff0 100755 --- a/numpy/f2py/f2py2e.py +++ b/numpy/f2py/f2py2e.py @@ -339,7 +339,7 @@ def dict_append(d_out,d_in): for (k,v) in d_in.items(): if k not in d_out: d_out[k] = [] - if type(v) is list: + if isinstance(v, list): d_out[k] = d_out[k] + v else: d_out[k].append(v) diff --git a/numpy/f2py/f90mod_rules.py b/numpy/f2py/f90mod_rules.py index b68a79b64..7e25a4930 100644 --- a/numpy/f2py/f90mod_rules.py +++ b/numpy/f2py/f90mod_rules.py @@ -119,7 +119,7 @@ def buildhooks(pymod): dadd('\\subsection{Fortran 90/95 module \\texttt{%s}}\n'%(m['name'])) if hasnote(m): note = m['note'] - if type(note) is type([]): note='\n'.join(note) + if isinstance(note, list): note='\n'.join(note) dadd(note) if onlyvars: dadd('\\begin{description}') @@ -145,7 +145,7 @@ def buildhooks(pymod): dadd('\\item[]{{}\\verb@%s@{}}'%(capi_maps.getarrdocsign(n,var))) if hasnote(var): note = var['note'] - if type(note) is type([]): note='\n'.join(note) + if isinstance(note, list): note='\n'.join(note) dadd('--- %s'%(note)) if isallocatable(var): fargs.append('f2py_%s_getdims_%s'%(m['name'],n)) diff --git a/numpy/f2py/rules.py b/numpy/f2py/rules.py index a76401ac9..f7f82fc99 100644 --- a/numpy/f2py/rules.py +++ b/numpy/f2py/rules.py @@ -1388,9 +1388,9 @@ def buildapi(rout): vrd['check']=c ar=applyrules(check_rules,vrd,var[a]) rd=dictappend(rd,ar) - if type(rd['cleanupfrompyobj']) is list: + if isinstance(rd['cleanupfrompyobj'], list): rd['cleanupfrompyobj'].reverse() - if type(rd['closepyobjfrom']) is list: + if isinstance(rd['closepyobjfrom'], list): rd['closepyobjfrom'].reverse() rd['docsignature']=stripcomma(replace('#docsign##docsignopt##docsignxa#', {'docsign':rd['docsign'], @@ -1415,15 +1415,15 @@ def buildapi(rout): else: rd['callcompaqfortran']=cfs rd['callfortran']=cfs - if type(rd['docreturn'])==list: + if isinstance(rd['docreturn'], list): rd['docreturn']=stripcomma(replace('#docreturn#',{'docreturn':rd['docreturn']}))+' = ' rd['docstrsigns']=[] rd['latexdocstrsigns']=[] for k in ['docstrreq','docstropt','docstrout','docstrcbs']: - if k in rd and type(rd[k])==list: + if k in rd and isinstance(rd[k], list): rd['docstrsigns']=rd['docstrsigns']+rd[k] k='latex'+k - if k in rd and type(rd[k])==list: + if k in rd and isinstance(rd[k], list): rd['latexdocstrsigns']=rd['latexdocstrsigns']+rd[k][0:1]+\ ['\\begin{description}']+rd[k][1:]+\ ['\\end{description}'] diff --git a/numpy/lib/arrayterator.py b/numpy/lib/arrayterator.py index 094d41c11..c2cde574e 100644 --- a/numpy/lib/arrayterator.py +++ b/numpy/lib/arrayterator.py @@ -188,7 +188,7 @@ class Arrayterator(object): step = self.step[:] ndims = len(self.var.shape) - while 1: + while True: count = self.buf_size or reduce(mul, self.shape) # iterate over each dimension, looking for the diff --git a/numpy/lib/format.py b/numpy/lib/format.py index 40788e148..81e8cd010 100644 --- a/numpy/lib/format.py +++ b/numpy/lib/format.py @@ -344,8 +344,7 @@ def read_array_header_1_0(fp): if not isinstance(d, dict): msg = "Header is not a dictionary: %r" raise ValueError(msg % d) - keys = list(d.keys()) - keys.sort() + keys = sorted(d.keys()) if keys != ['descr', 'fortran_order', 'shape']: msg = "Header does not contain the correct keys: %r" raise ValueError(msg % (keys,)) diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py index 1ead53c87..a7163a7ca 100644 --- a/numpy/lib/function_base.py +++ b/numpy/lib/function_base.py @@ -1273,8 +1273,7 @@ def unique(x): idx = concatenate(([True],tmp[1:]!=tmp[:-1])) return tmp[idx] except AttributeError: - items = list(set(x)) - items.sort() + items = sorted(set(x)) return asarray(items) def extract(condition, arr): diff --git a/numpy/lib/index_tricks.py b/numpy/lib/index_tricks.py index 314cba120..b3c9b72bc 100644 --- a/numpy/lib/index_tricks.py +++ b/numpy/lib/index_tricks.py @@ -244,7 +244,7 @@ class AxisConcatenator(object): frame = sys._getframe().f_back mymat = matrix.bmat(key,frame.f_globals,frame.f_locals) return mymat - if type(key) is not tuple: + if not isinstance(key, tuple): key = (key,) objs = [] scalars = [] @@ -252,7 +252,7 @@ class AxisConcatenator(object): scalartypes = [] for k in range(len(key)): scalar = False - if type(key[k]) is slice: + if isinstance(key[k], slice): step = key[k].step start = key[k].start stop = key[k].stop @@ -627,7 +627,7 @@ class IndexExpression(object): self.maketuple = maketuple def __getitem__(self, item): - if self.maketuple and type(item) != tuple: + if self.maketuple and not isinstance(item, tuple): return (item,) else: return item diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index c13c7e94a..fbcb5a46e 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -1025,7 +1025,7 @@ def savetxt(fname, X, fmt='%.18e', delimiter=' ', newline='\n', header='', if len(fmt) != ncol: raise AttributeError('fmt has wrong shape. %s' % str(fmt)) format = asstr(delimiter).join(map(asstr, fmt)) - elif type(fmt) is str: + elif isinstance(fmt, str): n_fmt_chars = fmt.count('%') error = ValueError('fmt has wrong number of %% formats: %s' % fmt) if n_fmt_chars == 1: diff --git a/numpy/lib/polynomial.py b/numpy/lib/polynomial.py index e61a89b87..5402adc6d 100644 --- a/numpy/lib/polynomial.py +++ b/numpy/lib/polynomial.py @@ -905,7 +905,7 @@ def _raise_power(astr, wrap=70): line1 = '' line2 = '' output = ' ' - while 1: + while True: mat = _poly_mat.search(astr, n) if mat is None: break diff --git a/numpy/lib/shape_base.py b/numpy/lib/shape_base.py index de8606167..e81bae5fc 100644 --- a/numpy/lib/shape_base.py +++ b/numpy/lib/shape_base.py @@ -55,14 +55,11 @@ def apply_along_axis(func1d,axis,arr,*args): For a function that doesn't return a scalar, the number of dimensions in `outarr` is the same as `arr`. - >>> def new_func(a): - ... \"\"\"Divide elements of a by 2.\"\"\" - ... return a * 0.5 - >>> b = np.array([[1,2,3], [4,5,6], [7,8,9]]) - >>> np.apply_along_axis(new_func, 0, b) - array([[ 0.5, 1. , 1.5], - [ 2. , 2.5, 3. ], - [ 3.5, 4. , 4.5]]) + >>> b = np.array([[8,1,7], [4,3,9], [5,2,6]]) + >>> np.apply_along_axis(sorted, 1, b) + array([[1, 7, 8], + [3, 4, 9], + [2, 5, 6]]) """ arr = asarray(arr) @@ -643,10 +640,9 @@ def get_array_prepare(*args): In case of ties, leftmost wins. If no wrapper is found, return None """ - wrappers = [(getattr(x, '__array_priority__', 0), -i, + wrappers = sorted((getattr(x, '__array_priority__', 0), -i, x.__array_prepare__) for i, x in enumerate(args) - if hasattr(x, '__array_prepare__')] - wrappers.sort() + if hasattr(x, '__array_prepare__')) if wrappers: return wrappers[-1][-1] return None @@ -656,10 +652,9 @@ def get_array_wrap(*args): In case of ties, leftmost wins. If no wrapper is found, return None """ - wrappers = [(getattr(x, '__array_priority__', 0), -i, + wrappers = sorted((getattr(x, '__array_priority__', 0), -i, x.__array_wrap__) for i, x in enumerate(args) - if hasattr(x, '__array_wrap__')] - wrappers.sort() + if hasattr(x, '__array_wrap__')) if wrappers: return wrappers[-1][-1] return None diff --git a/numpy/lib/type_check.py b/numpy/lib/type_check.py index 6e0cfcddb..cb9b6ee49 100644 --- a/numpy/lib/type_check.py +++ b/numpy/lib/type_check.py @@ -58,7 +58,7 @@ def mintypecode(typechars,typeset='GDFgdf',default='d'): 'G' """ - typecodes = [(type(t) is type('') and t) or asarray(t).dtype.char\ + typecodes = [(isinstance(t, str) and t) or asarray(t).dtype.char\ for t in typechars] intersection = [t for t in typecodes if t in typeset] if not intersection: diff --git a/numpy/lib/utils.py b/numpy/lib/utils.py index 2519cd4d4..f94abeeab 100644 --- a/numpy/lib/utils.py +++ b/numpy/lib/utils.py @@ -435,7 +435,7 @@ def _makenamedict(module='numpy'): thedict = {module.__name__:module.__dict__} dictlist = [module.__name__] totraverse = [module.__dict__] - while 1: + while True: if len(totraverse) == 0: break thisdict = totraverse.pop(0) @@ -584,7 +584,7 @@ def info(object=None,maxwidth=76,output=sys.stdout,toplevel='numpy'): methstr, other = pydoc.splitdoc(inspect.getdoc(thisobj) or "None") print(" %s -- %s" % (meth, methstr), file=output) - elif type(object) is types.InstanceType: ## check for __call__ method + elif isinstance(object, types.InstanceType): ## check for __call__ method print("Instance of class: ", object.__class__.__name__, file=output) print(file=output) if hasattr(object, '__call__'): diff --git a/numpy/linalg/bento.info b/numpy/linalg/bento.info index 1c2c180da..52d036753 100644 --- a/numpy/linalg/bento.info +++ b/numpy/linalg/bento.info @@ -1,7 +1,7 @@ HookFile: bscript Library: - Extension: umath_linalg + Extension: _umath_linalg Sources: umath_linalg.c.src, lapack_lite/blas_lite.c, diff --git a/numpy/linalg/bscript b/numpy/linalg/bscript index dc275f085..deed4fd72 100644 --- a/numpy/linalg/bscript +++ b/numpy/linalg/bscript @@ -1,5 +1,3 @@ -import os - from bento.commands.hooks \ import \ pre_build @@ -10,18 +8,19 @@ def pbuild(context): def build_lapack_lite(extension): kw = {} - kw["uselib"] = "npymath" + kw["use"] = "npymath" if bld.env.HAS_LAPACK: for s in ['python_xerbla.c', 'zlapack_lite.c', 'dlapack_lite.c', 'blas_lite.c', 'dlamch.c', 'f2c_lite.c']: - extension.sources.pop(extension.sources.index(s)) - kw["uselib"] = "npymath LAPACK" + extension.sources.pop(extension.sources.index('lapack_lite/' + s)) + kw["use"] = "npymath LAPACK" includes = ["../core/include", "../core/include/numpy", "../core", "../core/src/private"] return context.default_builder(extension, includes=includes, **kw) + context.register_builder("lapack_lite", build_lapack_lite) - context.register_builder("umath_linalg", build_lapack_lite) + context.register_builder("_umath_linalg", build_lapack_lite) diff --git a/numpy/linalg/lapack_lite/clapack_scrub.py b/numpy/linalg/lapack_lite/clapack_scrub.py index f8471f965..4a517d531 100644 --- a/numpy/linalg/lapack_lite/clapack_scrub.py +++ b/numpy/linalg/lapack_lite/clapack_scrub.py @@ -32,7 +32,7 @@ def runScanner(data, scanner_class, lexicon=None): scanner = scanner_class(lexicon, info) else: scanner = scanner_class(info) - while 1: + while True: value, text = scanner.read() if value is None: break diff --git a/numpy/linalg/lapack_lite/make_lite.py b/numpy/linalg/lapack_lite/make_lite.py index 66171ba85..6aa5c3e80 100755 --- a/numpy/linalg/lapack_lite/make_lite.py +++ b/numpy/linalg/lapack_lite/make_lite.py @@ -126,7 +126,7 @@ class FortranLibrary(object): """ done_this = set() last_todo = set() - while 1: + while True: todo = set(self.allRoutineNames()) - done_this if todo == last_todo: break @@ -151,8 +151,7 @@ class LapackLibrary(FortranLibrary): return routine def allRoutinesByType(self, typename): - routines = [(r.name,r) for r in self.allRoutines() if r.type == typename] - routines.sort() + routines = sorted((r.name,r) for r in self.allRoutines() if r.type == typename) return [a[1] for a in routines] def printRoutineNames(desc, routines): diff --git a/numpy/ma/extras.py b/numpy/ma/extras.py index 861fefad0..2f3159c49 100644 --- a/numpy/ma/extras.py +++ b/numpy/ma/extras.py @@ -1435,14 +1435,14 @@ class MAxisConcatenator(AxisConcatenator): def __getitem__(self, key): if isinstance(key, str): raise MAError("Unavailable for masked array.") - if type(key) is not tuple: + if not isinstance(key, tuple): key = (key,) objs = [] scalars = [] final_dtypedescr = None for k in range(len(key)): scalar = False - if type(key[k]) is slice: + if isinstance(key[k], slice): step = key[k].step start = key[k].start stop = key[k].stop @@ -1450,12 +1450,12 @@ class MAxisConcatenator(AxisConcatenator): start = 0 if step is None: step = 1 - if type(step) is type(1j): + if isinstance(step, complex): size = int(abs(step)) newobj = np.linspace(start, stop, num=size) else: newobj = np.arange(start, stop, step) - elif type(key[k]) is str: + elif isinstance(key[k], str): if (key[k] in 'rc'): self.matrix = True self.col = (key[k] == 'c') diff --git a/numpy/numarray/alter_code1.py b/numpy/numarray/alter_code1.py index 4c5b7e9fc..a80a5ae3c 100644 --- a/numpy/numarray/alter_code1.py +++ b/numpy/numarray/alter_code1.py @@ -81,7 +81,7 @@ def changeimports(fstr, name, newname): ind = 0 Nlen = len(fromstr) Nlen2 = len("from %s import " % newname) - while 1: + while True: found = fstr.find(fromstr,ind) if (found < 0): break diff --git a/numpy/numarray/functions.py b/numpy/numarray/functions.py index 3f91046d2..78d05e5f5 100644 --- a/numpy/numarray/functions.py +++ b/numpy/numarray/functions.py @@ -222,7 +222,7 @@ def fromfile(infile, type=None, shape=None, sizing=STRICT, buf = np.newbuffer(initsize) bytesread=0 - while 1: + while True: data=infile.read(blocksize) if len(data) != blocksize: ##eof break diff --git a/numpy/numarray/session.py b/numpy/numarray/session.py index f1dcbfbdc..e40cd4033 100644 --- a/numpy/numarray/session.py +++ b/numpy/numarray/session.py @@ -120,7 +120,7 @@ def _callers_modules(): g = _callers_globals() mods = [] for k,v in g.items(): - if type(v) == type(sys): + if isinstance(v, type(sys)): mods.append(getattr(v,"__name__")) return mods @@ -326,7 +326,7 @@ def load(variables=None, file=SAVEFILE, dictionary=None, verbose=False): dictionary = _callers_globals() values = [] p = pickle.Unpickler(file) - while 1: + while True: o = p.load() if isinstance(o, _SaveSession): session = dict(zip(o.keys, values)) diff --git a/numpy/oldnumeric/alter_code1.py b/numpy/oldnumeric/alter_code1.py index 1e84fd894..34a59a7ca 100644 --- a/numpy/oldnumeric/alter_code1.py +++ b/numpy/oldnumeric/alter_code1.py @@ -88,7 +88,7 @@ def changeimports(fstr, name, newname): ind = 0 Nlen = len(fromstr) Nlen2 = len("from %s import " % newname) - while 1: + while True: found = fstr.find(fromstr,ind) if (found < 0): break diff --git a/numpy/oldnumeric/alter_code2.py b/numpy/oldnumeric/alter_code2.py index 4e4d94cd6..c163c9565 100644 --- a/numpy/oldnumeric/alter_code2.py +++ b/numpy/oldnumeric/alter_code2.py @@ -54,7 +54,7 @@ def changeimports(fstr, name, newname): ind = 0 Nlen = len(fromstr) Nlen2 = len("from %s import " % newname) - while 1: + while True: found = fstr.find(fromstr,ind) if (found < 0): break diff --git a/numpy/oldnumeric/fix_default_axis.py b/numpy/oldnumeric/fix_default_axis.py index 57ab3ce78..5f6128724 100644 --- a/numpy/oldnumeric/fix_default_axis.py +++ b/numpy/oldnumeric/fix_default_axis.py @@ -185,7 +185,7 @@ def _import_change(fstr, names): ind = 0 importstr = "from numpy import" N = len(importstr) - while 1: + while True: ind = fstr.find(importstr, ind) if (ind < 0): break diff --git a/tools/c_coverage/c_coverage_report.py b/tools/c_coverage/c_coverage_report.py index d1084b75e..d9eb49739 100755 --- a/tools/c_coverage/c_coverage_report.py +++ b/tools/c_coverage/c_coverage_report.py @@ -110,8 +110,7 @@ class SourceFiles: fd = open(os.path.join(root, 'index.html'), 'w') fd.write("<html>") - paths = list(self.files.keys()) - paths.sort() + paths = sorted(self.files.keys()) for path in paths: fd.write('<p><a href="%s.html">%s</a></p>' % (self.clean_path(path), escape(path[len(self.prefix):]))) diff --git a/tools/osxbuild/docs/README.txt b/tools/osxbuild/docs/README.txt index c83ce5e97..f7a8e2a37 100644 --- a/tools/osxbuild/docs/README.txt +++ b/tools/osxbuild/docs/README.txt @@ -10,12 +10,15 @@ It derives from the old Numeric code base and can be used as a replacement for N More information can be found at the website: -http://scipy.org/NumPy +http://www.numpy.org After installation, tests can be run with: python -c 'import numpy; numpy.test()' +Starting in NumPy 1.7, deprecation warnings have been set to 'raise' by +default, so the -Wd command-line option is no longer necessary. + The most current development version is always available from our git repository: |