Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ workflows:
jobs:
build:
docker:
- image: circleci/python:3.6
- image: circleci/python:3.7
steps:
- checkout
test-flake8:
docker:
- image: circleci/python:3.6
- image: circleci/python:3.7
steps:
- checkout
- run: sudo pip install tox
- run: tox -eflake8
test-pycodestyle:
docker:
- image: circleci/python:3.6
- image: circleci/python:3.7
steps:
- checkout
- run: sudo pip install tox
Expand Down
35 changes: 35 additions & 0 deletions benchmark.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import time

from pyximport import install

import c_fibs
import py_fibs

try:
import cy_fibs
except ImportError:
install()
import cy_fibs


def benchmark(n, *fs, times=100):
r = {}

for _ in range(times):
for f in fs:
s = time.time()
f(n)
e = time.time()

r[f.__module__] = min(e - s, r.get(f.__module__, e - s))

print("module".ljust(10), "best".ljust(25), "times")
for k, v in r.items():
print(k.ljust(10), str(v).ljust(25), v / min(*r.values()))


if __name__ == "__main__":
# Maximum value for C unsigned long long type
benchmark(93, c_fibs.get_number, cy_fibs.get_number, py_fibs.get_number)

benchmark(1_000_000, cy_fibs.get_number, py_fibs.get_number, times=3)
46 changes: 46 additions & 0 deletions c_fibs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include <Python.h>

unsigned long long fibonacci(unsigned long long n)
{
unsigned long long f[n+2];
unsigned long long i;

f[0] = 0;
f[1] = 1;

for (i = 2; i <= n; i++)
{
f[i] = f[i-1] + f[i-2];
}

return f[n];
}

static PyObject* get_number(PyObject* self, PyObject* args)
{
unsigned long long n;

if (!PyArg_ParseTuple(args, "K", &n))
return NULL;

return Py_BuildValue("K", fibonacci(n));
}

static PyMethodDef fibonacci_methods[] = {
{"get_number", get_number, METH_VARARGS, "Get fibonacci number."},
{NULL, NULL, 0, NULL}
};

static struct PyModuleDef c_fibs_definition = {
PyModuleDef_HEAD_INIT,
"c_fibs",
NULL,
-1,
fibonacci_methods
};

PyMODINIT_FUNC PyInit_c_fibs(void) {
Py_Initialize();

return PyModule_Create(&c_fibs_definition);
}
22 changes: 22 additions & 0 deletions cy_fibs.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
def _matmul(left, right):
return [left[0] * right[0] + left[1] * right[2],
left[0] * right[1] + left[1] * right[3],
left[2] * right[0] + left[3] * right[2],
left[2] * right[1] + left[3] * right[3]]


def _pow(base, power):
result = [1, 0, 0, 1]

while power:
if power % 2:
result = _matmul(result, base)

power >>= 1
base = _matmul(base, base)

return result


def get_number(n):
return _pow([1, 1, 1, 0], n)[1]
30 changes: 30 additions & 0 deletions py_fibs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
class Q:

__slots__ = ("q00", "q01", "q10", "q11")

def __init__(self, q00=1, q01=1, q10=1, q11=0):
self.q00, self.q01, self.q10, self.q11 = q00, q01, q10, q11

def __imatmul__(self, other):
self.q00, self.q01, self.q10, self.q11 = (
self.q00 * other.q00 + self.q01 * other.q10,
self.q00 * other.q01 + self.q01 * other.q11,
self.q10 * other.q00 + self.q11 * other.q10,
self.q10 * other.q01 + self.q11 * other.q11
)

return self


def get_number(n):
base = Q()
result = Q(1, 0, 0, 1)

while n:
if n % 2:
result @= base

n >>= 1
base @= base

return result.q01
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
Cython
flake8
hypothesis
pydocstyle
11 changes: 11 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from setuptools import Extension, setup

setup(
author="Pavel Tsialnou",
author_email="pavel_tsialnou@epam.com",
ext_modules=[
Extension("c_fibs", sources=["c_fibs.c"])
],
name="c_fibs",
version="0.1",
)
45 changes: 45 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from hypothesis import given, settings
from hypothesis.strategies import integers
from pyximport import install

import c_fibs
import py_fibs

try:
import cy_fibs
except ImportError:
install()
import cy_fibs


@given(integers(1, 93))
def test_c_cy_py(n):
c = c_fibs.get_number(n)
cy = cy_fibs.get_number(n)
py = py_fibs.get_number(n)

assert c == cy == py


@given(integers(94, 1000))
@settings(max_examples=100)
def test_cy_py_1000(n):
cy = cy_fibs.get_number(n)
py = py_fibs.get_number(n)

assert cy == py


@given(integers(1000, 1_000_000))
@settings(deadline=10_000, max_examples=5)
def test_cy_py_1_000_000(n):
cy = cy_fibs.get_number(n)
py = py_fibs.get_number(n)

assert cy == py


if __name__ == "__main__":
test_c_cy_py()
test_cy_py_1000()
test_cy_py_1_000_000()
6 changes: 3 additions & 3 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ envlist = flake8,pycodestyle
skipsdist = True

[testenv]
basepython = python3.6
basepython = python3.7
usedevelop = False
install_command = pip install {opts} {packages}
deps = -r{toxinidir}/requirements.txt


[testenv:flake8]
deps = hacking
deps = flake8==3.7.7
commands = flake8 {posargs}

[flake8]
Expand All @@ -20,5 +20,5 @@ max-line-length = 100
exclude = .venv,.tox,doc,*egg,.git

[testenv:pycodestyle]
deps = hacking
deps = pycodestyle==2.5.0
commands = pycodestyle