What algorithm does Python employ in fractions.gcd()? - python
I'm using the fractions module in Python v3.1 to compute the greatest common divisor. I would like to know what algorithm is used. I'm guessing the Euclidean method, but would like to be sure. The docs (http://docs.python.org/py3k/library/fractions.html?highlight=fractions.gcd#fractions.gcd) don't help. Can anybody clue me in?
According to the 3.1.2 source code online, here's gcd as defined in Python-3.1.2/Lib/fractions.py:
def gcd(a, b):
"""Calculate the Greatest Common Divisor of a and b.
Unless b==0, the result will have the same sign as b (so that when
b is divided by it, the result comes out positive).
"""
while b:
a, b = b, a%b
return a
So yes, it's the Euclidean algorithm, written in pure Python.
From fractions python
"Deprecated since version 3.5: Use math.gcd() instead."
I was looking for the algorithm as well. I hope it helped.
Since Python 3.5, the GCD code has been moved to math.gcd. Since Python 3.9, math.gcd takes an arbitrary number of arguments.
The actual GCD code is now implemented in C (for CPython), making it significantly faster than the original pure Python implementation.
Boilerplate:
static PyObject *
math_gcd(PyObject *module, PyObject * const *args, Py_ssize_t nargs)
{
PyObject *res, *x;
Py_ssize_t i;
if (nargs == 0) {
return PyLong_FromLong(0);
}
res = PyNumber_Index(args[0]);
if (res == NULL) {
return NULL;
}
if (nargs == 1) {
Py_SETREF(res, PyNumber_Absolute(res));
return res;
}
PyObject *one = _PyLong_GetOne(); // borrowed ref
for (i = 1; i < nargs; i++) {
x = _PyNumber_Index(args[i]);
if (x == NULL) {
Py_DECREF(res);
return NULL;
}
if (res == one) {
/* Fast path: just check arguments.
It is okay to use identity comparison here. */
Py_DECREF(x);
continue;
}
Py_SETREF(res, _PyLong_GCD(res, x));
Py_DECREF(x);
if (res == NULL) {
return NULL;
}
}
return res;
}
The actual computation uses Lehmer's GCD algorithm:
PyObject *
_PyLong_GCD(PyObject *aarg, PyObject *barg)
{
PyLongObject *a, *b, *c = NULL, *d = NULL, *r;
stwodigits x, y, q, s, t, c_carry, d_carry;
stwodigits A, B, C, D, T;
int nbits, k;
Py_ssize_t size_a, size_b, alloc_a, alloc_b;
digit *a_digit, *b_digit, *c_digit, *d_digit, *a_end, *b_end;
a = (PyLongObject *)aarg;
b = (PyLongObject *)barg;
size_a = Py_SIZE(a);
size_b = Py_SIZE(b);
if (-2 <= size_a && size_a <= 2 && -2 <= size_b && size_b <= 2) {
Py_INCREF(a);
Py_INCREF(b);
goto simple;
}
/* Initial reduction: make sure that 0 <= b <= a. */
a = (PyLongObject *)long_abs(a);
if (a == NULL)
return NULL;
b = (PyLongObject *)long_abs(b);
if (b == NULL) {
Py_DECREF(a);
return NULL;
}
if (long_compare(a, b) < 0) {
r = a;
a = b;
b = r;
}
/* We now own references to a and b */
alloc_a = Py_SIZE(a);
alloc_b = Py_SIZE(b);
/* reduce until a fits into 2 digits */
while ((size_a = Py_SIZE(a)) > 2) {
nbits = bit_length_digit(a->ob_digit[size_a-1]);
/* extract top 2*PyLong_SHIFT bits of a into x, along with
corresponding bits of b into y */
size_b = Py_SIZE(b);
assert(size_b <= size_a);
if (size_b == 0) {
if (size_a < alloc_a) {
r = (PyLongObject *)_PyLong_Copy(a);
Py_DECREF(a);
}
else
r = a;
Py_DECREF(b);
Py_XDECREF(c);
Py_XDECREF(d);
return (PyObject *)r;
}
x = (((twodigits)a->ob_digit[size_a-1] << (2*PyLong_SHIFT-nbits)) |
((twodigits)a->ob_digit[size_a-2] << (PyLong_SHIFT-nbits)) |
(a->ob_digit[size_a-3] >> nbits));
y = ((size_b >= size_a - 2 ? b->ob_digit[size_a-3] >> nbits : 0) |
(size_b >= size_a - 1 ? (twodigits)b->ob_digit[size_a-2] << (PyLong_SHIFT-nbits) : 0) |
(size_b >= size_a ? (twodigits)b->ob_digit[size_a-1] << (2*PyLong_SHIFT-nbits) : 0));
/* inner loop of Lehmer's algorithm; A, B, C, D never grow
larger than PyLong_MASK during the algorithm. */
A = 1; B = 0; C = 0; D = 1;
for (k=0;; k++) {
if (y-C == 0)
break;
q = (x+(A-1))/(y-C);
s = B+q*D;
t = x-q*y;
if (s > t)
break;
x = y; y = t;
t = A+q*C; A = D; B = C; C = s; D = t;
}
if (k == 0) {
/* no progress; do a Euclidean step */
if (l_mod(a, b, &r) < 0)
goto error;
Py_SETREF(a, b);
b = r;
alloc_a = alloc_b;
alloc_b = Py_SIZE(b);
continue;
}
/*
a, b = A*b-B*a, D*a-C*b if k is odd
a, b = A*a-B*b, D*b-C*a if k is even
*/
if (k&1) {
T = -A; A = -B; B = T;
T = -C; C = -D; D = T;
}
if (c != NULL) {
Py_SET_SIZE(c, size_a);
}
else if (Py_REFCNT(a) == 1) {
c = (PyLongObject*)Py_NewRef(a);
}
else {
alloc_a = size_a;
c = _PyLong_New(size_a);
if (c == NULL)
goto error;
}
if (d != NULL) {
Py_SET_SIZE(d, size_a);
}
else if (Py_REFCNT(b) == 1 && size_a <= alloc_b) {
d = (PyLongObject*)Py_NewRef(b);
Py_SET_SIZE(d, size_a);
}
else {
alloc_b = size_a;
d = _PyLong_New(size_a);
if (d == NULL)
goto error;
}
a_end = a->ob_digit + size_a;
b_end = b->ob_digit + size_b;
/* compute new a and new b in parallel */
a_digit = a->ob_digit;
b_digit = b->ob_digit;
c_digit = c->ob_digit;
d_digit = d->ob_digit;
c_carry = 0;
d_carry = 0;
while (b_digit < b_end) {
c_carry += (A * *a_digit) - (B * *b_digit);
d_carry += (D * *b_digit++) - (C * *a_digit++);
*c_digit++ = (digit)(c_carry & PyLong_MASK);
*d_digit++ = (digit)(d_carry & PyLong_MASK);
c_carry >>= PyLong_SHIFT;
d_carry >>= PyLong_SHIFT;
}
while (a_digit < a_end) {
c_carry += A * *a_digit;
d_carry -= C * *a_digit++;
*c_digit++ = (digit)(c_carry & PyLong_MASK);
*d_digit++ = (digit)(d_carry & PyLong_MASK);
c_carry >>= PyLong_SHIFT;
d_carry >>= PyLong_SHIFT;
}
assert(c_carry == 0);
assert(d_carry == 0);
Py_INCREF(c);
Py_INCREF(d);
Py_DECREF(a);
Py_DECREF(b);
a = long_normalize(c);
b = long_normalize(d);
}
Py_XDECREF(c);
Py_XDECREF(d);
simple:
assert(Py_REFCNT(a) > 0);
assert(Py_REFCNT(b) > 0);
/* Issue #24999: use two shifts instead of ">> 2*PyLong_SHIFT" to avoid
undefined behaviour when LONG_MAX type is smaller than 60 bits */
#if LONG_MAX >> PyLong_SHIFT >> PyLong_SHIFT
/* a fits into a long, so b must too */
x = PyLong_AsLong((PyObject *)a);
y = PyLong_AsLong((PyObject *)b);
#elif LLONG_MAX >> PyLong_SHIFT >> PyLong_SHIFT
x = PyLong_AsLongLong((PyObject *)a);
y = PyLong_AsLongLong((PyObject *)b);
#else
# error "_PyLong_GCD"
#endif
x = Py_ABS(x);
y = Py_ABS(y);
Py_DECREF(a);
Py_DECREF(b);
/* usual Euclidean algorithm for longs */
while (y != 0) {
t = y;
y = x % y;
x = t;
}
#if LONG_MAX >> PyLong_SHIFT >> PyLong_SHIFT
return PyLong_FromLong(x);
#elif LLONG_MAX >> PyLong_SHIFT >> PyLong_SHIFT
return PyLong_FromLongLong(x);
#else
# error "_PyLong_GCD"
#endif
error:
Py_DECREF(a);
Py_DECREF(b);
Py_XDECREF(c);
Py_XDECREF(d);
return NULL;
}
Related
How can I add a colour gradient feature to my bresenham line drawing algorithm?
I've found this thread on StackOverflow but my python understanding isn't that good to properly translate it to C, I'm trying to add that gradient feature to this line drawing algorithm: #define sign(x) ((x > 0)? 1 : ((x < 0)? -1: 0)) x = x1; y = y1; dx = abs(x2 - x1); dy = abs(y2 - y1); s1 = sign(x2 - x1); s2 = sign(y2 - y1); swap = 0; if (dy > dx) { temp = dx; dx = dy; dy = temp; swap = 1; } D = 2*dy - dx; for (i = 0; i < dx; i++) { display_pixel (x, y); while (D >= 0) { D = D - 2*dx; if (swap) x += s1; else y += s2; } D = D + 2*dy; if (swap) y += s2; else x += s1; } I feel bad for asking such a trivial task but I really can't understand what is going on on the python side nor how the colours are represented (mine are int(0xttrrggbb))
Figured it out: #define GAMMA 0.43 //Returns a linear value in the range [0,1] //for sRGB input in [0,255]. double ChannelInvCompanding(int c) { double y; c = c & 0xFF; y = (double) c; y = y / 255.0; if (c <= 0.04045) y = y / 12.92; else y = pow(((y + 0.055) / 1.055), 2.4); return (y); } //Convert color from 0..255 to 0..1 //Inverse Srgb Companding for //Red, Green, and Blue double *InverseSrgbCompanding(int c) { double *r = malloc(4 * sizeof(double)); r[0] = (double) get_t(c); r[1] = ChannelInvCompanding(get_r(c)); r[2] = ChannelInvCompanding(get_g(c)); r[3] = ChannelInvCompanding(get_b(c)); return (r); } //Apply companding to Red, Green, and Blue double ChannelCompanding(double c) { double x; if (c <= 0.0031308) x = 12.92 * c; else x = (1.055 * pow(c, (1/2.4))) - 0.055; return (x); } //return new color. Convert 0..1 back into 0..255 //Srgb Companding for Red, Green, and Blue int SrgbCompanding(double *c) { int t; int r; int g; int b; t = (int)c[0]; r = (int)(ChannelCompanding(c[1]) * 255); g = (int)(ChannelCompanding(c[2]) * 255); b = (int)(ChannelCompanding(c[3]) * 255); free(c); return (create_trgb(t, r, g, b)); } //sums channels //does not include transperancy double sumChannels(double *c) { double x = c[1] + c[2] + c[3]; return (x); } //Lerping see //https://en.wikipedia.org/wiki/Linear_interpolation //#Programming_language_support double lerp_int(double c1, double c2, double t) { return (c1 * (1 - t) + c2 * t); //return ((1 - t) * c1 + t * c2); } double *lerp(double *c1, double *c2, double t) { double *r = malloc(4 * sizeof(double)); //r[1] = ((1 - t) * c1[1] + t * c2[1]); //r[2] = ((1 - t) * c1[2] + t * c2[2]); //r[3] = ((1 - t) * c1[3] + t * c2[3]); r[1] = (c1[1] * (1 - t)) + c2[1] * t; r[2] = (c1[2] * (1 - t)) + c2[2] * t; r[3] = (c1[3] * (1 - t)) + c2[3] * t; return (r); } typedef struct s_bresvars { int x; int y; int dx; int dy; int s1; int s2; int swap; int temp; int d; int i; } t_bresvars; int sign(int x) { if (x > 0) return (1); else if (x < 0) return (-1); else return (0); } void bresenhams_alg(int x1, int y1, int x2, int y2, int scolor, int ecolor, t_vars *vars) { double step; double *color; double intensity; double total; int temp; int d; int clr; double *color1_lin = InverseSrgbCompanding(scolor); double bright1 = pow(sumChannels(c.color1_lin), GAMMA); double *color2_lin = InverseSrgbCompanding(ecolor); double bright2 = pow(sumChannels(c.color2_lin), GAMMA); int x = x1; int y = y1; int dx = abs(x2 - x1); int dy = abs(y2 - y1); int s1 = sign(x2 - x1); int s2 = sign(y2 - y1); int swap = 0; int i = 0; double step_c = 0; if (dy > dx) { temp = dx; dx = dy; dy = temp; swap = 1; } d = 2*dy - dx; step = (1.0 / dx); while (i < dx) { step_c += step; intensity = pow(lerp_int(bright1, bright2, step), (1 / GAMMA)); color = lerp(color1_lin, color2_lin, step); total = sumChannels(color); if (total != 0) c[1] = (c[1] * intensity / total); c[2] = (c[2] * intensity / total); c[3] = (c[3] * intensity / total); clr = SrgbCompanding(color); pixel_put(x, y, clr); while (v.d >= 0) { v.d = v.d - 2 * v.dx; if (v.swap) v.x += v.s1; else v.y += v.s2; } v.d = v.d + 2 * v.dy; if (v.swap) v.y += v.s2; else v.x += v.s1; v.i++; } free(color1_lin); free(color2_lin); }
Primality test Python C extension is slower than pure Python
I've implemented a 6k+-1 primality test function both in a C extension and pure Python code but seems pure Python code is much faster! is there something wrong with my C code or something else? I also compiled a similar test in pure C with the is_prime function, and its execution time was the same as the C extension (almost 2sec) primemodule.c #define PY_SSIZE_T_CLEAN #include "Python.h" int is_prime(int n) { int i; if (n <= 3) return (n > 1); if (n % 2 == 0 || n % 3 == 0) return (0); i = 5; while ((i * i) <= n) { if (n % i == 0 || n % (i + 2) == 0) return (0); i += 6; } return (1); } static PyObject *prime_isprime(PyObject *self, PyObject *args) { int n; if (!PyArg_ParseTuple(args, "i", &n)) return (NULL); if (is_prime(n)) Py_RETURN_TRUE; Py_RETURN_FALSE; } static PyMethodDef prime_methods[] = { {"is_prime", prime_isprime, METH_VARARGS, "Check if a number is prime"}, {NULL, NULL, 0, NULL}}; static struct PyModuleDef prime_module = { PyModuleDef_HEAD_INIT, "prime", NULL, -1, prime_methods}; PyMODINIT_FUNC PyInit_prime(void) { return (PyModule_Create(&prime_module)); } py_test.py import time MAX_INT = 2147483647 def is_prime(n: int) -> bool: if n <= 3: return n > 1 if n % 2 == 0 or n % 3 == 0: return False i = 5 while i ** 2 <= n: if n % i == 0 or n % (i + 2) == 0: return False i += 6 return True t1 = time.process_time() for i in range(MAX_INT - 100, MAX_INT): is_prime(i) print(time.process_time() - t1, "seconds") c_test.py import time import prime MAX_INT = 2147483647 t1 = time.process_time() for i in range(MAX_INT - 100, MAX_INT): prime.is_prime(i) print(time.process_time() - t1, "seconds") python c_test.py 2.078125 seconds python py_test.py 0.03125 seconds timecmd.bat a.exe 2.13 seconds
I think your C implementation is buggy regarding integer overflows and signedness and ends up in a bigger loop than the Python version. Changing the parameter type to unsigned int (and i too, since otherwise that's a compiler warning): static int is_prime(unsigned int n) { unsigned int i; if (n <= 3) return (n > 1); if (n == 2 || n == 3) return (1); if (n % 2 == 0 || n % 3 == 0) return (0); i = 5; while ((i * i) <= n) { if (n % i == 0 || n % (i + 2) == 0) return (0); i += 6; } return (1); } makes it (anecdotally, on my machine, approximately) 37 times faster than the Python implementation.
How to sort points along a Hilbert curve without using Hilbert indices?
I'm trying to implement the algorithm described in the paper Fast Hilbert Sort Algorithm Without Using Hilbert Indices (https://www.researchgate.net/profile/Takeshi_Shinohara/publication/313074453_Fast_Hilbert_Sort_Algorithm_Without_Using_Hilbert_Indices/links/5b8468bd299bf1d5a72b9a0c/Fast-Hilbert-Sort-Algorithm-Without-Using-Hilbert-Indices.pdf?origin=publication_detail), but I can't get the right results. Below is my python code (For bitset and it's member functions flip and test in C++ , please refer to https://en.cppreference.com/w/cpp/utility/bitset): N=9 # 9 points n=2 # 2 dimension m=3 # order of Hilbert curve b=m-1 def BitTest(x,od,maxlen=3): bit=format(x,'b').zfill(maxlen) return int(bit[maxlen-1-od]) def BitFlip(b,pos,): b ^= 1 << pos return b def partition(A,st,en,od,ax,di): i = st j = en while True: while i < j and BitTest(A[i][ax],od)==di: i = i + 1 while i < j and BitTest(A[j][ax],od)!=di: j = j - 1 if i >= j: return i A[i], A[j] = A[j], A[i] def HSort(A,st,en,od,c,e,d,di,cnt): if en<=st: return p =partition(A,st,en,od,(d+c)%n,BitTest(e,(d+c)%n)) if c==n-1: if b==0: return d2= (d+n+n-(di if(di==2) else cnt+2))%n e=BitFlip(e,d2) e=BitFlip(e,(d+c)%n) HSort(A,st,p-1,b-1,0,e,d2,False,0) e=BitFlip(e,(d+c)%n) e=BitFlip(e,d2) d2= (d+n+n-(di if(di==cnt+2) else 2))%n HSort(A,p+1,en,b-1,0,e,d2,False,0) else: HSort(A,st,p-1,b,c+1,e,d,False,(di if(di==1) else cnt+1)) e=BitFlip(e,(d+c)%n) e=BitFlip(e,(d+c+1)%n) HSort(A,p+1,en,b,c+1,e,d,True,(di if(di==cnt+1) else 1)) e=BitFlip(e,(d+c+1)%n) e=BitFlip(e,(d+c)%n) array = [[2,2],[2,4],[3,4],[2,5],[3,5],[1,6],[3,6],[5,6],[3,7]] HSort(array,st=0,en=N-1,od=m-1,c=0,e=0,d=0,di=False,cnt=0) print(array)
That document has a typo, the constant "b" should be replaced with "od". Here is a working code in c++: #include <iostream> #include <vector> #include <array> constexpr std::int32_t m = 3; constexpr std::int32_t n = 2; bool test_bit(std::int32_t value, std::int32_t pos) { const auto result = value & (1 << pos); return result; } void flip_bit(std::int32_t &value, std::int32_t pos) { value ^= 1 << pos; } std::int32_t partition(std::vector<std::array<std::int32_t, 2>> &A, std::size_t st, std::size_t en, std::int32_t od, std::int32_t ax, bool di) { std::int32_t i = st - 1; std::int32_t j = en + 1; while(true) { do i = i + 1; while(i < j && test_bit(A[i][ax], od) == di); do j = j - 1; while(i < j && test_bit(A[j][ax], od) != di); if(j <= i) return i; //partition is complete std::swap(A[i], A[j]); } } void hilbert_sort(std::vector<std::array<std::int32_t, 2>> &A, std::size_t st, std::size_t en, std::int32_t od, std::int32_t c, std::int32_t &e, std::int32_t d, bool di, std::int32_t cnt) { std::int32_t p; std::int32_t d2; if(en <= st) return; p = partition(A, st, en, od, (d + c) % n, test_bit(e, (d + c) % n)); if(c == n - 1) { if(od == 0) return; d2 = (d + n + n - (di ? 2 : cnt + 2)) % n; flip_bit(e, d2); flip_bit(e, (d + c) % n); hilbert_sort(A, st, p - 1, od - 1, 0, e, d2, false, 0); flip_bit(e, (d + c) % n); flip_bit(e, d2); d2 = (d + n + n - (di ? cnt + 2 : 2)) % n; hilbert_sort(A, p, en, od - 1, 0, e, d2, false, 0); } else { hilbert_sort(A, st, p - 1, od, c + 1, e, d, false, di ? 1 : cnt + 1); flip_bit(e, (d + c) % n); flip_bit(e, (d + c + 1) % n); hilbert_sort(A, p, en, od, c + 1, e, d, true, di ? cnt + 1 : 1); flip_bit(e, (d + c + 1) % n); flip_bit(e, (d + c) % n); } } int main() { std::vector<std::array<std::int32_t, 2>> points = {{2,2},{2,4},{3,4},{2,5},{3,5},{1,6},{3,6},{5,6},{3,7}}; std::int32_t e = 0; hilbert_sort(points, 0, points.size() - 1, m - 1, 0, e, 0, false , 0); for(const auto &point : points) std::clog << "(" << point[0] << ", " << point[1] << ")\n"; return 0; } You also seems to have a typo "p+1" it should be just "p". Here is a working python code: N=9 # 9 points n=2 # 2 dimension m=3 # order of Hilbert curve def BitTest(x,od): result = x & (1 << od) return int(bool(result)) def BitFlip(b,pos): b ^= 1 << pos return b def partition(A,st,en,od,ax,di): i = st j = en while True: while i < j and BitTest(A[i][ax],od) == di: i = i + 1 while i < j and BitTest(A[j][ax],od) != di: j = j - 1 if j <= i: return i A[i], A[j] = A[j], A[i] def HSort(A,st,en,od,c,e,d,di,cnt): if en<=st: return p = partition(A,st,en,od,(d+c)%n,BitTest(e,(d+c)%n)) if c==n-1: if od==0: return d2= (d+n+n-(2 if di else cnt + 2)) % n e=BitFlip(e,d2) e=BitFlip(e,(d+c)%n) HSort(A,st,p-1,od-1,0,e,d2,False,0) e=BitFlip(e,(d+c)%n) e=BitFlip(e,d2) d2= (d+n+n-(cnt + 2 if di else 2))%n HSort(A,p,en,od-1,0,e,d2,False,0) else: HSort(A,st,p-1,od,c+1,e,d,False,(1 if di else cnt+1)) e=BitFlip(e,(d+c)%n) e=BitFlip(e,(d+c+1)%n) HSort(A,p,en,od,c+1,e,d,True,(cnt+1 if di else 1)) e=BitFlip(e,(d+c+1)%n) e=BitFlip(e,(d+c)%n) array = [[2,2],[2,4],[3,4],[2,5],[3,5],[1,6],[3,6],[5,6],[3,7]] HSort(array,st=0,en=N-1,od=m-1,c=0,e=0,d=0,di=False,cnt=0) print(array)
C++ equivalent of python's expandtabs() function?
I need to implement the C++ equivalent of the expandtabs() function. Can someone help me with converting this code to C++? def expandtabs(string, n): result = "" pos = 0 for char in string: if char == "\t": # instead of the tab character, append the # number of spaces to the next tab stop char = " " * (n - pos % n) pos = 0 elif char == "\n": pos = 0 else: pos += 1 result += char return result This is what I have: std::string ExpandTabs(const std::string &str, int tabsize =4){ std::string ReturnString = str; std::string result = " "; int pos = 0; for(std::string::size_type i = 0; i < ReturnString.size(); ++i) { if (ReturnString[i] == '\t'){ int spaces = tabsize - pos % tabsize ; ReturnString.append(" ", spaces); pos = 0; } else{ pos+=1; } } return ReturnString;
You need to build up the string character by character. Currently you assign str to ReturnString at the start of the function and then append whatever spaces you decide are necessary to the end of the string, instead of in place of the tabs. There are no doubt more idiomatic ways to achieve the same result, but a like for like conversion of the python might look like. #include <iostream> #include <string> std::string expand_tabs(const std::string &str, int tabsize=4) { std::string result = ""; int pos = 0; for(char c: str) { if(c == '\t') { // append the spaces here. result.append(tabsize - pos % tabsize, ' '); pos = 0; } else { result += c; pos = (c == '\n') ? 0: pos + 1; } } return result; } int main() { std::cout << expand_tabs("i\tam\ta\tstring\twith\ttabs") << '\n'; std::cout << expand_tabs("1\t2\t3\t4", 2) << '\n'; } It basically steps through the input forwarding on any non tab characters to the result string, otherwise it adds the correct number of spaces to the result. Output: i am a string with tabs 1 2 3 4
A straight translation of the python code is problematic, since char cannot be both a string and a single character, but apart from that it's straightforward: std::string expandtabs(std::string const&str, std::string::size_type tabsize=8) { std::string result; std::string::size_type pos = 0 result.reserve(str.size()); // avoid re-allocations in case there are no tabs for(c : str) switch(c) { default: result += c; ++pos; break; case '\n': result += c; pos = 0; break; case '\t': result.append(tabsize - pos % tabsize,' '); pos = 0; } return result }
Cython: for i from 1 <= i < N
I'm learning Cython and came across this snippit of code: import numpy as np cimport numpy as np def mean(np.ndarray[np.double_t] input): cdef np.double_t cur # Py_ssize_t is numpy's index type cdef Py_ssize_t i cdef Py_ssize_t N = len(input) for i from 0 <= i < N: cur += input[i] return cur / N a=np.array([1,2,3,4], dtype=np.double) Obviously, this returns the mean of a which is 2.5. My question is this: Is the for loop a Python loop, Cython, or C?
Compile it and see: the C code that Cython produces is nicely annotated. /* "cyexample.pyx":11 * cdef Py_ssize_t N = len(input) * * for i from 0 <= i < N: # <<<<<<<<<<<<<< * cur += input[i] * */ __pyx_t_1 = __pyx_v_N; for (__pyx_v_i = 0; __pyx_v_i < __pyx_t_1; __pyx_v_i++) { /* "cyexample.pyx":12 * * for i from 0 <= i < N: * cur += input[i] # <<<<<<<<<<<<<< * * return cur / N */ __pyx_t_2 = __pyx_v_i; __pyx_t_3 = -1; if (__pyx_t_2 < 0) { __pyx_t_2 += __pyx_bshape_0_input; if (unlikely(__pyx_t_2 < 0)) __pyx_t_3 = 0; } else if (unlikely(__pyx_t_2 >= __pyx_bshape_0_input)) __pyx_t_3 = 0; if (unlikely(__pyx_t_3 != -1)) { __Pyx_RaiseBufferIndexError(__pyx_t_3); {__pyx_filename = __pyx_f[0]; __pyx_lineno = 12; __pyx_clineno = __LINE__; goto __pyx_L1_error;} } __pyx_v_cur = (__pyx_v_cur + (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_double_t *, __pyx_bstruct_input.buf, __pyx_t_2, __pyx_bstride_0_input))); } And so the loop itself is successfully turned into C. Note that these days Cython can handle range naturally, so the older "from 0 <= i < N" style isn't necessary. The point of introducing the (non-Python) "for/from" syntax was to signify which loops should be C-ified.
for..from seems to be a Pyrex / Cython loop: http://docs.cython.org/src/userguide/language_basics.html#integer-for-loops