From a9179378e589ccd4612ba3fe3f043c0b38f1f9b7 Mon Sep 17 00:00:00 2001
From: Michiel Cottaar <MichielCottaar@gmail.com>
Date: Fri, 16 Feb 2018 11:47:35 +0000
Subject: [PATCH] Added packages/speed talks

---
 talks/packages/packages.ipynb |  870 ++++++++++++++
 talks/packages/packages.md    |  579 +++++++++
 talks/speed/speed.ipynb       | 2060 +++++++++++++++++++++++++++++++++
 3 files changed, 3509 insertions(+)
 create mode 100644 talks/packages/packages.ipynb
 create mode 100644 talks/packages/packages.md
 create mode 100644 talks/speed/speed.ipynb

diff --git a/talks/packages/packages.ipynb b/talks/packages/packages.ipynb
new file mode 100644
index 0000000..0c2b66f
--- /dev/null
+++ b/talks/packages/packages.ipynb
@@ -0,0 +1,870 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Main scientific python libraries\n",
+    "See https://scipy.org/\n",
+    "\n",
+    "Most of these packages have or are in thr progress of dropping support for python2.\n",
+    "So use python3!\n",
+    "\n",
+    "## [Numpy](http://www.numpy.org/): arrays\n",
+    "This is the main library underlying (nearly) all of the scientific python ecosystem.\n",
+    "See the tutorial in the beginner session or [the official numpy tutorial](https://docs.scipy.org/doc/numpy-dev/user/quickstart.html) for usage details.\n",
+    "\n",
+    "The usual nickname of numpy is np:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import numpy as np"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Numpy includes support for:\n",
+    "- N-dimensional arrays with various datatypes\n",
+    "- basic functions (e.g., polynomials)\n",
+    "- basic linear algebra\n",
+    "- random number generation\n",
+    "\n",
+    "## [Scipy](https://scipy.org/scipylib/index.html): most general scientific tools\n",
+    "At the top level this module includes all of the basic functionality from numpy.\n",
+    "You could import this as, but you might as well import numpy directly."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import scipy as sp"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The main strength in scipy lies in its sub-packages:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from scipy import optimize\n",
+    "def costfunc(params):\n",
+    "    return params[0] ** 2 * (params[1] - 3) ** 2 + (params[0] - 2) ** 2\n",
+    "optimize.minimize(costfunc, x0=[0, 0], method='l-bfgs-b')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Tutorials for all sub-packages can be found [here](https://docs.scipy.org/doc/scipy-1.0.0/reference/).\n",
+    "\n",
+    "## [Matplotlib](https://matplotlib.org/): Main plotting library"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import matplotlib as mpl\n",
+    "mpl.use('nbagg')\n",
+    "import matplotlib.pyplot as plt"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The matplotlib tutorials are [here](https://matplotlib.org/tutorials/index.html)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "x = np.linspace(0, 2, 100)\n",
+    "\n",
+    "plt.plot(x, x, label='linear')\n",
+    "plt.plot(x, x**2, label='quadratic')\n",
+    "plt.plot(x, x**3, label='cubic')\n",
+    "\n",
+    "plt.xlabel('x label')\n",
+    "plt.ylabel('y label')\n",
+    "\n",
+    "plt.title(\"Simple Plot\")\n",
+    "\n",
+    "plt.legend()\n",
+    "\n",
+    "plt.show()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Alternatives:\n",
+    "- [Mayavi](http://docs.enthought.com/mayavi/mayavi/): 3D plotting (hard to install)\n",
+    "- [Bokeh](https://bokeh.pydata.org/en/latest/) among many others: interactive plots in the browser (i.e., in javascript)\n",
+    "\n",
+    "## [Ipython](http://ipython.org/)/[Jupyter](https://jupyter.org/) notebook: interactive python environments\n",
+    "There are many [useful extensions available](https://github.com/ipython-contrib/jupyter_contrib_nbextensions).\n",
+    "\n",
+    "## [Pandas](https://pandas.pydata.org/): Analyzing \"clean\" data\n",
+    "Once your data is in tabular form (e.g. Biobank IDP's), you want to use pandas dataframes to analyze them.\n",
+    "This brings most of the functionality of R into python.\n",
+    "Pandas has excellent support for:\n",
+    "- fast IO to many tabular formats\n",
+    "- accurate handling of missing data\n",
+    "- Many, many routines to handle data\n",
+    "  - group by categorical data (i.e., male/female, or age groups)\n",
+    "  - joining/merging data\n",
+    "  - time series support\n",
+    "- statistical models through [statsmodels](http://www.statsmodels.org/stable/index.html)\n",
+    "- plotting though seaborn [seaborn](https://seaborn.pydata.org/)\n",
+    "- Use [dask](https://dask.pydata.org/en/latest/) if your data is too big for memory (or if you want to run in parallel)\n",
+    "\n",
+    "You should also install `numexpr` and `bottleneck` for optimal performance.\n",
+    "\n",
+    "For the documentation check [here](http://pandas.pydata.org/pandas-docs/stable/index.html)\n",
+    "\n",
+    "### Adjusted example from statsmodels tutorial"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import statsmodels.api as sm\n",
+    "import statsmodels.formula.api as smf\n",
+    "import numpy as np"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "df = sm.datasets.get_rdataset(\"Guerry\", \"HistData\").data\n",
+    "df"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "df.describe()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "df.groupby('Region').mean()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "results = smf.ols('Lottery ~ Literacy + np.log(Pop1831)', data=df).fit()\n",
+    "results.summary()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "df['log_pop'] = np.log(df.Pop1831)\n",
+    "df"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "results = smf.ols('Lottery ~ Literacy + log_pop', data=df).fit()\n",
+    "results.summary()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "results = smf.ols('Lottery ~ Literacy + np.log(Pop1831) + Region', data=df).fit()\n",
+    "results.summary()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "results = smf.ols('Lottery ~ Literacy + np.log(Pop1831) + Region + Region * Literacy', data=df).fit()\n",
+    "results.summary()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%matplotlib nbagg\n",
+    "import seaborn as sns\n",
+    "sns.pairplot(df, hue=\"Region\", vars=('Lottery', 'Literacy', 'log_pop'))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## [Sympy](http://www.sympy.org/en/index.html): Symbolic programming"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import sympy as sym  # no standard nickname"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "x, a, b, c = sym.symbols('x, a, b, c')\n",
+    "sym.solve(a * x ** 2 + b * x + c, x)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "sym.integrate(x/(x**2+a*x+2), x)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "f = sym.utilities.lambdify((x, a), sym.integrate((x**2+a*x+2), x))\n",
+    "f(np.random.rand(10), np.random.rand(10))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Other topics\n",
+    "## [Argparse](https://docs.python.org/3.6/howto/argparse.html): Command line arguments"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%%writefile test_argparse.py\n",
+    "import argparse\n",
+    "\n",
+    "def main():\n",
+    "    parser = argparse.ArgumentParser(description=\"calculate X to the power of Y\")\n",
+    "    parser.add_argument(\"-v\", \"--verbose\", action=\"store_true\")\n",
+    "    parser.add_argument(\"x\", type=int, help=\"the base\")\n",
+    "    parser.add_argument(\"y\", type=int, help=\"the exponent\")\n",
+    "    args = parser.parse_args()\n",
+    "    answer = args.x**args.y\n",
+    "\n",
+    "    if args.verbose:\n",
+    "        print(\"{} to the power {} equals {}\".format(args.x, args.y, answer))\n",
+    "    else:\n",
+    "        print(\"{}^{} == {}\".format(args.x, args.y, answer))\n",
+    "\n",
+    "if __name__ == '__main__':\n",
+    "    main()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%run test_argparse.py 3 8 -v"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%run test_argparse.py -h"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%run test_argparse.py 3 8.5 -q"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### [Gooey](https://github.com/chriskiehl/Gooey): GUI from command line tool"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%%writefile test_gooey.py\n",
+    "import argparse\n",
+    "from gooey import Gooey\n",
+    "\n",
+    "@Gooey\n",
+    "def main():\n",
+    "    parser = argparse.ArgumentParser(description=\"calculate X to the power of Y\")\n",
+    "    parser.add_argument(\"-v\", \"--verbose\", action=\"store_true\")\n",
+    "    parser.add_argument(\"x\", type=int, help=\"the base\")\n",
+    "    parser.add_argument(\"y\", type=int, help=\"the exponent\")\n",
+    "    args = parser.parse_args()\n",
+    "    answer = args.x**args.y\n",
+    "\n",
+    "    if args.verbose:\n",
+    "        print(\"{} to the power {} equals {}\".format(args.x, args.y, answer))\n",
+    "    else:\n",
+    "        print(\"{}^{} == {}\".format(args.x, args.y, answer))\n",
+    "\n",
+    "if __name__ == '__main__':\n",
+    "    main()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%run test_gooey.py"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!gcoord_gui"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## [Jinja2](http://jinja.pocoo.org/docs/2.10/): HTML generation"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%%writefile image_list.jinja2\n",
+    "<!DOCTYPE html>\n",
+    "<html lang=\"en\">\n",
+    "<head>\n",
+    "    {% block head %}\n",
+    "    <title>{{ title }}</title>\n",
+    "    {% endblock %}\n",
+    "</head>\n",
+    "<body>\n",
+    "    <div id=\"content\">\n",
+    "        {% block content %}\n",
+    "            {% for description, filenames in images %}\n",
+    "                <p>\n",
+    "                    {{ description }}\n",
+    "                </p>\n",
+    "                {% for filename in filenames %}\n",
+    "                    <a href=\"{{ filename }}\">\n",
+    "                        <img src=\"{{ filename }}\">\n",
+    "                    </a>\n",
+    "                {% endfor %}\n",
+    "            {% endfor %}\n",
+    "        {% endblock %}\n",
+    "    </div>\n",
+    "    <footer>\n",
+    "        Created on {{ time }}\n",
+    "    </footer>\n",
+    "</body>\n",
+    "</html>"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def plot_sine(amplitude, frequency):\n",
+    "    x = np.linspace(0, 2 * np.pi, 100)\n",
+    "    y = amplitude * np.sin(frequency * x)\n",
+    "    plt.plot(x, y)\n",
+    "    plt.xticks([0, np.pi, 2 * np.pi], ['0', '$\\pi$', '$2 \\pi$'])\n",
+    "    plt.ylim(-1.1, 1.1)\n",
+    "    filename = 'plots/A{:.2f}_F{:.2f}.png'.format(amplitude, frequency)\n",
+    "    plt.title('A={:.2f}, F={:.2f}'.format(amplitude, frequency))\n",
+    "    plt.savefig(filename)\n",
+    "    plt.close(plt.gcf())\n",
+    "    return filename\n",
+    "\n",
+    "!mkdir plots\n",
+    "amplitudes = [plot_sine(A, 1.) for A in [0.1, 0.3, 0.7, 1.0]]\n",
+    "frequencies = [plot_sine(1., F) for F in [1, 2, 3, 4, 5, 6]]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from jinja2 import Environment, FileSystemLoader\n",
+    "from datetime import datetime\n",
+    "loader = FileSystemLoader('.')\n",
+    "env = Environment(loader=loader)\n",
+    "template = env.get_template('image_list.jinja2')\n",
+    "\n",
+    "images = [\n",
+    "    ('Varying the amplitude', amplitudes),\n",
+    "    ('Varying the frequency', frequencies),\n",
+    "]\n",
+    "\n",
+    "with open('image_list.html', 'w') as f:\n",
+    "    f.write(template.render(title='Lots of sines',\n",
+    "                            images=images, time=datetime.now()))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!open image_list.html"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Neuroimage packages\n",
+    "The [nipy](http://nipy.org/) ecosystem covers most of these.\n",
+    "\n",
+    "### [CIFTI](https://github.com/MichielCottaar/cifti): easy creation/manipulation"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import nibabel\n",
+    "thickness = nibabel.load('100307/fsaverage_LR32k/100307.L.thickness.32k_fs_LR.shape.gii').darrays[0].data\n",
+    "thickness"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import cifti\n",
+    "ctx = thickness != 0\n",
+    "arr = np.random.rand(ctx.sum())\n",
+    "\n",
+    "bm_ctx = cifti.BrainModel.from_mask(ctx, name='CortexLeft')\n",
+    "sc = cifti.Scalar.from_names(['random'])\n",
+    "cifti.write('random_ctx.dscalar.nii', arr[None, :], (sc, bm_ctx))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!wb_view 100307/fsaverage_LR32k/100307.*.32k_fs_LR.surf.gii random_ctx.dscalar.nii"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "img = nibabel.load('100307/aparc+aseg.nii.gz')\n",
+    "cerebellum = img.get_data() == 8\n",
+    "\n",
+    "bm = bm_ctx + cifti.BrainModel.from_mask(cerebellum, name='CerebellumLeft', affine=img.affine)\n",
+    "sc = cifti.Scalar.from_names(['random'])\n",
+    "arr = np.random.rand(len(bm))\n",
+    "cifti.write('random_ctx_cerebellum.dscalar.nii', arr[None, :], (sc, bm))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!wb_view 100307/fsaverage_LR32k/100307.*.32k_fs_LR.surf.gii 100307/aparc+aseg.nii.gz random_ctx_cerebellum.dscalar.nii"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "arr = abs(thickness[ctx, None] - thickness[None, ctx])\n",
+    "cifti.write('diff_thickness.dconn.nii', arr, (bm_ctx, bm_ctx))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "!wb_view 100307/fsaverage_LR32k/100307.*.32k_fs_LR.surf.gii diff_thickness.dconn.nii"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## [networkx](https://networkx.github.io/): graph theory\n",
+    "\n",
+    "## GUI\n",
+    "- [tkinter](https://docs.python.org/3.6/library/tkinter.html): thin wrapper around Tcl/Tk; included in python\n",
+    "- [wxpython](https://www.wxpython.org/): Wrapper around the C++ wxWidgets library"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%%writefile wx_hello_world.py\n",
+    "#!/usr/bin/env python\n",
+    "\"\"\"\n",
+    "Hello World, but with more meat.\n",
+    "\"\"\"\n",
+    "\n",
+    "import wx\n",
+    "\n",
+    "class HelloFrame(wx.Frame):\n",
+    "    \"\"\"\n",
+    "    A Frame that says Hello World\n",
+    "    \"\"\"\n",
+    "\n",
+    "    def __init__(self, *args, **kw):\n",
+    "        # ensure the parent's __init__ is called\n",
+    "        super(HelloFrame, self).__init__(*args, **kw)\n",
+    "\n",
+    "        # create a panel in the frame\n",
+    "        pnl = wx.Panel(self)\n",
+    "\n",
+    "        # and put some text with a larger bold font on it\n",
+    "        st = wx.StaticText(pnl, label=\"Hello World!\", pos=(25,25))\n",
+    "        font = st.GetFont()\n",
+    "        font.PointSize += 10\n",
+    "        font = font.Bold()\n",
+    "        st.SetFont(font)\n",
+    "\n",
+    "        # create a menu bar\n",
+    "        self.makeMenuBar()\n",
+    "\n",
+    "        # and a status bar\n",
+    "        self.CreateStatusBar()\n",
+    "        self.SetStatusText(\"Welcome to wxPython!\")\n",
+    "\n",
+    "\n",
+    "    def makeMenuBar(self):\n",
+    "        \"\"\"\n",
+    "        A menu bar is composed of menus, which are composed of menu items.\n",
+    "        This method builds a set of menus and binds handlers to be called\n",
+    "        when the menu item is selected.\n",
+    "        \"\"\"\n",
+    "\n",
+    "        # Make a file menu with Hello and Exit items\n",
+    "        fileMenu = wx.Menu()\n",
+    "        # The \"\\t...\" syntax defines an accelerator key that also triggers\n",
+    "        # the same event\n",
+    "        helloItem = fileMenu.Append(-1, \"&Hello...\\tCtrl-H\",\n",
+    "                \"Help string shown in status bar for this menu item\")\n",
+    "        fileMenu.AppendSeparator()\n",
+    "        # When using a stock ID we don't need to specify the menu item's\n",
+    "        # label\n",
+    "        exitItem = fileMenu.Append(wx.ID_EXIT)\n",
+    "\n",
+    "        # Now a help menu for the about item\n",
+    "        helpMenu = wx.Menu()\n",
+    "        aboutItem = helpMenu.Append(wx.ID_ABOUT)\n",
+    "\n",
+    "        # Make the menu bar and add the two menus to it. The '&' defines\n",
+    "        # that the next letter is the \"mnemonic\" for the menu item. On the\n",
+    "        # platforms that support it those letters are underlined and can be\n",
+    "        # triggered from the keyboard.\n",
+    "        menuBar = wx.MenuBar()\n",
+    "        menuBar.Append(fileMenu, \"&File\")\n",
+    "        menuBar.Append(helpMenu, \"&Help\")\n",
+    "\n",
+    "        # Give the menu bar to the frame\n",
+    "        self.SetMenuBar(menuBar)\n",
+    "\n",
+    "        # Finally, associate a handler function with the EVT_MENU event for\n",
+    "        # each of the menu items. That means that when that menu item is\n",
+    "        # activated then the associated handler function will be called.\n",
+    "        self.Bind(wx.EVT_MENU, self.OnHello, helloItem)\n",
+    "        self.Bind(wx.EVT_MENU, self.OnExit,  exitItem)\n",
+    "        self.Bind(wx.EVT_MENU, self.OnAbout, aboutItem)\n",
+    "\n",
+    "\n",
+    "    def OnExit(self, event):\n",
+    "        \"\"\"Close the frame, terminating the application.\"\"\"\n",
+    "        self.Close(True)\n",
+    "\n",
+    "\n",
+    "    def OnHello(self, event):\n",
+    "        \"\"\"Say hello to the user.\"\"\"\n",
+    "        wx.MessageBox(\"Hello again from wxPython\")\n",
+    "\n",
+    "\n",
+    "    def OnAbout(self, event):\n",
+    "        \"\"\"Display an About Dialog\"\"\"\n",
+    "        wx.MessageBox(\"This is a wxPython Hello World sample\",\n",
+    "                      \"About Hello World 2\",\n",
+    "                      wx.OK|wx.ICON_INFORMATION)\n",
+    "\n",
+    "\n",
+    "if __name__ == '__main__':\n",
+    "    # When this module is run (not imported) then create the app, the\n",
+    "    # frame, show it, and start the event loop.\n",
+    "    app = wx.App()\n",
+    "    frm = HelloFrame(None, title='Hello World 2')\n",
+    "    frm.Show()\n",
+    "    app.MainLoop()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "%run wx_hello_world.py"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Machine learning\n",
+    "- scikit-learn\n",
+    "- theano/tensorflow/pytorch\n",
+    "  - keras\n",
+    "\n",
+    "## [Pycuda](https://documen.tician.de/pycuda/): Programming the GPU"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import pycuda.autoinit\n",
+    "import pycuda.driver as drv\n",
+    "\n",
+    "from pycuda.compiler import SourceModule\n",
+    "mod = SourceModule(\"\"\"\n",
+    "__global__ void multiply_them(double *dest, double *a, double *b)\n",
+    "{\n",
+    "  const int i = threadIdx.x;\n",
+    "  dest[i] = a[i] * b[i];\n",
+    "}\n",
+    "\"\"\")\n",
+    "\n",
+    "multiply_them = mod.get_function(\"multiply_them\")\n",
+    "\n",
+    "a = np.random.randn(400)\n",
+    "b = np.random.randn(400)\n",
+    "\n",
+    "dest = np.zeros_like(a)\n",
+    "multiply_them(\n",
+    "        drv.Out(dest), drv.In(a), drv.In(b),\n",
+    "        block=(400,1,1), grid=(1,1))\n",
+    "\n",
+    "print(dest-a*b)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Testing\n",
+    "- [unittest](https://docs.python.org/3.6/library/unittest.html): python built-in testing\n",
+    "> ```\n",
+    "> import unittest\n",
+    ">\n",
+    "> class TestStringMethods(unittest.TestCase):\n",
+    ">\n",
+    ">     def test_upper(self):\n",
+    ">         self.assertEqual('foo'.upper(), 'FOO')\n",
+    ">\n",
+    ">     def test_isupper(self):\n",
+    ">         self.assertTrue('FOO'.isupper())\n",
+    ">         self.assertFalse('Foo'.isupper())\n",
+    ">\n",
+    ">     def test_split(self):\n",
+    ">         s = 'hello world'\n",
+    ">         self.assertEqual(s.split(), ['hello', 'world'])\n",
+    ">         # check that s.split fails when the separator is not a string\n",
+    ">         with self.assertRaises(TypeError):\n",
+    ">             s.split(2)\n",
+    ">\n",
+    "> if __name__ == '__main__':\n",
+    ">     unittest.main()\n",
+    "> ```\n",
+    "- [doctest](https://docs.python.org/3.6/library/doctest.html): checks the example usage in the documentation\n",
+    "> ```\n",
+    "> def factorial(n):\n",
+    ">     \"\"\"Return the factorial of n, an exact integer >= 0.\n",
+    ">\n",
+    ">     >>> [factorial(n) for n in range(6)]\n",
+    ">     [1, 1, 2, 6, 24, 120]\n",
+    ">     >>> factorial(30)\n",
+    ">     265252859812191058636308480000000\n",
+    ">     >>> factorial(-1)\n",
+    ">     Traceback (most recent call last):\n",
+    ">         ...\n",
+    ">     ValueError: n must be >= 0\n",
+    ">\n",
+    ">     Factorials of floats are OK, but the float must be an exact integer:\n",
+    ">     >>> factorial(30.1)\n",
+    ">     Traceback (most recent call last):\n",
+    ">         ...\n",
+    ">     ValueError: n must be exact integer\n",
+    ">     >>> factorial(30.0)\n",
+    ">     265252859812191058636308480000000\n",
+    ">\n",
+    ">     It must also not be ridiculously large:\n",
+    ">     >>> factorial(1e100)\n",
+    ">     Traceback (most recent call last):\n",
+    ">         ...\n",
+    ">     OverflowError: n too large\n",
+    ">     \"\"\"\n",
+    ">\n",
+    ">     import math\n",
+    ">     if not n >= 0:\n",
+    ">         raise ValueError(\"n must be >= 0\")\n",
+    ">     if math.floor(n) != n:\n",
+    ">         raise ValueError(\"n must be exact integer\")\n",
+    ">     if n+1 == n:  # catch a value like 1e300\n",
+    ">         raise OverflowError(\"n too large\")\n",
+    ">     result = 1\n",
+    ">     factor = 2\n",
+    ">     while factor <= n:\n",
+    ">         result *= factor\n",
+    ">         factor += 1\n",
+    ">     return result\n",
+    ">\n",
+    ">\n",
+    "> if __name__ == \"__main__\":\n",
+    ">     import doctest\n",
+    ">     doctest.testmod()\n",
+    "> ```\n",
+    "Two external packages provide more convenient unit tests:\n",
+    "- [py.test](https://docs.pytest.org/en/latest/)\n",
+    "- [nose2](http://nose2.readthedocs.io/en/latest/usage.html)\n",
+    "> ```\n",
+    "> # content of test_sample.py\n",
+    "> def inc(x):\n",
+    ">     return x + 1\n",
+    ">\n",
+    "> def test_answer():\n",
+    ">     assert inc(3) == 5\n",
+    "> ```\n",
+    "\n",
+    "- [coverage](https://coverage.readthedocs.io/en/coverage-4.5.1/): measures which part of the code is covered by the tests\n",
+    "\n",
+    "## Linters\n",
+    "Linters check the code for any syntax errors, [style errors](https://www.python.org/dev/peps/pep-0008/), unused variables, unreachable code, etc.\n",
+    "- [pylint](https://pypi.python.org/pypi/pylint): most extensive linter\n",
+    "- [pyflake](https://pypi.python.org/pypi/pyflakes): if you think pylint is too strict\n",
+    "- [pep8](https://pypi.python.org/pypi/pep8): just checks for style errors\n",
+    "- [mypy](http://mypy-lang.org/): adding explicit typing to python"
+   ]
+  }
+ ],
+ "metadata": {},
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/talks/packages/packages.md b/talks/packages/packages.md
new file mode 100644
index 0000000..8da72e4
--- /dev/null
+++ b/talks/packages/packages.md
@@ -0,0 +1,579 @@
+# Main scientific python libraries
+See https://scipy.org/
+
+Most of these packages have or are in thr progress of dropping support for python2.
+So use python3!
+
+## [Numpy](http://www.numpy.org/): arrays
+This is the main library underlying (nearly) all of the scientific python ecosystem.
+See the tutorial in the beginner session or [the official numpy tutorial](https://docs.scipy.org/doc/numpy-dev/user/quickstart.html) for usage details.
+
+The usual nickname of numpy is np:
+```
+import numpy as np
+```
+
+Numpy includes support for:
+- N-dimensional arrays with various datatypes
+- basic functions (e.g., polynomials)
+- basic linear algebra
+- random number generation
+
+## [Scipy](https://scipy.org/scipylib/index.html): most general scientific tools
+At the top level this module includes all of the basic functionality from numpy.
+You could import this as, but you might as well import numpy directly.
+```
+import scipy as sp
+```
+
+The main strength in scipy lies in its sub-packages:
+```
+from scipy import optimize
+def costfunc(params):
+    return params[0] ** 2 * (params[1] - 3) ** 2 + (params[0] - 2) ** 2
+optimize.minimize(costfunc, x0=[0, 0], method='l-bfgs-b')
+```
+
+Tutorials for all sub-packages can be found [here](https://docs.scipy.org/doc/scipy-1.0.0/reference/).
+
+## [Matplotlib](https://matplotlib.org/): Main plotting library
+```
+import matplotlib as mpl
+mpl.use('nbagg')
+import matplotlib.pyplot as plt
+```
+The matplotlib tutorials are [here](https://matplotlib.org/tutorials/index.html)
+
+```
+x = np.linspace(0, 2, 100)
+
+plt.plot(x, x, label='linear')
+plt.plot(x, x**2, label='quadratic')
+plt.plot(x, x**3, label='cubic')
+
+plt.xlabel('x label')
+plt.ylabel('y label')
+
+plt.title("Simple Plot")
+
+plt.legend()
+
+plt.show()
+```
+
+Alternatives:
+- [Mayavi](http://docs.enthought.com/mayavi/mayavi/): 3D plotting (hard to install)
+- [Bokeh](https://bokeh.pydata.org/en/latest/) among many others: interactive plots in the browser (i.e., in javascript)
+
+## [Ipython](http://ipython.org/)/[Jupyter](https://jupyter.org/) notebook: interactive python environments
+There are many [useful extensions available](https://github.com/ipython-contrib/jupyter_contrib_nbextensions).
+
+## [Pandas](https://pandas.pydata.org/): Analyzing "clean" data
+Once your data is in tabular form (e.g. Biobank IDP's), you want to use pandas dataframes to analyze them.
+This brings most of the functionality of R into python.
+Pandas has excellent support for:
+- fast IO to many tabular formats
+- accurate handling of missing data
+- Many, many routines to handle data
+  - group by categorical data (i.e., male/female, or age groups)
+  - joining/merging data
+  - time series support
+- statistical models through [statsmodels](http://www.statsmodels.org/stable/index.html)
+- plotting though seaborn [seaborn](https://seaborn.pydata.org/)
+- Use [dask](https://dask.pydata.org/en/latest/) if your data is too big for memory (or if you want to run in parallel)
+
+You should also install `numexpr` and `bottleneck` for optimal performance.
+
+For the documentation check [here](http://pandas.pydata.org/pandas-docs/stable/index.html)
+
+### Adjusted example from statsmodels tutorial
+```
+import statsmodels.api as sm
+import statsmodels.formula.api as smf
+import numpy as np
+```
+
+```
+df = sm.datasets.get_rdataset("Guerry", "HistData").data
+df
+```
+
+```
+df.describe()
+```
+
+```
+df.groupby('Region').mean()
+```
+
+```
+results = smf.ols('Lottery ~ Literacy + np.log(Pop1831)', data=df).fit()
+results.summary()
+```
+
+```
+df['log_pop'] = np.log(df.Pop1831)
+df
+```
+
+```
+results = smf.ols('Lottery ~ Literacy + log_pop', data=df).fit()
+results.summary()
+```
+
+```
+results = smf.ols('Lottery ~ Literacy + np.log(Pop1831) + Region', data=df).fit()
+results.summary()
+```
+
+```
+results = smf.ols('Lottery ~ Literacy + np.log(Pop1831) + Region + Region * Literacy', data=df).fit()
+results.summary()
+```
+
+```
+%matplotlib nbagg
+import seaborn as sns
+sns.pairplot(df, hue="Region", vars=('Lottery', 'Literacy', 'log_pop'))
+```
+
+## [Sympy](http://www.sympy.org/en/index.html): Symbolic programming
+```
+import sympy as sym  # no standard nickname
+```
+
+```
+x, a, b, c = sym.symbols('x, a, b, c')
+sym.solve(a * x ** 2 + b * x + c, x)
+```
+
+```
+sym.integrate(x/(x**2+a*x+2), x)
+```
+
+```
+f = sym.utilities.lambdify((x, a), sym.integrate((x**2+a*x+2), x))
+f(np.random.rand(10), np.random.rand(10))
+```
+
+# Other topics
+## [Argparse](https://docs.python.org/3.6/howto/argparse.html): Command line arguments
+```
+%%writefile test_argparse.py
+import argparse
+
+def main():
+    parser = argparse.ArgumentParser(description="calculate X to the power of Y")
+    parser.add_argument("-v", "--verbose", action="store_true")
+    parser.add_argument("x", type=int, help="the base")
+    parser.add_argument("y", type=int, help="the exponent")
+    args = parser.parse_args()
+    answer = args.x**args.y
+
+    if args.verbose:
+        print("{} to the power {} equals {}".format(args.x, args.y, answer))
+    else:
+        print("{}^{} == {}".format(args.x, args.y, answer))
+
+if __name__ == '__main__':
+    main()
+```
+
+```
+%run test_argparse.py 3 8 -v
+```
+
+```
+%run test_argparse.py -h
+```
+
+```
+%run test_argparse.py 3 8.5 -q
+```
+
+### [Gooey](https://github.com/chriskiehl/Gooey): GUI from command line tool
+```
+%%writefile test_gooey.py
+import argparse
+from gooey import Gooey
+
+@Gooey
+def main():
+    parser = argparse.ArgumentParser(description="calculate X to the power of Y")
+    parser.add_argument("-v", "--verbose", action="store_true")
+    parser.add_argument("x", type=int, help="the base")
+    parser.add_argument("y", type=int, help="the exponent")
+    args = parser.parse_args()
+    answer = args.x**args.y
+
+    if args.verbose:
+        print("{} to the power {} equals {}".format(args.x, args.y, answer))
+    else:
+        print("{}^{} == {}".format(args.x, args.y, answer))
+
+if __name__ == '__main__':
+    main()
+```
+
+```
+%run test_gooey.py
+```
+
+```
+!gcoord_gui
+```
+
+## [Jinja2](http://jinja.pocoo.org/docs/2.10/): HTML generation
+```
+%%writefile image_list.jinja2
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    {% block head %}
+    <title>{{ title }}</title>
+    {% endblock %}
+</head>
+<body>
+    <div id="content">
+        {% block content %}
+            {% for description, filenames in images %}
+                <p>
+                    {{ description }}
+                </p>
+                {% for filename in filenames %}
+                    <a href="{{ filename }}">
+                        <img src="{{ filename }}">
+                    </a>
+                {% endfor %}
+            {% endfor %}
+        {% endblock %}
+    </div>
+    <footer>
+        Created on {{ time }}
+    </footer>
+</body>
+</html>
+```
+
+```
+def plot_sine(amplitude, frequency):
+    x = np.linspace(0, 2 * np.pi, 100)
+    y = amplitude * np.sin(frequency * x)
+    plt.plot(x, y)
+    plt.xticks([0, np.pi, 2 * np.pi], ['0', '$\pi$', '$2 \pi$'])
+    plt.ylim(-1.1, 1.1)
+    filename = 'plots/A{:.2f}_F{:.2f}.png'.format(amplitude, frequency)
+    plt.title('A={:.2f}, F={:.2f}'.format(amplitude, frequency))
+    plt.savefig(filename)
+    plt.close(plt.gcf())
+    return filename
+
+!mkdir plots
+amplitudes = [plot_sine(A, 1.) for A in [0.1, 0.3, 0.7, 1.0]]
+frequencies = [plot_sine(1., F) for F in [1, 2, 3, 4, 5, 6]]
+```
+
+```
+from jinja2 import Environment, FileSystemLoader
+from datetime import datetime
+loader = FileSystemLoader('.')
+env = Environment(loader=loader)
+template = env.get_template('image_list.jinja2')
+
+images = [
+    ('Varying the amplitude', amplitudes),
+    ('Varying the frequency', frequencies),
+]
+
+with open('image_list.html', 'w') as f:
+    f.write(template.render(title='Lots of sines',
+                            images=images, time=datetime.now()))
+```
+
+```
+!open image_list.html
+```
+
+## Neuroimage packages
+The [nipy](http://nipy.org/) ecosystem covers most of these.
+
+### [CIFTI](https://github.com/MichielCottaar/cifti): easy creation/manipulation
+```
+import nibabel
+thickness = nibabel.load('100307/fsaverage_LR32k/100307.L.thickness.32k_fs_LR.shape.gii').darrays[0].data
+thickness
+```
+
+```
+import cifti
+ctx = thickness != 0
+arr = np.random.rand(ctx.sum())
+
+bm_ctx = cifti.BrainModel.from_mask(ctx, name='CortexLeft')
+sc = cifti.Scalar.from_names(['random'])
+cifti.write('random_ctx.dscalar.nii', arr[None, :], (sc, bm_ctx))
+```
+
+```
+!wb_view 100307/fsaverage_LR32k/100307.*.32k_fs_LR.surf.gii random_ctx.dscalar.nii
+```
+
+```
+img = nibabel.load('100307/aparc+aseg.nii.gz')
+cerebellum = img.get_data() == 8
+
+bm = bm_ctx + cifti.BrainModel.from_mask(cerebellum, name='CerebellumLeft', affine=img.affine)
+sc = cifti.Scalar.from_names(['random'])
+arr = np.random.rand(len(bm))
+cifti.write('random_ctx_cerebellum.dscalar.nii', arr[None, :], (sc, bm))
+```
+
+```
+!wb_view 100307/fsaverage_LR32k/100307.*.32k_fs_LR.surf.gii 100307/aparc+aseg.nii.gz random_ctx_cerebellum.dscalar.nii
+```
+
+```
+arr = abs(thickness[ctx, None] - thickness[None, ctx])
+cifti.write('diff_thickness.dconn.nii', arr, (bm_ctx, bm_ctx))
+```
+
+```
+!wb_view 100307/fsaverage_LR32k/100307.*.32k_fs_LR.surf.gii diff_thickness.dconn.nii
+```
+
+## [networkx](https://networkx.github.io/): graph theory
+
+## GUI
+- [tkinter](https://docs.python.org/3.6/library/tkinter.html): thin wrapper around Tcl/Tk; included in python
+- [wxpython](https://www.wxpython.org/): Wrapper around the C++ wxWidgets library
+```
+%%writefile wx_hello_world.py
+#!/usr/bin/env python
+"""
+Hello World, but with more meat.
+"""
+
+import wx
+
+class HelloFrame(wx.Frame):
+    """
+    A Frame that says Hello World
+    """
+
+    def __init__(self, *args, **kw):
+        # ensure the parent's __init__ is called
+        super(HelloFrame, self).__init__(*args, **kw)
+
+        # create a panel in the frame
+        pnl = wx.Panel(self)
+
+        # and put some text with a larger bold font on it
+        st = wx.StaticText(pnl, label="Hello World!", pos=(25,25))
+        font = st.GetFont()
+        font.PointSize += 10
+        font = font.Bold()
+        st.SetFont(font)
+
+        # create a menu bar
+        self.makeMenuBar()
+
+        # and a status bar
+        self.CreateStatusBar()
+        self.SetStatusText("Welcome to wxPython!")
+
+
+    def makeMenuBar(self):
+        """
+        A menu bar is composed of menus, which are composed of menu items.
+        This method builds a set of menus and binds handlers to be called
+        when the menu item is selected.
+        """
+
+        # Make a file menu with Hello and Exit items
+        fileMenu = wx.Menu()
+        # The "\t..." syntax defines an accelerator key that also triggers
+        # the same event
+        helloItem = fileMenu.Append(-1, "&Hello...\tCtrl-H",
+                "Help string shown in status bar for this menu item")
+        fileMenu.AppendSeparator()
+        # When using a stock ID we don't need to specify the menu item's
+        # label
+        exitItem = fileMenu.Append(wx.ID_EXIT)
+
+        # Now a help menu for the about item
+        helpMenu = wx.Menu()
+        aboutItem = helpMenu.Append(wx.ID_ABOUT)
+
+        # Make the menu bar and add the two menus to it. The '&' defines
+        # that the next letter is the "mnemonic" for the menu item. On the
+        # platforms that support it those letters are underlined and can be
+        # triggered from the keyboard.
+        menuBar = wx.MenuBar()
+        menuBar.Append(fileMenu, "&File")
+        menuBar.Append(helpMenu, "&Help")
+
+        # Give the menu bar to the frame
+        self.SetMenuBar(menuBar)
+
+        # Finally, associate a handler function with the EVT_MENU event for
+        # each of the menu items. That means that when that menu item is
+        # activated then the associated handler function will be called.
+        self.Bind(wx.EVT_MENU, self.OnHello, helloItem)
+        self.Bind(wx.EVT_MENU, self.OnExit,  exitItem)
+        self.Bind(wx.EVT_MENU, self.OnAbout, aboutItem)
+
+
+    def OnExit(self, event):
+        """Close the frame, terminating the application."""
+        self.Close(True)
+
+
+    def OnHello(self, event):
+        """Say hello to the user."""
+        wx.MessageBox("Hello again from wxPython")
+
+
+    def OnAbout(self, event):
+        """Display an About Dialog"""
+        wx.MessageBox("This is a wxPython Hello World sample",
+                      "About Hello World 2",
+                      wx.OK|wx.ICON_INFORMATION)
+
+
+if __name__ == '__main__':
+    # When this module is run (not imported) then create the app, the
+    # frame, show it, and start the event loop.
+    app = wx.App()
+    frm = HelloFrame(None, title='Hello World 2')
+    frm.Show()
+    app.MainLoop()
+```
+
+```
+%run wx_hello_world.py
+```
+
+## Machine learning
+- scikit-learn
+- theano/tensorflow/pytorch
+  - keras
+
+## [Pycuda](https://documen.tician.de/pycuda/): Programming the GPU
+```
+import pycuda.autoinit
+import pycuda.driver as drv
+
+from pycuda.compiler import SourceModule
+mod = SourceModule("""
+__global__ void multiply_them(double *dest, double *a, double *b)
+{
+  const int i = threadIdx.x;
+  dest[i] = a[i] * b[i];
+}
+""")
+
+multiply_them = mod.get_function("multiply_them")
+
+a = np.random.randn(400)
+b = np.random.randn(400)
+
+dest = np.zeros_like(a)
+multiply_them(
+        drv.Out(dest), drv.In(a), drv.In(b),
+        block=(400,1,1), grid=(1,1))
+
+print(dest-a*b)
+```
+
+## Testing
+- [unittest](https://docs.python.org/3.6/library/unittest.html): python built-in testing
+> ```
+> import unittest
+>
+> class TestStringMethods(unittest.TestCase):
+>
+>     def test_upper(self):
+>         self.assertEqual('foo'.upper(), 'FOO')
+>
+>     def test_isupper(self):
+>         self.assertTrue('FOO'.isupper())
+>         self.assertFalse('Foo'.isupper())
+>
+>     def test_split(self):
+>         s = 'hello world'
+>         self.assertEqual(s.split(), ['hello', 'world'])
+>         # check that s.split fails when the separator is not a string
+>         with self.assertRaises(TypeError):
+>             s.split(2)
+>
+> if __name__ == '__main__':
+>     unittest.main()
+> ```
+- [doctest](https://docs.python.org/3.6/library/doctest.html): checks the example usage in the documentation
+> ```
+> def factorial(n):
+>     """Return the factorial of n, an exact integer >= 0.
+>
+>     >>> [factorial(n) for n in range(6)]
+>     [1, 1, 2, 6, 24, 120]
+>     >>> factorial(30)
+>     265252859812191058636308480000000
+>     >>> factorial(-1)
+>     Traceback (most recent call last):
+>         ...
+>     ValueError: n must be >= 0
+>
+>     Factorials of floats are OK, but the float must be an exact integer:
+>     >>> factorial(30.1)
+>     Traceback (most recent call last):
+>         ...
+>     ValueError: n must be exact integer
+>     >>> factorial(30.0)
+>     265252859812191058636308480000000
+>
+>     It must also not be ridiculously large:
+>     >>> factorial(1e100)
+>     Traceback (most recent call last):
+>         ...
+>     OverflowError: n too large
+>     """
+>
+>     import math
+>     if not n >= 0:
+>         raise ValueError("n must be >= 0")
+>     if math.floor(n) != n:
+>         raise ValueError("n must be exact integer")
+>     if n+1 == n:  # catch a value like 1e300
+>         raise OverflowError("n too large")
+>     result = 1
+>     factor = 2
+>     while factor <= n:
+>         result *= factor
+>         factor += 1
+>     return result
+>
+>
+> if __name__ == "__main__":
+>     import doctest
+>     doctest.testmod()
+> ```
+Two external packages provide more convenient unit tests:
+- [py.test](https://docs.pytest.org/en/latest/)
+- [nose2](http://nose2.readthedocs.io/en/latest/usage.html)
+> ```
+> # content of test_sample.py
+> def inc(x):
+>     return x + 1
+>
+> def test_answer():
+>     assert inc(3) == 5
+> ```
+
+- [coverage](https://coverage.readthedocs.io/en/coverage-4.5.1/): measures which part of the code is covered by the tests
+
+## Linters
+Linters check the code for any syntax errors, [style errors](https://www.python.org/dev/peps/pep-0008/), unused variables, unreachable code, etc.
+- [pylint](https://pypi.python.org/pypi/pylint): most extensive linter
+- [pyflake](https://pypi.python.org/pypi/pyflakes): if you think pylint is too strict
+- [pep8](https://pypi.python.org/pypi/pep8): just checks for style errors
+- [mypy](http://mypy-lang.org/): adding explicit typing to python
\ No newline at end of file
diff --git a/talks/speed/speed.ipynb b/talks/speed/speed.ipynb
new file mode 100644
index 0000000..c8c9cde
--- /dev/null
+++ b/talks/speed/speed.ipynb
@@ -0,0 +1,2060 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Numpy vectorizing"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 17,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "import numpy as np"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "((nan, nan), (-1.0, 4.0), (0.0, 0.0))"
+      ]
+     },
+     "execution_count": 18,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "def root(a, b, c):\n",
+    "    D = b ** 2 - 4 * a * c\n",
+    "    if D < 0:\n",
+    "        return np.nan, np.nan\n",
+    "    x1 = (-b + np.sqrt(D)) / (2 * a)\n",
+    "    x2 = (-b - np.sqrt(D)) / (2 * a)\n",
+    "    return x1, x2\n",
+    "root(1, 3, 4), root(-1, 3, 4), root(1, 0, 0)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 19,
+   "metadata": {},
+   "outputs": [
+    {
+     "ename": "ValueError",
+     "evalue": "The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
+      "\u001b[0;32m<ipython-input-19-5d1fed3ed2df>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0mb\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrandom\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrandn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1e6\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      3\u001b[0m \u001b[0mc\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrandom\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrandn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1e6\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0mroot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mc\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+      "\u001b[0;32m<ipython-input-18-54b500cd66b1>\u001b[0m in \u001b[0;36mroot\u001b[0;34m(a, b, c)\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mroot\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mb\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mc\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      2\u001b[0m     \u001b[0mD\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mb\u001b[0m \u001b[0;34m**\u001b[0m \u001b[0;36m2\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;36m4\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0ma\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mc\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m     \u001b[0;32mif\u001b[0m \u001b[0mD\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      4\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnan\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnan\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      5\u001b[0m     \u001b[0mx1\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0mb\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msqrt\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mD\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m2\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0ma\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;31mValueError\u001b[0m: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()"
+     ]
+    }
+   ],
+   "source": [
+    "a = np.random.randn(int(1e6))\n",
+    "b = np.random.randn(int(1e6))\n",
+    "c = np.random.randn(int(1e6))\n",
+    "root(a, b, c)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 20,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 3.64 s, sys: 65.9 ms, total: 3.71 s\n",
+      "Wall time: 3.71 s\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "array([[-0.55797188,  1.11121928],\n",
+       "       [        nan,         nan],\n",
+       "       [ 0.76166857, -1.70637551],\n",
+       "       ...,\n",
+       "       [-0.56859783,  0.98659831],\n",
+       "       [        nan,         nan],\n",
+       "       [-0.53531175,  0.18339357]])"
+      ]
+     },
+     "execution_count": 20,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "%%time\n",
+    "np.array([root(av, bv, cv) for av, bv, cv in zip(a, b, c)])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 21,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "root_vec = np.vectorize(root)\n",
+    "np.vectorize?"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 22,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 1.85 s, sys: 71.9 ms, total: 1.92 s\n",
+      "Wall time: 1.92 s\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "(array([-0.55797188,         nan,  0.76166857, ..., -0.56859783,\n",
+       "                nan, -0.53531175]),\n",
+       " array([ 1.11121928,         nan, -1.70637551, ...,  0.98659831,\n",
+       "                nan,  0.18339357]))"
+      ]
+     },
+     "execution_count": 22,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "%%time\n",
+    "root_vec(a, b, c)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 23,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 1.66 s, sys: 62.7 ms, total: 1.73 s\n",
+      "Wall time: 1.73 s\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "(array([-1.72246968, -2.63787563,         nan, ..., -2.20722406,\n",
+       "                nan, -1.84415698]),\n",
+       " array([2.27571708, 2.04353033,        nan, ..., 2.62522454,        nan,\n",
+       "        1.4922388 ]))"
+      ]
+     },
+     "execution_count": 23,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "%%time\n",
+    "root_vec(a, b, 7)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Numba"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 24,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "import numba"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 25,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "root_jit = numba.jit(root)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 26,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "3.08 µs ± 192 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)\n"
+     ]
+    }
+   ],
+   "source": [
+    "%timeit root(23, 78, 19.0)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 27,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "The slowest run took 11.09 times longer than the fastest. This could mean that an intermediate result is being cached.\n",
+      "786 ns ± 1.08 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
+     ]
+    }
+   ],
+   "source": [
+    "%timeit root_jit(23, 78, 19.0)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 28,
+   "metadata": {},
+   "outputs": [
+    {
+     "ename": "SystemError",
+     "evalue": "CPUDispatcher(<function root at 0x114035620>) returned a result with an error set",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
+      "\u001b[0;31mValueError\u001b[0m: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()",
+      "\nThe above exception was the direct cause of the following exception:\n",
+      "\u001b[0;31mSystemError\u001b[0m                               Traceback (most recent call last)",
+      "\u001b[0;32m<timed eval>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n",
+      "\u001b[0;31mSystemError\u001b[0m: CPUDispatcher(<function root at 0x114035620>) returned a result with an error set"
+     ]
+    }
+   ],
+   "source": [
+    "%%time\n",
+    "root_jit(a, b, 7)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 29,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 406 ms, sys: 57.2 ms, total: 464 ms\n",
+      "Wall time: 464 ms\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "(array([-1.72246968, -2.63787563,         nan, ..., -2.20722406,\n",
+       "                nan, -1.84415698]),\n",
+       " array([2.27571708, 2.04353033,        nan, ..., 2.62522454,        nan,\n",
+       "        1.4922388 ]))"
+      ]
+     },
+     "execution_count": 29,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "root_jit_vec = np.vectorize(root_jit)\n",
+    "%time root_jit_vec(a, b, 7)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 30,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 391 ms, sys: 58.2 ms, total: 449 ms\n",
+      "Wall time: 448 ms\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "(array([-1.72246968, -2.63787563,         nan, ..., -2.20722406,\n",
+       "                nan, -1.84415698]),\n",
+       " array([2.27571708, 2.04353033,        nan, ..., 2.62522454,        nan,\n",
+       "        1.4922388 ]))"
+      ]
+     },
+     "execution_count": 30,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "@np.vectorize\n",
+    "@numba.jit\n",
+    "def fast_root(a, b, c):\n",
+    "    D = b ** 2 - 4 * a * c\n",
+    "    if D < 0:\n",
+    "        return np.nan, np.nan\n",
+    "    x1 = (-b + np.sqrt(D)) / (2 * a)\n",
+    "    x2 = (-b - np.sqrt(D)) / (2 * a)\n",
+    "    return x1, x2\n",
+    "%time fast_root(a, b, 7)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 31,
+   "metadata": {},
+   "outputs": [
+    {
+     "ename": "NotImplementedError",
+     "evalue": "(float64 x 2) cannot be represented as a Numpy dtype",
+     "output_type": "error",
+     "traceback": [
+      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+      "\u001b[0;31mNotImplementedError\u001b[0m                       Traceback (most recent call last)",
+      "\u001b[0;32m<timed eval>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n",
+      "\u001b[0;32m~/miniconda3/lib/python3.6/site-packages/numba/npyufunc/dufunc.py\u001b[0m in \u001b[0;36m_compile_for_args\u001b[0;34m(self, *args, **kws)\u001b[0m\n\u001b[1;32m    166\u001b[0m                     \u001b[0margty\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0margty\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdtype\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    167\u001b[0m                 \u001b[0margtys\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margty\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 168\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_compile_for_argtys\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtuple\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margtys\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    169\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    170\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0m_compile_for_argtys\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0margtys\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mreturn_type\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m~/miniconda3/lib/python3.6/site-packages/numba/npyufunc/dufunc.py\u001b[0m in \u001b[0;36m_compile_for_argtys\u001b[0;34m(self, argtys, return_type)\u001b[0m\n\u001b[1;32m    188\u001b[0m             cres, argtys, return_type)\n\u001b[1;32m    189\u001b[0m         dtypenums, ptr, env = ufuncbuilder._build_element_wise_ufunc_wrapper(\n\u001b[0;32m--> 190\u001b[0;31m             cres, actual_sig)\n\u001b[0m\u001b[1;32m    191\u001b[0m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_add_loop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mutils\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlongint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mptr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdtypenums\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    192\u001b[0m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_keepalive\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mptr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcres\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mlibrary\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0menv\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m~/miniconda3/lib/python3.6/site-packages/numba/npyufunc/ufuncbuilder.py\u001b[0m in \u001b[0;36m_build_element_wise_ufunc_wrapper\u001b[0;34m(cres, signature)\u001b[0m\n\u001b[1;32m    163\u001b[0m     \u001b[0;31m# Get dtypes\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    164\u001b[0m     \u001b[0mdtypenums\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mas_dtype\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnum\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0ma\u001b[0m \u001b[0;32min\u001b[0m \u001b[0msignature\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 165\u001b[0;31m     \u001b[0mdtypenums\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mappend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mas_dtype\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msignature\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreturn_type\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnum\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    166\u001b[0m     \u001b[0;32mreturn\u001b[0m \u001b[0mdtypenums\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mptr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0menv\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    167\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;32m~/miniconda3/lib/python3.6/site-packages/numba/numpy_support.py\u001b[0m in \u001b[0;36mas_dtype\u001b[0;34m(nbtype)\u001b[0m\n\u001b[1;32m    134\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mas_dtype\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnbtype\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdtype\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    135\u001b[0m     raise NotImplementedError(\"%r cannot be represented as a Numpy dtype\"\n\u001b[0;32m--> 136\u001b[0;31m                               % (nbtype,))\n\u001b[0m\u001b[1;32m    137\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    138\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;31mNotImplementedError\u001b[0m: (float64 x 2) cannot be represented as a Numpy dtype"
+     ]
+    }
+   ],
+   "source": [
+    "@numba.vectorize\n",
+    "def fast_root(a, b, c):\n",
+    "    D = b ** 2 - 4 * a * c\n",
+    "    if D < 0:\n",
+    "        return np.nan, np.nan\n",
+    "    x1 = (-b + np.sqrt(D)) / (2 * a)\n",
+    "    x2 = (-b - np.sqrt(D)) / (2 * a)\n",
+    "    return x1, x2\n",
+    "%time fast_root(a, b, 7)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# But run the profiler first"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 32,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 1.56 s, sys: 3.5 ms, total: 1.56 s\n",
+      "Wall time: 1.56 s\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "5702887"
+      ]
+     },
+     "execution_count": 32,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "def fib(n):\n",
+    "    if n <= 1:\n",
+    "        return 1\n",
+    "    else:\n",
+    "        return fib(n-2) + fib(n-1)\n",
+    "%time fib(33)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 35,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 63.4 ms, sys: 1.79 ms, total: 65.2 ms\n",
+      "Wall time: 64.2 ms\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "5702887"
+      ]
+     },
+     "execution_count": 35,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "@numba.jit\n",
+    "def fib_jit(n):\n",
+    "    if n <= 1:\n",
+    "        return 1\n",
+    "    else:\n",
+    "        return fib_jit(n-2) + fib_jit(n-1)\n",
+    "%time fib_jit(33)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 34,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 1.28 s, sys: 5.26 ms, total: 1.28 s\n",
+      "Wall time: 1.28 s\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "267914296"
+      ]
+     },
+     "execution_count": 34,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "%time fib_jit(41)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 103,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      " "
+     ]
+    }
+   ],
+   "source": [
+    "%prun fib(33)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 104,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      " "
+     ]
+    }
+   ],
+   "source": [
+    "%prun fib_jit(41)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 112,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 20 µs, sys: 1 µs, total: 21 µs\n",
+      "Wall time: 24.1 µs\n"
+     ]
+    }
+   ],
+   "source": [
+    "from functools import lru_cache\n",
+    "@lru_cache(None)\n",
+    "def fib_cache(n):\n",
+    "    if n <= 1:\n",
+    "        return 1\n",
+    "    else:\n",
+    "        return fib_cache(n-2) + fib_cache(n-1)\n",
+    "%time fib_cache(33)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "lru_cache?"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 111,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 261 µs, sys: 1e+03 ns, total: 262 µs\n",
+      "Wall time: 267 µs\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "225591516161936330872512695036072072046011324913758190588638866418474627738686883405015987052796968498626"
+      ]
+     },
+     "execution_count": 111,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "%time fib_cache(500)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Optimizing numpy algebra"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Multiplying/adding arrays incurs a lot of overhead, because lots of intermediate arrays get created and discarded again"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 48,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "139 ms ± 1.16 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
+     ]
+    }
+   ],
+   "source": [
+    "a = np.random.randn(int(1e7))\n",
+    "b = np.random.randn(int(1e7))\n",
+    "%timeit a * b + 4.1 * a < 3 * b"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 49,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "11.4 ms ± 484 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
+     ]
+    }
+   ],
+   "source": [
+    "import numexpr as ne\n",
+    "%timeit ne.evaluate('a * b + 4.1 * a < 3 * b')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Efficiency gain reduces once the operations become more complex"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 54,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "141 ms ± 1 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n"
+     ]
+    }
+   ],
+   "source": [
+    "%timeit a * np.sqrt(abs(b)) < 3 * b"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 55,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "12.6 ms ± 986 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
+     ]
+    }
+   ],
+   "source": [
+    "%timeit ne.evaluate('a * sqrt(abs(b)) < 3 * b')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Cython"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "http://docs.cython.org/en/latest/\n",
+    "\n",
+    "Compiles most python programs to C code for extra speed (but less introspection). Also allows direct calling of C library code.\n",
+    "\n",
+    "Can include python classes and much more."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 69,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "The cython extension is already loaded. To reload it, use:\n",
+      "  %reload_ext cython\n"
+     ]
+    }
+   ],
+   "source": [
+    "%load_ext cython"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 70,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]\n",
+      "CPU times: user 67.4 ms, sys: 1.22 ms, total: 68.6 ms\n",
+      "Wall time: 68 ms\n"
+     ]
+    }
+   ],
+   "source": [
+    "def primes(kmax):\n",
+    "    p = {}\n",
+    "    result = []\n",
+    "    if kmax > 1000:\n",
+    "        kmax = 1000\n",
+    "    k = 0\n",
+    "    n = 2\n",
+    "    while k < kmax:\n",
+    "        i = 0\n",
+    "        while i < k and n % p[i] != 0:\n",
+    "            i = i + 1\n",
+    "        if i == k:\n",
+    "            p[k] = n\n",
+    "            k = k + 1\n",
+    "            result.append(n)\n",
+    "        n = n + 1\n",
+    "    return result"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 91,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<!DOCTYPE html>\n",
+       "<!-- Generated by Cython 0.26 -->\n",
+       "<html>\n",
+       "<head>\n",
+       "    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n",
+       "    <title>Cython: _cython_magic_52e640a266384bf0478136fe7ab4a75d.pyx</title>\n",
+       "    <style type=\"text/css\">\n",
+       "    \n",
+       "body.cython { font-family: courier; font-size: 12; }\n",
+       "\n",
+       ".cython.tag  {  }\n",
+       ".cython.line { margin: 0em }\n",
+       ".cython.code { font-size: 9; color: #444444; display: none; margin: 0px 0px 0px 8px; border-left: 8px none; }\n",
+       "\n",
+       ".cython.line .run { background-color: #B0FFB0; }\n",
+       ".cython.line .mis { background-color: #FFB0B0; }\n",
+       ".cython.code.run  { border-left: 8px solid #B0FFB0; }\n",
+       ".cython.code.mis  { border-left: 8px solid #FFB0B0; }\n",
+       "\n",
+       ".cython.code .py_c_api  { color: red; }\n",
+       ".cython.code .py_macro_api  { color: #FF7000; }\n",
+       ".cython.code .pyx_c_api  { color: #FF3000; }\n",
+       ".cython.code .pyx_macro_api  { color: #FF7000; }\n",
+       ".cython.code .refnanny  { color: #FFA000; }\n",
+       ".cython.code .trace  { color: #FFA000; }\n",
+       ".cython.code .error_goto  { color: #FFA000; }\n",
+       "\n",
+       ".cython.code .coerce  { color: #008000; border: 1px dotted #008000 }\n",
+       ".cython.code .py_attr { color: #FF0000; font-weight: bold; }\n",
+       ".cython.code .c_attr  { color: #0000FF; }\n",
+       ".cython.code .py_call { color: #FF0000; font-weight: bold; }\n",
+       ".cython.code .c_call  { color: #0000FF; }\n",
+       "\n",
+       ".cython.score-0 {background-color: #FFFFff;}\n",
+       ".cython.score-1 {background-color: #FFFFe7;}\n",
+       ".cython.score-2 {background-color: #FFFFd4;}\n",
+       ".cython.score-3 {background-color: #FFFFc4;}\n",
+       ".cython.score-4 {background-color: #FFFFb6;}\n",
+       ".cython.score-5 {background-color: #FFFFaa;}\n",
+       ".cython.score-6 {background-color: #FFFF9f;}\n",
+       ".cython.score-7 {background-color: #FFFF96;}\n",
+       ".cython.score-8 {background-color: #FFFF8d;}\n",
+       ".cython.score-9 {background-color: #FFFF86;}\n",
+       ".cython.score-10 {background-color: #FFFF7f;}\n",
+       ".cython.score-11 {background-color: #FFFF79;}\n",
+       ".cython.score-12 {background-color: #FFFF73;}\n",
+       ".cython.score-13 {background-color: #FFFF6e;}\n",
+       ".cython.score-14 {background-color: #FFFF6a;}\n",
+       ".cython.score-15 {background-color: #FFFF66;}\n",
+       ".cython.score-16 {background-color: #FFFF62;}\n",
+       ".cython.score-17 {background-color: #FFFF5e;}\n",
+       ".cython.score-18 {background-color: #FFFF5b;}\n",
+       ".cython.score-19 {background-color: #FFFF57;}\n",
+       ".cython.score-20 {background-color: #FFFF55;}\n",
+       ".cython.score-21 {background-color: #FFFF52;}\n",
+       ".cython.score-22 {background-color: #FFFF4f;}\n",
+       ".cython.score-23 {background-color: #FFFF4d;}\n",
+       ".cython.score-24 {background-color: #FFFF4b;}\n",
+       ".cython.score-25 {background-color: #FFFF48;}\n",
+       ".cython.score-26 {background-color: #FFFF46;}\n",
+       ".cython.score-27 {background-color: #FFFF44;}\n",
+       ".cython.score-28 {background-color: #FFFF43;}\n",
+       ".cython.score-29 {background-color: #FFFF41;}\n",
+       ".cython.score-30 {background-color: #FFFF3f;}\n",
+       ".cython.score-31 {background-color: #FFFF3e;}\n",
+       ".cython.score-32 {background-color: #FFFF3c;}\n",
+       ".cython.score-33 {background-color: #FFFF3b;}\n",
+       ".cython.score-34 {background-color: #FFFF39;}\n",
+       ".cython.score-35 {background-color: #FFFF38;}\n",
+       ".cython.score-36 {background-color: #FFFF37;}\n",
+       ".cython.score-37 {background-color: #FFFF36;}\n",
+       ".cython.score-38 {background-color: #FFFF35;}\n",
+       ".cython.score-39 {background-color: #FFFF34;}\n",
+       ".cython.score-40 {background-color: #FFFF33;}\n",
+       ".cython.score-41 {background-color: #FFFF32;}\n",
+       ".cython.score-42 {background-color: #FFFF31;}\n",
+       ".cython.score-43 {background-color: #FFFF30;}\n",
+       ".cython.score-44 {background-color: #FFFF2f;}\n",
+       ".cython.score-45 {background-color: #FFFF2e;}\n",
+       ".cython.score-46 {background-color: #FFFF2d;}\n",
+       ".cython.score-47 {background-color: #FFFF2c;}\n",
+       ".cython.score-48 {background-color: #FFFF2b;}\n",
+       ".cython.score-49 {background-color: #FFFF2b;}\n",
+       ".cython.score-50 {background-color: #FFFF2a;}\n",
+       ".cython.score-51 {background-color: #FFFF29;}\n",
+       ".cython.score-52 {background-color: #FFFF29;}\n",
+       ".cython.score-53 {background-color: #FFFF28;}\n",
+       ".cython.score-54 {background-color: #FFFF27;}\n",
+       ".cython.score-55 {background-color: #FFFF27;}\n",
+       ".cython.score-56 {background-color: #FFFF26;}\n",
+       ".cython.score-57 {background-color: #FFFF26;}\n",
+       ".cython.score-58 {background-color: #FFFF25;}\n",
+       ".cython.score-59 {background-color: #FFFF24;}\n",
+       ".cython.score-60 {background-color: #FFFF24;}\n",
+       ".cython.score-61 {background-color: #FFFF23;}\n",
+       ".cython.score-62 {background-color: #FFFF23;}\n",
+       ".cython.score-63 {background-color: #FFFF22;}\n",
+       ".cython.score-64 {background-color: #FFFF22;}\n",
+       ".cython.score-65 {background-color: #FFFF22;}\n",
+       ".cython.score-66 {background-color: #FFFF21;}\n",
+       ".cython.score-67 {background-color: #FFFF21;}\n",
+       ".cython.score-68 {background-color: #FFFF20;}\n",
+       ".cython.score-69 {background-color: #FFFF20;}\n",
+       ".cython.score-70 {background-color: #FFFF1f;}\n",
+       ".cython.score-71 {background-color: #FFFF1f;}\n",
+       ".cython.score-72 {background-color: #FFFF1f;}\n",
+       ".cython.score-73 {background-color: #FFFF1e;}\n",
+       ".cython.score-74 {background-color: #FFFF1e;}\n",
+       ".cython.score-75 {background-color: #FFFF1e;}\n",
+       ".cython.score-76 {background-color: #FFFF1d;}\n",
+       ".cython.score-77 {background-color: #FFFF1d;}\n",
+       ".cython.score-78 {background-color: #FFFF1c;}\n",
+       ".cython.score-79 {background-color: #FFFF1c;}\n",
+       ".cython.score-80 {background-color: #FFFF1c;}\n",
+       ".cython.score-81 {background-color: #FFFF1c;}\n",
+       ".cython.score-82 {background-color: #FFFF1b;}\n",
+       ".cython.score-83 {background-color: #FFFF1b;}\n",
+       ".cython.score-84 {background-color: #FFFF1b;}\n",
+       ".cython.score-85 {background-color: #FFFF1a;}\n",
+       ".cython.score-86 {background-color: #FFFF1a;}\n",
+       ".cython.score-87 {background-color: #FFFF1a;}\n",
+       ".cython.score-88 {background-color: #FFFF1a;}\n",
+       ".cython.score-89 {background-color: #FFFF19;}\n",
+       ".cython.score-90 {background-color: #FFFF19;}\n",
+       ".cython.score-91 {background-color: #FFFF19;}\n",
+       ".cython.score-92 {background-color: #FFFF19;}\n",
+       ".cython.score-93 {background-color: #FFFF18;}\n",
+       ".cython.score-94 {background-color: #FFFF18;}\n",
+       ".cython.score-95 {background-color: #FFFF18;}\n",
+       ".cython.score-96 {background-color: #FFFF18;}\n",
+       ".cython.score-97 {background-color: #FFFF17;}\n",
+       ".cython.score-98 {background-color: #FFFF17;}\n",
+       ".cython.score-99 {background-color: #FFFF17;}\n",
+       ".cython.score-100 {background-color: #FFFF17;}\n",
+       ".cython.score-101 {background-color: #FFFF16;}\n",
+       ".cython.score-102 {background-color: #FFFF16;}\n",
+       ".cython.score-103 {background-color: #FFFF16;}\n",
+       ".cython.score-104 {background-color: #FFFF16;}\n",
+       ".cython.score-105 {background-color: #FFFF16;}\n",
+       ".cython.score-106 {background-color: #FFFF15;}\n",
+       ".cython.score-107 {background-color: #FFFF15;}\n",
+       ".cython.score-108 {background-color: #FFFF15;}\n",
+       ".cython.score-109 {background-color: #FFFF15;}\n",
+       ".cython.score-110 {background-color: #FFFF15;}\n",
+       ".cython.score-111 {background-color: #FFFF15;}\n",
+       ".cython.score-112 {background-color: #FFFF14;}\n",
+       ".cython.score-113 {background-color: #FFFF14;}\n",
+       ".cython.score-114 {background-color: #FFFF14;}\n",
+       ".cython.score-115 {background-color: #FFFF14;}\n",
+       ".cython.score-116 {background-color: #FFFF14;}\n",
+       ".cython.score-117 {background-color: #FFFF14;}\n",
+       ".cython.score-118 {background-color: #FFFF13;}\n",
+       ".cython.score-119 {background-color: #FFFF13;}\n",
+       ".cython.score-120 {background-color: #FFFF13;}\n",
+       ".cython.score-121 {background-color: #FFFF13;}\n",
+       ".cython.score-122 {background-color: #FFFF13;}\n",
+       ".cython.score-123 {background-color: #FFFF13;}\n",
+       ".cython.score-124 {background-color: #FFFF13;}\n",
+       ".cython.score-125 {background-color: #FFFF12;}\n",
+       ".cython.score-126 {background-color: #FFFF12;}\n",
+       ".cython.score-127 {background-color: #FFFF12;}\n",
+       ".cython.score-128 {background-color: #FFFF12;}\n",
+       ".cython.score-129 {background-color: #FFFF12;}\n",
+       ".cython.score-130 {background-color: #FFFF12;}\n",
+       ".cython.score-131 {background-color: #FFFF12;}\n",
+       ".cython.score-132 {background-color: #FFFF11;}\n",
+       ".cython.score-133 {background-color: #FFFF11;}\n",
+       ".cython.score-134 {background-color: #FFFF11;}\n",
+       ".cython.score-135 {background-color: #FFFF11;}\n",
+       ".cython.score-136 {background-color: #FFFF11;}\n",
+       ".cython.score-137 {background-color: #FFFF11;}\n",
+       ".cython.score-138 {background-color: #FFFF11;}\n",
+       ".cython.score-139 {background-color: #FFFF11;}\n",
+       ".cython.score-140 {background-color: #FFFF11;}\n",
+       ".cython.score-141 {background-color: #FFFF10;}\n",
+       ".cython.score-142 {background-color: #FFFF10;}\n",
+       ".cython.score-143 {background-color: #FFFF10;}\n",
+       ".cython.score-144 {background-color: #FFFF10;}\n",
+       ".cython.score-145 {background-color: #FFFF10;}\n",
+       ".cython.score-146 {background-color: #FFFF10;}\n",
+       ".cython.score-147 {background-color: #FFFF10;}\n",
+       ".cython.score-148 {background-color: #FFFF10;}\n",
+       ".cython.score-149 {background-color: #FFFF10;}\n",
+       ".cython.score-150 {background-color: #FFFF0f;}\n",
+       ".cython.score-151 {background-color: #FFFF0f;}\n",
+       ".cython.score-152 {background-color: #FFFF0f;}\n",
+       ".cython.score-153 {background-color: #FFFF0f;}\n",
+       ".cython.score-154 {background-color: #FFFF0f;}\n",
+       ".cython.score-155 {background-color: #FFFF0f;}\n",
+       ".cython.score-156 {background-color: #FFFF0f;}\n",
+       ".cython.score-157 {background-color: #FFFF0f;}\n",
+       ".cython.score-158 {background-color: #FFFF0f;}\n",
+       ".cython.score-159 {background-color: #FFFF0f;}\n",
+       ".cython.score-160 {background-color: #FFFF0f;}\n",
+       ".cython.score-161 {background-color: #FFFF0e;}\n",
+       ".cython.score-162 {background-color: #FFFF0e;}\n",
+       ".cython.score-163 {background-color: #FFFF0e;}\n",
+       ".cython.score-164 {background-color: #FFFF0e;}\n",
+       ".cython.score-165 {background-color: #FFFF0e;}\n",
+       ".cython.score-166 {background-color: #FFFF0e;}\n",
+       ".cython.score-167 {background-color: #FFFF0e;}\n",
+       ".cython.score-168 {background-color: #FFFF0e;}\n",
+       ".cython.score-169 {background-color: #FFFF0e;}\n",
+       ".cython.score-170 {background-color: #FFFF0e;}\n",
+       ".cython.score-171 {background-color: #FFFF0e;}\n",
+       ".cython.score-172 {background-color: #FFFF0e;}\n",
+       ".cython.score-173 {background-color: #FFFF0d;}\n",
+       ".cython.score-174 {background-color: #FFFF0d;}\n",
+       ".cython.score-175 {background-color: #FFFF0d;}\n",
+       ".cython.score-176 {background-color: #FFFF0d;}\n",
+       ".cython.score-177 {background-color: #FFFF0d;}\n",
+       ".cython.score-178 {background-color: #FFFF0d;}\n",
+       ".cython.score-179 {background-color: #FFFF0d;}\n",
+       ".cython.score-180 {background-color: #FFFF0d;}\n",
+       ".cython.score-181 {background-color: #FFFF0d;}\n",
+       ".cython.score-182 {background-color: #FFFF0d;}\n",
+       ".cython.score-183 {background-color: #FFFF0d;}\n",
+       ".cython.score-184 {background-color: #FFFF0d;}\n",
+       ".cython.score-185 {background-color: #FFFF0d;}\n",
+       ".cython.score-186 {background-color: #FFFF0d;}\n",
+       ".cython.score-187 {background-color: #FFFF0c;}\n",
+       ".cython.score-188 {background-color: #FFFF0c;}\n",
+       ".cython.score-189 {background-color: #FFFF0c;}\n",
+       ".cython.score-190 {background-color: #FFFF0c;}\n",
+       ".cython.score-191 {background-color: #FFFF0c;}\n",
+       ".cython.score-192 {background-color: #FFFF0c;}\n",
+       ".cython.score-193 {background-color: #FFFF0c;}\n",
+       ".cython.score-194 {background-color: #FFFF0c;}\n",
+       ".cython.score-195 {background-color: #FFFF0c;}\n",
+       ".cython.score-196 {background-color: #FFFF0c;}\n",
+       ".cython.score-197 {background-color: #FFFF0c;}\n",
+       ".cython.score-198 {background-color: #FFFF0c;}\n",
+       ".cython.score-199 {background-color: #FFFF0c;}\n",
+       ".cython.score-200 {background-color: #FFFF0c;}\n",
+       ".cython.score-201 {background-color: #FFFF0c;}\n",
+       ".cython.score-202 {background-color: #FFFF0c;}\n",
+       ".cython.score-203 {background-color: #FFFF0b;}\n",
+       ".cython.score-204 {background-color: #FFFF0b;}\n",
+       ".cython.score-205 {background-color: #FFFF0b;}\n",
+       ".cython.score-206 {background-color: #FFFF0b;}\n",
+       ".cython.score-207 {background-color: #FFFF0b;}\n",
+       ".cython.score-208 {background-color: #FFFF0b;}\n",
+       ".cython.score-209 {background-color: #FFFF0b;}\n",
+       ".cython.score-210 {background-color: #FFFF0b;}\n",
+       ".cython.score-211 {background-color: #FFFF0b;}\n",
+       ".cython.score-212 {background-color: #FFFF0b;}\n",
+       ".cython.score-213 {background-color: #FFFF0b;}\n",
+       ".cython.score-214 {background-color: #FFFF0b;}\n",
+       ".cython.score-215 {background-color: #FFFF0b;}\n",
+       ".cython.score-216 {background-color: #FFFF0b;}\n",
+       ".cython.score-217 {background-color: #FFFF0b;}\n",
+       ".cython.score-218 {background-color: #FFFF0b;}\n",
+       ".cython.score-219 {background-color: #FFFF0b;}\n",
+       ".cython.score-220 {background-color: #FFFF0b;}\n",
+       ".cython.score-221 {background-color: #FFFF0b;}\n",
+       ".cython.score-222 {background-color: #FFFF0a;}\n",
+       ".cython.score-223 {background-color: #FFFF0a;}\n",
+       ".cython.score-224 {background-color: #FFFF0a;}\n",
+       ".cython.score-225 {background-color: #FFFF0a;}\n",
+       ".cython.score-226 {background-color: #FFFF0a;}\n",
+       ".cython.score-227 {background-color: #FFFF0a;}\n",
+       ".cython.score-228 {background-color: #FFFF0a;}\n",
+       ".cython.score-229 {background-color: #FFFF0a;}\n",
+       ".cython.score-230 {background-color: #FFFF0a;}\n",
+       ".cython.score-231 {background-color: #FFFF0a;}\n",
+       ".cython.score-232 {background-color: #FFFF0a;}\n",
+       ".cython.score-233 {background-color: #FFFF0a;}\n",
+       ".cython.score-234 {background-color: #FFFF0a;}\n",
+       ".cython.score-235 {background-color: #FFFF0a;}\n",
+       ".cython.score-236 {background-color: #FFFF0a;}\n",
+       ".cython.score-237 {background-color: #FFFF0a;}\n",
+       ".cython.score-238 {background-color: #FFFF0a;}\n",
+       ".cython.score-239 {background-color: #FFFF0a;}\n",
+       ".cython.score-240 {background-color: #FFFF0a;}\n",
+       ".cython.score-241 {background-color: #FFFF0a;}\n",
+       ".cython.score-242 {background-color: #FFFF0a;}\n",
+       ".cython.score-243 {background-color: #FFFF0a;}\n",
+       ".cython.score-244 {background-color: #FFFF0a;}\n",
+       ".cython.score-245 {background-color: #FFFF0a;}\n",
+       ".cython.score-246 {background-color: #FFFF09;}\n",
+       ".cython.score-247 {background-color: #FFFF09;}\n",
+       ".cython.score-248 {background-color: #FFFF09;}\n",
+       ".cython.score-249 {background-color: #FFFF09;}\n",
+       ".cython.score-250 {background-color: #FFFF09;}\n",
+       ".cython.score-251 {background-color: #FFFF09;}\n",
+       ".cython.score-252 {background-color: #FFFF09;}\n",
+       ".cython.score-253 {background-color: #FFFF09;}\n",
+       ".cython.score-254 {background-color: #FFFF09;}\n",
+       ".cython .hll { background-color: #ffffcc }\n",
+       ".cython  { background: #f8f8f8; }\n",
+       ".cython .c { color: #408080; font-style: italic } /* Comment */\n",
+       ".cython .err { border: 1px solid #FF0000 } /* Error */\n",
+       ".cython .k { color: #008000; font-weight: bold } /* Keyword */\n",
+       ".cython .o { color: #666666 } /* Operator */\n",
+       ".cython .ch { color: #408080; font-style: italic } /* Comment.Hashbang */\n",
+       ".cython .cm { color: #408080; font-style: italic } /* Comment.Multiline */\n",
+       ".cython .cp { color: #BC7A00 } /* Comment.Preproc */\n",
+       ".cython .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */\n",
+       ".cython .c1 { color: #408080; font-style: italic } /* Comment.Single */\n",
+       ".cython .cs { color: #408080; font-style: italic } /* Comment.Special */\n",
+       ".cython .gd { color: #A00000 } /* Generic.Deleted */\n",
+       ".cython .ge { font-style: italic } /* Generic.Emph */\n",
+       ".cython .gr { color: #FF0000 } /* Generic.Error */\n",
+       ".cython .gh { color: #000080; font-weight: bold } /* Generic.Heading */\n",
+       ".cython .gi { color: #00A000 } /* Generic.Inserted */\n",
+       ".cython .go { color: #888888 } /* Generic.Output */\n",
+       ".cython .gp { color: #000080; font-weight: bold } /* Generic.Prompt */\n",
+       ".cython .gs { font-weight: bold } /* Generic.Strong */\n",
+       ".cython .gu { color: #800080; font-weight: bold } /* Generic.Subheading */\n",
+       ".cython .gt { color: #0044DD } /* Generic.Traceback */\n",
+       ".cython .kc { color: #008000; font-weight: bold } /* Keyword.Constant */\n",
+       ".cython .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */\n",
+       ".cython .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */\n",
+       ".cython .kp { color: #008000 } /* Keyword.Pseudo */\n",
+       ".cython .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */\n",
+       ".cython .kt { color: #B00040 } /* Keyword.Type */\n",
+       ".cython .m { color: #666666 } /* Literal.Number */\n",
+       ".cython .s { color: #BA2121 } /* Literal.String */\n",
+       ".cython .na { color: #7D9029 } /* Name.Attribute */\n",
+       ".cython .nb { color: #008000 } /* Name.Builtin */\n",
+       ".cython .nc { color: #0000FF; font-weight: bold } /* Name.Class */\n",
+       ".cython .no { color: #880000 } /* Name.Constant */\n",
+       ".cython .nd { color: #AA22FF } /* Name.Decorator */\n",
+       ".cython .ni { color: #999999; font-weight: bold } /* Name.Entity */\n",
+       ".cython .ne { color: #D2413A; font-weight: bold } /* Name.Exception */\n",
+       ".cython .nf { color: #0000FF } /* Name.Function */\n",
+       ".cython .nl { color: #A0A000 } /* Name.Label */\n",
+       ".cython .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */\n",
+       ".cython .nt { color: #008000; font-weight: bold } /* Name.Tag */\n",
+       ".cython .nv { color: #19177C } /* Name.Variable */\n",
+       ".cython .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */\n",
+       ".cython .w { color: #bbbbbb } /* Text.Whitespace */\n",
+       ".cython .mb { color: #666666 } /* Literal.Number.Bin */\n",
+       ".cython .mf { color: #666666 } /* Literal.Number.Float */\n",
+       ".cython .mh { color: #666666 } /* Literal.Number.Hex */\n",
+       ".cython .mi { color: #666666 } /* Literal.Number.Integer */\n",
+       ".cython .mo { color: #666666 } /* Literal.Number.Oct */\n",
+       ".cython .sa { color: #BA2121 } /* Literal.String.Affix */\n",
+       ".cython .sb { color: #BA2121 } /* Literal.String.Backtick */\n",
+       ".cython .sc { color: #BA2121 } /* Literal.String.Char */\n",
+       ".cython .dl { color: #BA2121 } /* Literal.String.Delimiter */\n",
+       ".cython .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */\n",
+       ".cython .s2 { color: #BA2121 } /* Literal.String.Double */\n",
+       ".cython .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */\n",
+       ".cython .sh { color: #BA2121 } /* Literal.String.Heredoc */\n",
+       ".cython .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */\n",
+       ".cython .sx { color: #008000 } /* Literal.String.Other */\n",
+       ".cython .sr { color: #BB6688 } /* Literal.String.Regex */\n",
+       ".cython .s1 { color: #BA2121 } /* Literal.String.Single */\n",
+       ".cython .ss { color: #19177C } /* Literal.String.Symbol */\n",
+       ".cython .bp { color: #008000 } /* Name.Builtin.Pseudo */\n",
+       ".cython .fm { color: #0000FF } /* Name.Function.Magic */\n",
+       ".cython .vc { color: #19177C } /* Name.Variable.Class */\n",
+       ".cython .vg { color: #19177C } /* Name.Variable.Global */\n",
+       ".cython .vi { color: #19177C } /* Name.Variable.Instance */\n",
+       ".cython .vm { color: #19177C } /* Name.Variable.Magic */\n",
+       ".cython .il { color: #666666 } /* Literal.Number.Integer.Long */\n",
+       "    </style>\n",
+       "    <script>\n",
+       "    function toggleDiv(id) {\n",
+       "        theDiv = id.nextElementSibling\n",
+       "        if (theDiv.style.display != 'block') theDiv.style.display = 'block';\n",
+       "        else theDiv.style.display = 'none';\n",
+       "    }\n",
+       "    </script>\n",
+       "</head>\n",
+       "<body class=\"cython\">\n",
+       "<p><span style=\"border-bottom: solid 1px grey;\">Generated by Cython 0.26</span></p>\n",
+       "<p>\n",
+       "    <span style=\"background-color: #FFFF00\">Yellow lines</span> hint at Python interaction.<br />\n",
+       "    Click on a line that starts with a \"<code>+</code>\" to see the C code that Cython generated for it.\n",
+       "</p>\n",
+       "<div class=\"cython\"><pre class=\"cython line score-24\" onclick='toggleDiv(this)'>+<span class=\"\">01</span>: <span class=\"k\">def</span> <span class=\"nf\">cprimes</span><span class=\"p\">(</span><span class=\"nb\">int</span> <span class=\"n\">kmax</span><span class=\"p\">):</span></pre>\n",
+       "<pre class='cython code score-24 '>/* Python wrapper */\n",
+       "static PyObject *__pyx_pw_46_cython_magic_52e640a266384bf0478136fe7ab4a75d_1cprimes(PyObject *__pyx_self, PyObject *__pyx_arg_kmax); /*proto*/\n",
+       "static PyMethodDef __pyx_mdef_46_cython_magic_52e640a266384bf0478136fe7ab4a75d_1cprimes = {\"cprimes\", (PyCFunction)__pyx_pw_46_cython_magic_52e640a266384bf0478136fe7ab4a75d_1cprimes, METH_O, 0};\n",
+       "static PyObject *__pyx_pw_46_cython_magic_52e640a266384bf0478136fe7ab4a75d_1cprimes(PyObject *__pyx_self, PyObject *__pyx_arg_kmax) {\n",
+       "  int __pyx_v_kmax;\n",
+       "  PyObject *__pyx_r = 0;\n",
+       "  <span class='refnanny'>__Pyx_RefNannyDeclarations</span>\n",
+       "  <span class='refnanny'>__Pyx_RefNannySetupContext</span>(\"cprimes (wrapper)\", 0);\n",
+       "  assert(__pyx_arg_kmax); {\n",
+       "    __pyx_v_kmax = <span class='pyx_c_api'>__Pyx_PyInt_As_int</span>(__pyx_arg_kmax); if (unlikely((__pyx_v_kmax == (int)-1) &amp;&amp; <span class='py_c_api'>PyErr_Occurred</span>())) __PYX_ERR(0, 1, __pyx_L3_error)\n",
+       "  }\n",
+       "  goto __pyx_L4_argument_unpacking_done;\n",
+       "  __pyx_L3_error:;\n",
+       "  <span class='pyx_c_api'>__Pyx_AddTraceback</span>(\"_cython_magic_52e640a266384bf0478136fe7ab4a75d.cprimes\", __pyx_clineno, __pyx_lineno, __pyx_filename);\n",
+       "  <span class='refnanny'>__Pyx_RefNannyFinishContext</span>();\n",
+       "  return NULL;\n",
+       "  __pyx_L4_argument_unpacking_done:;\n",
+       "  __pyx_r = __pyx_pf_46_cython_magic_52e640a266384bf0478136fe7ab4a75d_cprimes(__pyx_self, ((int)__pyx_v_kmax));\n",
+       "\n",
+       "  /* function exit code */\n",
+       "  <span class='refnanny'>__Pyx_RefNannyFinishContext</span>();\n",
+       "  return __pyx_r;\n",
+       "}\n",
+       "\n",
+       "static PyObject *__pyx_pf_46_cython_magic_52e640a266384bf0478136fe7ab4a75d_cprimes(CYTHON_UNUSED PyObject *__pyx_self, int __pyx_v_kmax) {\n",
+       "  int __pyx_v_p[0x3E8];\n",
+       "  PyObject *__pyx_v_result = NULL;\n",
+       "  int __pyx_v_k;\n",
+       "  int __pyx_v_n;\n",
+       "  int __pyx_v_i;\n",
+       "  PyObject *__pyx_r = NULL;\n",
+       "  <span class='refnanny'>__Pyx_RefNannyDeclarations</span>\n",
+       "  <span class='refnanny'>__Pyx_RefNannySetupContext</span>(\"cprimes\", 0);\n",
+       "/* … */\n",
+       "  /* function exit code */\n",
+       "  __pyx_L1_error:;\n",
+       "  <span class='pyx_macro_api'>__Pyx_XDECREF</span>(__pyx_t_1);\n",
+       "  <span class='pyx_c_api'>__Pyx_AddTraceback</span>(\"_cython_magic_52e640a266384bf0478136fe7ab4a75d.cprimes\", __pyx_clineno, __pyx_lineno, __pyx_filename);\n",
+       "  __pyx_r = NULL;\n",
+       "  __pyx_L0:;\n",
+       "  <span class='pyx_macro_api'>__Pyx_XDECREF</span>(__pyx_v_result);\n",
+       "  <span class='refnanny'>__Pyx_XGIVEREF</span>(__pyx_r);\n",
+       "  <span class='refnanny'>__Pyx_RefNannyFinishContext</span>();\n",
+       "  return __pyx_r;\n",
+       "}\n",
+       "/* … */\n",
+       "  __pyx_tuple_ = <span class='py_c_api'>PyTuple_Pack</span>(7, __pyx_n_s_kmax, __pyx_n_s_kmax, __pyx_n_s_p, __pyx_n_s_result, __pyx_n_s_k, __pyx_n_s_n, __pyx_n_s_i); if (unlikely(!__pyx_tuple_)) __PYX_ERR(0, 1, __pyx_L1_error)\n",
+       "  <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_tuple_);\n",
+       "  <span class='refnanny'>__Pyx_GIVEREF</span>(__pyx_tuple_);\n",
+       "/* … */\n",
+       "  __pyx_t_1 = PyCFunction_NewEx(&amp;__pyx_mdef_46_cython_magic_52e640a266384bf0478136fe7ab4a75d_1cprimes, NULL, __pyx_n_s_cython_magic_52e640a266384bf047); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1, __pyx_L1_error)\n",
+       "  <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_1);\n",
+       "  if (<span class='py_c_api'>PyDict_SetItem</span>(__pyx_d, __pyx_n_s_cprimes, __pyx_t_1) &lt; 0) __PYX_ERR(0, 1, __pyx_L1_error)\n",
+       "  <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_1); __pyx_t_1 = 0;\n",
+       "</pre><pre class=\"cython line score-0\">&#xA0;<span class=\"\">02</span>:     <span class=\"k\">cdef</span> <span class=\"kt\">int</span>[1000] <span class=\"nf\">p</span></pre>\n",
+       "<pre class=\"cython line score-5\" onclick='toggleDiv(this)'>+<span class=\"\">03</span>:     <span class=\"n\">result</span> <span class=\"o\">=</span> <span class=\"p\">[]</span></pre>\n",
+       "<pre class='cython code score-5 '>  __pyx_t_1 = <span class='py_c_api'>PyList_New</span>(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 3, __pyx_L1_error)\n",
+       "  <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_1);\n",
+       "  __pyx_v_result = ((PyObject*)__pyx_t_1);\n",
+       "  __pyx_t_1 = 0;\n",
+       "</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">04</span>:     <span class=\"k\">if</span> <span class=\"n\">kmax</span> <span class=\"o\">&gt;</span> <span class=\"mf\">1000</span><span class=\"p\">:</span></pre>\n",
+       "<pre class='cython code score-0 '>  __pyx_t_2 = ((__pyx_v_kmax &gt; 0x3E8) != 0);\n",
+       "  if (__pyx_t_2) {\n",
+       "/* … */\n",
+       "  }\n",
+       "</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">05</span>:         <span class=\"n\">kmax</span> <span class=\"o\">=</span> <span class=\"mf\">1000</span></pre>\n",
+       "<pre class='cython code score-0 '>    __pyx_v_kmax = 0x3E8;\n",
+       "</pre><pre class=\"cython line score-0\">&#xA0;<span class=\"\">06</span>:     <span class=\"k\">cdef</span> <span class=\"kt\">int</span> <span class=\"nf\">k</span><span class=\"p\">,</span> <span class=\"nf\">n</span><span class=\"p\">,</span> <span class=\"nf\">i</span></pre>\n",
+       "<pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">07</span>:     <span class=\"n\">k</span> <span class=\"o\">=</span> <span class=\"mf\">0</span></pre>\n",
+       "<pre class='cython code score-0 '>  __pyx_v_k = 0;\n",
+       "</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">08</span>:     <span class=\"n\">n</span> <span class=\"o\">=</span> <span class=\"mf\">2</span></pre>\n",
+       "<pre class='cython code score-0 '>  __pyx_v_n = 2;\n",
+       "</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">09</span>:     <span class=\"k\">while</span> <span class=\"n\">k</span> <span class=\"o\">&lt;</span> <span class=\"n\">kmax</span><span class=\"p\">:</span></pre>\n",
+       "<pre class='cython code score-0 '>  while (1) {\n",
+       "    __pyx_t_2 = ((__pyx_v_k &lt; __pyx_v_kmax) != 0);\n",
+       "    if (!__pyx_t_2) break;\n",
+       "</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">10</span>:         <span class=\"n\">i</span> <span class=\"o\">=</span> <span class=\"mf\">0</span></pre>\n",
+       "<pre class='cython code score-0 '>    __pyx_v_i = 0;\n",
+       "</pre><pre class=\"cython line score-5\" onclick='toggleDiv(this)'>+<span class=\"\">11</span>:         <span class=\"k\">while</span> <span class=\"n\">i</span> <span class=\"o\">&lt;</span> <span class=\"n\">k</span> <span class=\"ow\">and</span> <span class=\"n\">n</span> <span class=\"o\">%</span> <span class=\"n\">p</span><span class=\"p\">[</span><span class=\"n\">i</span><span class=\"p\">]</span> <span class=\"o\">!=</span> <span class=\"mf\">0</span><span class=\"p\">:</span></pre>\n",
+       "<pre class='cython code score-5 '>    while (1) {\n",
+       "      __pyx_t_3 = ((__pyx_v_i &lt; __pyx_v_k) != 0);\n",
+       "      if (__pyx_t_3) {\n",
+       "      } else {\n",
+       "        __pyx_t_2 = __pyx_t_3;\n",
+       "        goto __pyx_L8_bool_binop_done;\n",
+       "      }\n",
+       "      if (unlikely((__pyx_v_p[__pyx_v_i]) == 0)) {\n",
+       "        <span class='py_c_api'>PyErr_SetString</span>(PyExc_ZeroDivisionError, \"integer division or modulo by zero\");\n",
+       "        __PYX_ERR(0, 11, __pyx_L1_error)\n",
+       "      }\n",
+       "      __pyx_t_3 = ((__Pyx_mod_int(__pyx_v_n, (__pyx_v_p[__pyx_v_i])) != 0) != 0);\n",
+       "      __pyx_t_2 = __pyx_t_3;\n",
+       "      __pyx_L8_bool_binop_done:;\n",
+       "      if (!__pyx_t_2) break;\n",
+       "</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">12</span>:             <span class=\"n\">i</span> <span class=\"o\">=</span> <span class=\"n\">i</span> <span class=\"o\">+</span> <span class=\"mf\">1</span></pre>\n",
+       "<pre class='cython code score-0 '>      __pyx_v_i = (__pyx_v_i + 1);\n",
+       "    }\n",
+       "</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">13</span>:         <span class=\"k\">if</span> <span class=\"n\">i</span> <span class=\"o\">==</span> <span class=\"n\">k</span><span class=\"p\">:</span></pre>\n",
+       "<pre class='cython code score-0 '>    __pyx_t_2 = ((__pyx_v_i == __pyx_v_k) != 0);\n",
+       "    if (__pyx_t_2) {\n",
+       "/* … */\n",
+       "    }\n",
+       "</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">14</span>:             <span class=\"n\">p</span><span class=\"p\">[</span><span class=\"n\">k</span><span class=\"p\">]</span> <span class=\"o\">=</span> <span class=\"n\">n</span></pre>\n",
+       "<pre class='cython code score-0 '>      (__pyx_v_p[__pyx_v_k]) = __pyx_v_n;\n",
+       "</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">15</span>:             <span class=\"n\">k</span> <span class=\"o\">=</span> <span class=\"n\">k</span> <span class=\"o\">+</span> <span class=\"mf\">1</span></pre>\n",
+       "<pre class='cython code score-0 '>      __pyx_v_k = (__pyx_v_k + 1);\n",
+       "</pre><pre class=\"cython line score-5\" onclick='toggleDiv(this)'>+<span class=\"\">16</span>:             <span class=\"n\">result</span><span class=\"o\">.</span><span class=\"n\">append</span><span class=\"p\">(</span><span class=\"n\">n</span><span class=\"p\">)</span></pre>\n",
+       "<pre class='cython code score-5 '>      __pyx_t_1 = <span class='pyx_c_api'>__Pyx_PyInt_From_int</span>(__pyx_v_n); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 16, __pyx_L1_error)\n",
+       "      <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_1);\n",
+       "      __pyx_t_4 = <span class='pyx_c_api'>__Pyx_PyList_Append</span>(__pyx_v_result, __pyx_t_1); if (unlikely(__pyx_t_4 == -1)) __PYX_ERR(0, 16, __pyx_L1_error)\n",
+       "      <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_1); __pyx_t_1 = 0;\n",
+       "</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">17</span>:         <span class=\"n\">n</span> <span class=\"o\">=</span> <span class=\"n\">n</span> <span class=\"o\">+</span> <span class=\"mf\">1</span></pre>\n",
+       "<pre class='cython code score-0 '>    __pyx_v_n = (__pyx_v_n + 1);\n",
+       "  }\n",
+       "</pre><pre class=\"cython line score-2\" onclick='toggleDiv(this)'>+<span class=\"\">18</span>:     <span class=\"k\">return</span> <span class=\"n\">result</span></pre>\n",
+       "<pre class='cython code score-2 '>  <span class='pyx_macro_api'>__Pyx_XDECREF</span>(__pyx_r);\n",
+       "  <span class='pyx_macro_api'>__Pyx_INCREF</span>(__pyx_v_result);\n",
+       "  __pyx_r = __pyx_v_result;\n",
+       "  goto __pyx_L0;\n",
+       "</pre></div></body></html>"
+      ],
+      "text/plain": [
+       "<IPython.core.display.HTML object>"
+      ]
+     },
+     "execution_count": 91,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "%%cython -a\n",
+    "def cprimes(int kmax):\n",
+    "    cdef int[1000] p\n",
+    "    result = []\n",
+    "    if kmax > 1000:\n",
+    "        kmax = 1000\n",
+    "    cdef int k, n, i\n",
+    "    k = 0\n",
+    "    n = 2\n",
+    "    while k < kmax:\n",
+    "        i = 0\n",
+    "        while i < k and n % p[i] != 0:\n",
+    "            i = i + 1\n",
+    "        if i == k:\n",
+    "            p[k] = n\n",
+    "            k = k + 1\n",
+    "            result.append(n)\n",
+    "        n = n + 1\n",
+    "    return result"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 92,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]\n",
+      "CPU times: user 2.2 ms, sys: 10 µs, total: 2.21 ms\n",
+      "Wall time: 2.22 ms\n"
+     ]
+    }
+   ],
+   "source": [
+    "print(cprimes(10))\n",
+    "%time _ = cprimes(10000)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Defining C-style function"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 47,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#%%cython -a\n",
+    "def fib(n):\n",
+    "    if n <= 1:\n",
+    "        return 1\n",
+    "    else:\n",
+    "        return fib(n-2) + fib(n-1)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 48,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 1.48 s, sys: 1.79 ms, total: 1.48 s\n",
+      "Wall time: 1.48 s\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "5702887"
+      ]
+     },
+     "execution_count": 48,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "%time fib(33)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 49,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 27.5 ms, sys: 73 µs, total: 27.5 ms\n",
+      "Wall time: 27.5 ms\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "5702887"
+      ]
+     },
+     "execution_count": 49,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "%time fib_jit(33)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 56,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<!DOCTYPE html>\n",
+       "<!-- Generated by Cython 0.26 -->\n",
+       "<html>\n",
+       "<head>\n",
+       "    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n",
+       "    <title>Cython: _cython_magic_ed1fcd3356d03e7c12842ade7d34bd4b.pyx</title>\n",
+       "    <style type=\"text/css\">\n",
+       "    \n",
+       "body.cython { font-family: courier; font-size: 12; }\n",
+       "\n",
+       ".cython.tag  {  }\n",
+       ".cython.line { margin: 0em }\n",
+       ".cython.code { font-size: 9; color: #444444; display: none; margin: 0px 0px 0px 8px; border-left: 8px none; }\n",
+       "\n",
+       ".cython.line .run { background-color: #B0FFB0; }\n",
+       ".cython.line .mis { background-color: #FFB0B0; }\n",
+       ".cython.code.run  { border-left: 8px solid #B0FFB0; }\n",
+       ".cython.code.mis  { border-left: 8px solid #FFB0B0; }\n",
+       "\n",
+       ".cython.code .py_c_api  { color: red; }\n",
+       ".cython.code .py_macro_api  { color: #FF7000; }\n",
+       ".cython.code .pyx_c_api  { color: #FF3000; }\n",
+       ".cython.code .pyx_macro_api  { color: #FF7000; }\n",
+       ".cython.code .refnanny  { color: #FFA000; }\n",
+       ".cython.code .trace  { color: #FFA000; }\n",
+       ".cython.code .error_goto  { color: #FFA000; }\n",
+       "\n",
+       ".cython.code .coerce  { color: #008000; border: 1px dotted #008000 }\n",
+       ".cython.code .py_attr { color: #FF0000; font-weight: bold; }\n",
+       ".cython.code .c_attr  { color: #0000FF; }\n",
+       ".cython.code .py_call { color: #FF0000; font-weight: bold; }\n",
+       ".cython.code .c_call  { color: #0000FF; }\n",
+       "\n",
+       ".cython.score-0 {background-color: #FFFFff;}\n",
+       ".cython.score-1 {background-color: #FFFFe7;}\n",
+       ".cython.score-2 {background-color: #FFFFd4;}\n",
+       ".cython.score-3 {background-color: #FFFFc4;}\n",
+       ".cython.score-4 {background-color: #FFFFb6;}\n",
+       ".cython.score-5 {background-color: #FFFFaa;}\n",
+       ".cython.score-6 {background-color: #FFFF9f;}\n",
+       ".cython.score-7 {background-color: #FFFF96;}\n",
+       ".cython.score-8 {background-color: #FFFF8d;}\n",
+       ".cython.score-9 {background-color: #FFFF86;}\n",
+       ".cython.score-10 {background-color: #FFFF7f;}\n",
+       ".cython.score-11 {background-color: #FFFF79;}\n",
+       ".cython.score-12 {background-color: #FFFF73;}\n",
+       ".cython.score-13 {background-color: #FFFF6e;}\n",
+       ".cython.score-14 {background-color: #FFFF6a;}\n",
+       ".cython.score-15 {background-color: #FFFF66;}\n",
+       ".cython.score-16 {background-color: #FFFF62;}\n",
+       ".cython.score-17 {background-color: #FFFF5e;}\n",
+       ".cython.score-18 {background-color: #FFFF5b;}\n",
+       ".cython.score-19 {background-color: #FFFF57;}\n",
+       ".cython.score-20 {background-color: #FFFF55;}\n",
+       ".cython.score-21 {background-color: #FFFF52;}\n",
+       ".cython.score-22 {background-color: #FFFF4f;}\n",
+       ".cython.score-23 {background-color: #FFFF4d;}\n",
+       ".cython.score-24 {background-color: #FFFF4b;}\n",
+       ".cython.score-25 {background-color: #FFFF48;}\n",
+       ".cython.score-26 {background-color: #FFFF46;}\n",
+       ".cython.score-27 {background-color: #FFFF44;}\n",
+       ".cython.score-28 {background-color: #FFFF43;}\n",
+       ".cython.score-29 {background-color: #FFFF41;}\n",
+       ".cython.score-30 {background-color: #FFFF3f;}\n",
+       ".cython.score-31 {background-color: #FFFF3e;}\n",
+       ".cython.score-32 {background-color: #FFFF3c;}\n",
+       ".cython.score-33 {background-color: #FFFF3b;}\n",
+       ".cython.score-34 {background-color: #FFFF39;}\n",
+       ".cython.score-35 {background-color: #FFFF38;}\n",
+       ".cython.score-36 {background-color: #FFFF37;}\n",
+       ".cython.score-37 {background-color: #FFFF36;}\n",
+       ".cython.score-38 {background-color: #FFFF35;}\n",
+       ".cython.score-39 {background-color: #FFFF34;}\n",
+       ".cython.score-40 {background-color: #FFFF33;}\n",
+       ".cython.score-41 {background-color: #FFFF32;}\n",
+       ".cython.score-42 {background-color: #FFFF31;}\n",
+       ".cython.score-43 {background-color: #FFFF30;}\n",
+       ".cython.score-44 {background-color: #FFFF2f;}\n",
+       ".cython.score-45 {background-color: #FFFF2e;}\n",
+       ".cython.score-46 {background-color: #FFFF2d;}\n",
+       ".cython.score-47 {background-color: #FFFF2c;}\n",
+       ".cython.score-48 {background-color: #FFFF2b;}\n",
+       ".cython.score-49 {background-color: #FFFF2b;}\n",
+       ".cython.score-50 {background-color: #FFFF2a;}\n",
+       ".cython.score-51 {background-color: #FFFF29;}\n",
+       ".cython.score-52 {background-color: #FFFF29;}\n",
+       ".cython.score-53 {background-color: #FFFF28;}\n",
+       ".cython.score-54 {background-color: #FFFF27;}\n",
+       ".cython.score-55 {background-color: #FFFF27;}\n",
+       ".cython.score-56 {background-color: #FFFF26;}\n",
+       ".cython.score-57 {background-color: #FFFF26;}\n",
+       ".cython.score-58 {background-color: #FFFF25;}\n",
+       ".cython.score-59 {background-color: #FFFF24;}\n",
+       ".cython.score-60 {background-color: #FFFF24;}\n",
+       ".cython.score-61 {background-color: #FFFF23;}\n",
+       ".cython.score-62 {background-color: #FFFF23;}\n",
+       ".cython.score-63 {background-color: #FFFF22;}\n",
+       ".cython.score-64 {background-color: #FFFF22;}\n",
+       ".cython.score-65 {background-color: #FFFF22;}\n",
+       ".cython.score-66 {background-color: #FFFF21;}\n",
+       ".cython.score-67 {background-color: #FFFF21;}\n",
+       ".cython.score-68 {background-color: #FFFF20;}\n",
+       ".cython.score-69 {background-color: #FFFF20;}\n",
+       ".cython.score-70 {background-color: #FFFF1f;}\n",
+       ".cython.score-71 {background-color: #FFFF1f;}\n",
+       ".cython.score-72 {background-color: #FFFF1f;}\n",
+       ".cython.score-73 {background-color: #FFFF1e;}\n",
+       ".cython.score-74 {background-color: #FFFF1e;}\n",
+       ".cython.score-75 {background-color: #FFFF1e;}\n",
+       ".cython.score-76 {background-color: #FFFF1d;}\n",
+       ".cython.score-77 {background-color: #FFFF1d;}\n",
+       ".cython.score-78 {background-color: #FFFF1c;}\n",
+       ".cython.score-79 {background-color: #FFFF1c;}\n",
+       ".cython.score-80 {background-color: #FFFF1c;}\n",
+       ".cython.score-81 {background-color: #FFFF1c;}\n",
+       ".cython.score-82 {background-color: #FFFF1b;}\n",
+       ".cython.score-83 {background-color: #FFFF1b;}\n",
+       ".cython.score-84 {background-color: #FFFF1b;}\n",
+       ".cython.score-85 {background-color: #FFFF1a;}\n",
+       ".cython.score-86 {background-color: #FFFF1a;}\n",
+       ".cython.score-87 {background-color: #FFFF1a;}\n",
+       ".cython.score-88 {background-color: #FFFF1a;}\n",
+       ".cython.score-89 {background-color: #FFFF19;}\n",
+       ".cython.score-90 {background-color: #FFFF19;}\n",
+       ".cython.score-91 {background-color: #FFFF19;}\n",
+       ".cython.score-92 {background-color: #FFFF19;}\n",
+       ".cython.score-93 {background-color: #FFFF18;}\n",
+       ".cython.score-94 {background-color: #FFFF18;}\n",
+       ".cython.score-95 {background-color: #FFFF18;}\n",
+       ".cython.score-96 {background-color: #FFFF18;}\n",
+       ".cython.score-97 {background-color: #FFFF17;}\n",
+       ".cython.score-98 {background-color: #FFFF17;}\n",
+       ".cython.score-99 {background-color: #FFFF17;}\n",
+       ".cython.score-100 {background-color: #FFFF17;}\n",
+       ".cython.score-101 {background-color: #FFFF16;}\n",
+       ".cython.score-102 {background-color: #FFFF16;}\n",
+       ".cython.score-103 {background-color: #FFFF16;}\n",
+       ".cython.score-104 {background-color: #FFFF16;}\n",
+       ".cython.score-105 {background-color: #FFFF16;}\n",
+       ".cython.score-106 {background-color: #FFFF15;}\n",
+       ".cython.score-107 {background-color: #FFFF15;}\n",
+       ".cython.score-108 {background-color: #FFFF15;}\n",
+       ".cython.score-109 {background-color: #FFFF15;}\n",
+       ".cython.score-110 {background-color: #FFFF15;}\n",
+       ".cython.score-111 {background-color: #FFFF15;}\n",
+       ".cython.score-112 {background-color: #FFFF14;}\n",
+       ".cython.score-113 {background-color: #FFFF14;}\n",
+       ".cython.score-114 {background-color: #FFFF14;}\n",
+       ".cython.score-115 {background-color: #FFFF14;}\n",
+       ".cython.score-116 {background-color: #FFFF14;}\n",
+       ".cython.score-117 {background-color: #FFFF14;}\n",
+       ".cython.score-118 {background-color: #FFFF13;}\n",
+       ".cython.score-119 {background-color: #FFFF13;}\n",
+       ".cython.score-120 {background-color: #FFFF13;}\n",
+       ".cython.score-121 {background-color: #FFFF13;}\n",
+       ".cython.score-122 {background-color: #FFFF13;}\n",
+       ".cython.score-123 {background-color: #FFFF13;}\n",
+       ".cython.score-124 {background-color: #FFFF13;}\n",
+       ".cython.score-125 {background-color: #FFFF12;}\n",
+       ".cython.score-126 {background-color: #FFFF12;}\n",
+       ".cython.score-127 {background-color: #FFFF12;}\n",
+       ".cython.score-128 {background-color: #FFFF12;}\n",
+       ".cython.score-129 {background-color: #FFFF12;}\n",
+       ".cython.score-130 {background-color: #FFFF12;}\n",
+       ".cython.score-131 {background-color: #FFFF12;}\n",
+       ".cython.score-132 {background-color: #FFFF11;}\n",
+       ".cython.score-133 {background-color: #FFFF11;}\n",
+       ".cython.score-134 {background-color: #FFFF11;}\n",
+       ".cython.score-135 {background-color: #FFFF11;}\n",
+       ".cython.score-136 {background-color: #FFFF11;}\n",
+       ".cython.score-137 {background-color: #FFFF11;}\n",
+       ".cython.score-138 {background-color: #FFFF11;}\n",
+       ".cython.score-139 {background-color: #FFFF11;}\n",
+       ".cython.score-140 {background-color: #FFFF11;}\n",
+       ".cython.score-141 {background-color: #FFFF10;}\n",
+       ".cython.score-142 {background-color: #FFFF10;}\n",
+       ".cython.score-143 {background-color: #FFFF10;}\n",
+       ".cython.score-144 {background-color: #FFFF10;}\n",
+       ".cython.score-145 {background-color: #FFFF10;}\n",
+       ".cython.score-146 {background-color: #FFFF10;}\n",
+       ".cython.score-147 {background-color: #FFFF10;}\n",
+       ".cython.score-148 {background-color: #FFFF10;}\n",
+       ".cython.score-149 {background-color: #FFFF10;}\n",
+       ".cython.score-150 {background-color: #FFFF0f;}\n",
+       ".cython.score-151 {background-color: #FFFF0f;}\n",
+       ".cython.score-152 {background-color: #FFFF0f;}\n",
+       ".cython.score-153 {background-color: #FFFF0f;}\n",
+       ".cython.score-154 {background-color: #FFFF0f;}\n",
+       ".cython.score-155 {background-color: #FFFF0f;}\n",
+       ".cython.score-156 {background-color: #FFFF0f;}\n",
+       ".cython.score-157 {background-color: #FFFF0f;}\n",
+       ".cython.score-158 {background-color: #FFFF0f;}\n",
+       ".cython.score-159 {background-color: #FFFF0f;}\n",
+       ".cython.score-160 {background-color: #FFFF0f;}\n",
+       ".cython.score-161 {background-color: #FFFF0e;}\n",
+       ".cython.score-162 {background-color: #FFFF0e;}\n",
+       ".cython.score-163 {background-color: #FFFF0e;}\n",
+       ".cython.score-164 {background-color: #FFFF0e;}\n",
+       ".cython.score-165 {background-color: #FFFF0e;}\n",
+       ".cython.score-166 {background-color: #FFFF0e;}\n",
+       ".cython.score-167 {background-color: #FFFF0e;}\n",
+       ".cython.score-168 {background-color: #FFFF0e;}\n",
+       ".cython.score-169 {background-color: #FFFF0e;}\n",
+       ".cython.score-170 {background-color: #FFFF0e;}\n",
+       ".cython.score-171 {background-color: #FFFF0e;}\n",
+       ".cython.score-172 {background-color: #FFFF0e;}\n",
+       ".cython.score-173 {background-color: #FFFF0d;}\n",
+       ".cython.score-174 {background-color: #FFFF0d;}\n",
+       ".cython.score-175 {background-color: #FFFF0d;}\n",
+       ".cython.score-176 {background-color: #FFFF0d;}\n",
+       ".cython.score-177 {background-color: #FFFF0d;}\n",
+       ".cython.score-178 {background-color: #FFFF0d;}\n",
+       ".cython.score-179 {background-color: #FFFF0d;}\n",
+       ".cython.score-180 {background-color: #FFFF0d;}\n",
+       ".cython.score-181 {background-color: #FFFF0d;}\n",
+       ".cython.score-182 {background-color: #FFFF0d;}\n",
+       ".cython.score-183 {background-color: #FFFF0d;}\n",
+       ".cython.score-184 {background-color: #FFFF0d;}\n",
+       ".cython.score-185 {background-color: #FFFF0d;}\n",
+       ".cython.score-186 {background-color: #FFFF0d;}\n",
+       ".cython.score-187 {background-color: #FFFF0c;}\n",
+       ".cython.score-188 {background-color: #FFFF0c;}\n",
+       ".cython.score-189 {background-color: #FFFF0c;}\n",
+       ".cython.score-190 {background-color: #FFFF0c;}\n",
+       ".cython.score-191 {background-color: #FFFF0c;}\n",
+       ".cython.score-192 {background-color: #FFFF0c;}\n",
+       ".cython.score-193 {background-color: #FFFF0c;}\n",
+       ".cython.score-194 {background-color: #FFFF0c;}\n",
+       ".cython.score-195 {background-color: #FFFF0c;}\n",
+       ".cython.score-196 {background-color: #FFFF0c;}\n",
+       ".cython.score-197 {background-color: #FFFF0c;}\n",
+       ".cython.score-198 {background-color: #FFFF0c;}\n",
+       ".cython.score-199 {background-color: #FFFF0c;}\n",
+       ".cython.score-200 {background-color: #FFFF0c;}\n",
+       ".cython.score-201 {background-color: #FFFF0c;}\n",
+       ".cython.score-202 {background-color: #FFFF0c;}\n",
+       ".cython.score-203 {background-color: #FFFF0b;}\n",
+       ".cython.score-204 {background-color: #FFFF0b;}\n",
+       ".cython.score-205 {background-color: #FFFF0b;}\n",
+       ".cython.score-206 {background-color: #FFFF0b;}\n",
+       ".cython.score-207 {background-color: #FFFF0b;}\n",
+       ".cython.score-208 {background-color: #FFFF0b;}\n",
+       ".cython.score-209 {background-color: #FFFF0b;}\n",
+       ".cython.score-210 {background-color: #FFFF0b;}\n",
+       ".cython.score-211 {background-color: #FFFF0b;}\n",
+       ".cython.score-212 {background-color: #FFFF0b;}\n",
+       ".cython.score-213 {background-color: #FFFF0b;}\n",
+       ".cython.score-214 {background-color: #FFFF0b;}\n",
+       ".cython.score-215 {background-color: #FFFF0b;}\n",
+       ".cython.score-216 {background-color: #FFFF0b;}\n",
+       ".cython.score-217 {background-color: #FFFF0b;}\n",
+       ".cython.score-218 {background-color: #FFFF0b;}\n",
+       ".cython.score-219 {background-color: #FFFF0b;}\n",
+       ".cython.score-220 {background-color: #FFFF0b;}\n",
+       ".cython.score-221 {background-color: #FFFF0b;}\n",
+       ".cython.score-222 {background-color: #FFFF0a;}\n",
+       ".cython.score-223 {background-color: #FFFF0a;}\n",
+       ".cython.score-224 {background-color: #FFFF0a;}\n",
+       ".cython.score-225 {background-color: #FFFF0a;}\n",
+       ".cython.score-226 {background-color: #FFFF0a;}\n",
+       ".cython.score-227 {background-color: #FFFF0a;}\n",
+       ".cython.score-228 {background-color: #FFFF0a;}\n",
+       ".cython.score-229 {background-color: #FFFF0a;}\n",
+       ".cython.score-230 {background-color: #FFFF0a;}\n",
+       ".cython.score-231 {background-color: #FFFF0a;}\n",
+       ".cython.score-232 {background-color: #FFFF0a;}\n",
+       ".cython.score-233 {background-color: #FFFF0a;}\n",
+       ".cython.score-234 {background-color: #FFFF0a;}\n",
+       ".cython.score-235 {background-color: #FFFF0a;}\n",
+       ".cython.score-236 {background-color: #FFFF0a;}\n",
+       ".cython.score-237 {background-color: #FFFF0a;}\n",
+       ".cython.score-238 {background-color: #FFFF0a;}\n",
+       ".cython.score-239 {background-color: #FFFF0a;}\n",
+       ".cython.score-240 {background-color: #FFFF0a;}\n",
+       ".cython.score-241 {background-color: #FFFF0a;}\n",
+       ".cython.score-242 {background-color: #FFFF0a;}\n",
+       ".cython.score-243 {background-color: #FFFF0a;}\n",
+       ".cython.score-244 {background-color: #FFFF0a;}\n",
+       ".cython.score-245 {background-color: #FFFF0a;}\n",
+       ".cython.score-246 {background-color: #FFFF09;}\n",
+       ".cython.score-247 {background-color: #FFFF09;}\n",
+       ".cython.score-248 {background-color: #FFFF09;}\n",
+       ".cython.score-249 {background-color: #FFFF09;}\n",
+       ".cython.score-250 {background-color: #FFFF09;}\n",
+       ".cython.score-251 {background-color: #FFFF09;}\n",
+       ".cython.score-252 {background-color: #FFFF09;}\n",
+       ".cython.score-253 {background-color: #FFFF09;}\n",
+       ".cython.score-254 {background-color: #FFFF09;}\n",
+       ".cython .hll { background-color: #ffffcc }\n",
+       ".cython  { background: #f8f8f8; }\n",
+       ".cython .c { color: #408080; font-style: italic } /* Comment */\n",
+       ".cython .err { border: 1px solid #FF0000 } /* Error */\n",
+       ".cython .k { color: #008000; font-weight: bold } /* Keyword */\n",
+       ".cython .o { color: #666666 } /* Operator */\n",
+       ".cython .ch { color: #408080; font-style: italic } /* Comment.Hashbang */\n",
+       ".cython .cm { color: #408080; font-style: italic } /* Comment.Multiline */\n",
+       ".cython .cp { color: #BC7A00 } /* Comment.Preproc */\n",
+       ".cython .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */\n",
+       ".cython .c1 { color: #408080; font-style: italic } /* Comment.Single */\n",
+       ".cython .cs { color: #408080; font-style: italic } /* Comment.Special */\n",
+       ".cython .gd { color: #A00000 } /* Generic.Deleted */\n",
+       ".cython .ge { font-style: italic } /* Generic.Emph */\n",
+       ".cython .gr { color: #FF0000 } /* Generic.Error */\n",
+       ".cython .gh { color: #000080; font-weight: bold } /* Generic.Heading */\n",
+       ".cython .gi { color: #00A000 } /* Generic.Inserted */\n",
+       ".cython .go { color: #888888 } /* Generic.Output */\n",
+       ".cython .gp { color: #000080; font-weight: bold } /* Generic.Prompt */\n",
+       ".cython .gs { font-weight: bold } /* Generic.Strong */\n",
+       ".cython .gu { color: #800080; font-weight: bold } /* Generic.Subheading */\n",
+       ".cython .gt { color: #0044DD } /* Generic.Traceback */\n",
+       ".cython .kc { color: #008000; font-weight: bold } /* Keyword.Constant */\n",
+       ".cython .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */\n",
+       ".cython .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */\n",
+       ".cython .kp { color: #008000 } /* Keyword.Pseudo */\n",
+       ".cython .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */\n",
+       ".cython .kt { color: #B00040 } /* Keyword.Type */\n",
+       ".cython .m { color: #666666 } /* Literal.Number */\n",
+       ".cython .s { color: #BA2121 } /* Literal.String */\n",
+       ".cython .na { color: #7D9029 } /* Name.Attribute */\n",
+       ".cython .nb { color: #008000 } /* Name.Builtin */\n",
+       ".cython .nc { color: #0000FF; font-weight: bold } /* Name.Class */\n",
+       ".cython .no { color: #880000 } /* Name.Constant */\n",
+       ".cython .nd { color: #AA22FF } /* Name.Decorator */\n",
+       ".cython .ni { color: #999999; font-weight: bold } /* Name.Entity */\n",
+       ".cython .ne { color: #D2413A; font-weight: bold } /* Name.Exception */\n",
+       ".cython .nf { color: #0000FF } /* Name.Function */\n",
+       ".cython .nl { color: #A0A000 } /* Name.Label */\n",
+       ".cython .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */\n",
+       ".cython .nt { color: #008000; font-weight: bold } /* Name.Tag */\n",
+       ".cython .nv { color: #19177C } /* Name.Variable */\n",
+       ".cython .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */\n",
+       ".cython .w { color: #bbbbbb } /* Text.Whitespace */\n",
+       ".cython .mb { color: #666666 } /* Literal.Number.Bin */\n",
+       ".cython .mf { color: #666666 } /* Literal.Number.Float */\n",
+       ".cython .mh { color: #666666 } /* Literal.Number.Hex */\n",
+       ".cython .mi { color: #666666 } /* Literal.Number.Integer */\n",
+       ".cython .mo { color: #666666 } /* Literal.Number.Oct */\n",
+       ".cython .sa { color: #BA2121 } /* Literal.String.Affix */\n",
+       ".cython .sb { color: #BA2121 } /* Literal.String.Backtick */\n",
+       ".cython .sc { color: #BA2121 } /* Literal.String.Char */\n",
+       ".cython .dl { color: #BA2121 } /* Literal.String.Delimiter */\n",
+       ".cython .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */\n",
+       ".cython .s2 { color: #BA2121 } /* Literal.String.Double */\n",
+       ".cython .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */\n",
+       ".cython .sh { color: #BA2121 } /* Literal.String.Heredoc */\n",
+       ".cython .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */\n",
+       ".cython .sx { color: #008000 } /* Literal.String.Other */\n",
+       ".cython .sr { color: #BB6688 } /* Literal.String.Regex */\n",
+       ".cython .s1 { color: #BA2121 } /* Literal.String.Single */\n",
+       ".cython .ss { color: #19177C } /* Literal.String.Symbol */\n",
+       ".cython .bp { color: #008000 } /* Name.Builtin.Pseudo */\n",
+       ".cython .fm { color: #0000FF } /* Name.Function.Magic */\n",
+       ".cython .vc { color: #19177C } /* Name.Variable.Class */\n",
+       ".cython .vg { color: #19177C } /* Name.Variable.Global */\n",
+       ".cython .vi { color: #19177C } /* Name.Variable.Instance */\n",
+       ".cython .vm { color: #19177C } /* Name.Variable.Magic */\n",
+       ".cython .il { color: #666666 } /* Literal.Number.Integer.Long */\n",
+       "    </style>\n",
+       "    <script>\n",
+       "    function toggleDiv(id) {\n",
+       "        theDiv = id.nextElementSibling\n",
+       "        if (theDiv.style.display != 'block') theDiv.style.display = 'block';\n",
+       "        else theDiv.style.display = 'none';\n",
+       "    }\n",
+       "    </script>\n",
+       "</head>\n",
+       "<body class=\"cython\">\n",
+       "<p><span style=\"border-bottom: solid 1px grey;\">Generated by Cython 0.26</span></p>\n",
+       "<p>\n",
+       "    <span style=\"background-color: #FFFF00\">Yellow lines</span> hint at Python interaction.<br />\n",
+       "    Click on a line that starts with a \"<code>+</code>\" to see the C code that Cython generated for it.\n",
+       "</p>\n",
+       "<div class=\"cython\"><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">1</span>: <span class=\"k\">cdef</span> <span class=\"kt\">int</span> <span class=\"nf\">cfib</span><span class=\"p\">(</span><span class=\"nb\">int</span> <span class=\"n\">n</span><span class=\"p\">)</span> <span class=\"k\">nogil</span><span class=\"p\">:</span></pre>\n",
+       "<pre class='cython code score-0 '>static int __pyx_f_46_cython_magic_ed1fcd3356d03e7c12842ade7d34bd4b_cfib(int __pyx_v_n) {\n",
+       "  int __pyx_r;\n",
+       "/* … */\n",
+       "  /* function exit code */\n",
+       "  __pyx_L0:;\n",
+       "  return __pyx_r;\n",
+       "}\n",
+       "</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">2</span>:     <span class=\"k\">if</span> <span class=\"n\">n</span> <span class=\"o\">&lt;=</span> <span class=\"mf\">1</span><span class=\"p\">:</span></pre>\n",
+       "<pre class='cython code score-0 '>  __pyx_t_1 = ((__pyx_v_n &lt;= 1) != 0);\n",
+       "  if (__pyx_t_1) {\n",
+       "/* … */\n",
+       "  }\n",
+       "</pre><pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">3</span>:         <span class=\"k\">return</span> <span class=\"mf\">1</span></pre>\n",
+       "<pre class='cython code score-0 '>    __pyx_r = 1;\n",
+       "    goto __pyx_L0;\n",
+       "</pre><pre class=\"cython line score-0\">&#xA0;<span class=\"\">4</span>:     <span class=\"k\">else</span><span class=\"p\">:</span></pre>\n",
+       "<pre class=\"cython line score-0\" onclick='toggleDiv(this)'>+<span class=\"\">5</span>:         <span class=\"k\">return</span> <span class=\"n\">cfib</span><span class=\"p\">(</span><span class=\"n\">n</span><span class=\"o\">-</span><span class=\"mf\">2</span><span class=\"p\">)</span> <span class=\"o\">+</span> <span class=\"n\">cfib</span><span class=\"p\">(</span><span class=\"n\">n</span><span class=\"o\">-</span><span class=\"mf\">1</span><span class=\"p\">)</span></pre>\n",
+       "<pre class='cython code score-0 '>  /*else*/ {\n",
+       "    __pyx_r = (__pyx_f_46_cython_magic_ed1fcd3356d03e7c12842ade7d34bd4b_cfib((__pyx_v_n - 2)) + __pyx_f_46_cython_magic_ed1fcd3356d03e7c12842ade7d34bd4b_cfib((__pyx_v_n - 1)));\n",
+       "    goto __pyx_L0;\n",
+       "  }\n",
+       "</pre><pre class=\"cython line score-0\">&#xA0;<span class=\"\">6</span>: </pre>\n",
+       "<pre class=\"cython line score-14\" onclick='toggleDiv(this)'>+<span class=\"\">7</span>: <span class=\"k\">def</span> <span class=\"nf\">fib</span><span class=\"p\">(</span><span class=\"n\">n</span><span class=\"p\">):</span></pre>\n",
+       "<pre class='cython code score-14 '>/* Python wrapper */\n",
+       "static PyObject *__pyx_pw_46_cython_magic_ed1fcd3356d03e7c12842ade7d34bd4b_1fib(PyObject *__pyx_self, PyObject *__pyx_v_n); /*proto*/\n",
+       "static PyMethodDef __pyx_mdef_46_cython_magic_ed1fcd3356d03e7c12842ade7d34bd4b_1fib = {\"fib\", (PyCFunction)__pyx_pw_46_cython_magic_ed1fcd3356d03e7c12842ade7d34bd4b_1fib, METH_O, 0};\n",
+       "static PyObject *__pyx_pw_46_cython_magic_ed1fcd3356d03e7c12842ade7d34bd4b_1fib(PyObject *__pyx_self, PyObject *__pyx_v_n) {\n",
+       "  PyObject *__pyx_r = 0;\n",
+       "  <span class='refnanny'>__Pyx_RefNannyDeclarations</span>\n",
+       "  <span class='refnanny'>__Pyx_RefNannySetupContext</span>(\"fib (wrapper)\", 0);\n",
+       "  __pyx_r = __pyx_pf_46_cython_magic_ed1fcd3356d03e7c12842ade7d34bd4b_fib(__pyx_self, ((PyObject *)__pyx_v_n));\n",
+       "\n",
+       "  /* function exit code */\n",
+       "  <span class='refnanny'>__Pyx_RefNannyFinishContext</span>();\n",
+       "  return __pyx_r;\n",
+       "}\n",
+       "\n",
+       "static PyObject *__pyx_pf_46_cython_magic_ed1fcd3356d03e7c12842ade7d34bd4b_fib(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_n) {\n",
+       "  PyObject *__pyx_r = NULL;\n",
+       "  <span class='refnanny'>__Pyx_RefNannyDeclarations</span>\n",
+       "  <span class='refnanny'>__Pyx_RefNannySetupContext</span>(\"fib\", 0);\n",
+       "/* … */\n",
+       "  /* function exit code */\n",
+       "  __pyx_L1_error:;\n",
+       "  <span class='pyx_macro_api'>__Pyx_XDECREF</span>(__pyx_t_2);\n",
+       "  <span class='pyx_c_api'>__Pyx_AddTraceback</span>(\"_cython_magic_ed1fcd3356d03e7c12842ade7d34bd4b.fib\", __pyx_clineno, __pyx_lineno, __pyx_filename);\n",
+       "  __pyx_r = NULL;\n",
+       "  __pyx_L0:;\n",
+       "  <span class='refnanny'>__Pyx_XGIVEREF</span>(__pyx_r);\n",
+       "  <span class='refnanny'>__Pyx_RefNannyFinishContext</span>();\n",
+       "  return __pyx_r;\n",
+       "}\n",
+       "/* … */\n",
+       "  __pyx_tuple_ = <span class='py_c_api'>PyTuple_Pack</span>(1, __pyx_n_s_n); if (unlikely(!__pyx_tuple_)) __PYX_ERR(0, 7, __pyx_L1_error)\n",
+       "  <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_tuple_);\n",
+       "  <span class='refnanny'>__Pyx_GIVEREF</span>(__pyx_tuple_);\n",
+       "/* … */\n",
+       "  __pyx_t_1 = PyCFunction_NewEx(&amp;__pyx_mdef_46_cython_magic_ed1fcd3356d03e7c12842ade7d34bd4b_1fib, NULL, __pyx_n_s_cython_magic_ed1fcd3356d03e7c12); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 7, __pyx_L1_error)\n",
+       "  <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_1);\n",
+       "  if (<span class='py_c_api'>PyDict_SetItem</span>(__pyx_d, __pyx_n_s_fib, __pyx_t_1) &lt; 0) __PYX_ERR(0, 7, __pyx_L1_error)\n",
+       "  <span class='pyx_macro_api'>__Pyx_DECREF</span>(__pyx_t_1); __pyx_t_1 = 0;\n",
+       "</pre><pre class=\"cython line score-10\" onclick='toggleDiv(this)'>+<span class=\"\">8</span>:     <span class=\"k\">return</span> <span class=\"n\">cfib</span><span class=\"p\">(</span><span class=\"n\">n</span><span class=\"p\">)</span></pre>\n",
+       "<pre class='cython code score-10 '>  <span class='pyx_macro_api'>__Pyx_XDECREF</span>(__pyx_r);\n",
+       "  __pyx_t_1 = <span class='pyx_c_api'>__Pyx_PyInt_As_int</span>(__pyx_v_n); if (unlikely((__pyx_t_1 == (int)-1) &amp;&amp; <span class='py_c_api'>PyErr_Occurred</span>())) __PYX_ERR(0, 8, __pyx_L1_error)\n",
+       "  __pyx_t_2 = <span class='pyx_c_api'>__Pyx_PyInt_From_int</span>(__pyx_f_46_cython_magic_ed1fcd3356d03e7c12842ade7d34bd4b_cfib(__pyx_t_1)); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 8, __pyx_L1_error)\n",
+       "  <span class='refnanny'>__Pyx_GOTREF</span>(__pyx_t_2);\n",
+       "  __pyx_r = __pyx_t_2;\n",
+       "  __pyx_t_2 = 0;\n",
+       "  goto __pyx_L0;\n",
+       "</pre></div></body></html>"
+      ],
+      "text/plain": [
+       "<IPython.core.display.HTML object>"
+      ]
+     },
+     "execution_count": 56,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "%%cython -a\n",
+    "cdef int cfib(int n) nogil:\n",
+    "    if n <= 1:\n",
+    "        return 1\n",
+    "    else:\n",
+    "        return cfib(n-2) + cfib(n-1)\n",
+    "\n",
+    "def fib(n):\n",
+    "    return cfib(n)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 57,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 10.2 ms, sys: 138 µs, total: 10.4 ms\n",
+      "Wall time: 10.4 ms\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "5702887"
+      ]
+     },
+     "execution_count": 57,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "%time fib(33)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Cython in production code"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "def root(a, b, c):\n",
+    "    D = b ** 2 - 4 * a * c\n",
+    "    if D < 0:\n",
+    "        return NAN, NAN\n",
+    "    x1 = (-b + sqrt(D)) / (2 * a)\n",
+    "    x2 = (-b - sqrt(D)) / (2 * a)\n",
+    "    return x1, x2"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 68,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 236 ms, sys: 53.7 ms, total: 290 ms\n",
+      "Wall time: 288 ms\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "(array([-1.72246969, -2.6378758 ,         nan, ..., -2.20722389,\n",
+       "                nan, -1.84415698]),\n",
+       " array([2.27571702, 2.04353046,        nan, ..., 2.62522459,        nan,\n",
+       "        1.49223876]))"
+      ]
+     },
+     "execution_count": 68,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "%time np.vectorize(root)(a, b, 7)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 66,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "CPU times: user 353 ms, sys: 57.8 ms, total: 411 ms\n",
+      "Wall time: 410 ms\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "(array([-1.72246968, -2.63787563,         nan, ..., -2.20722406,\n",
+       "                nan, -1.84415698]),\n",
+       " array([2.27571708, 2.04353033,        nan, ..., 2.62522454,        nan,\n",
+       "        1.4922388 ]))"
+      ]
+     },
+     "execution_count": 66,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "%time root_jit_vec(a, b, 7)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "%%cython -a\n",
+    "from libc.math cimport sqrt, NAN\n",
+    "\n",
+    "def root(float a, float b, float c):\n",
+    "    cdef float D, x1, x2\n",
+    "    D = b ** 2 - 4 * a * c\n",
+    "    if D < 0:\n",
+    "        return NAN, NAN\n",
+    "    x1 = (-b + sqrt(D)) / (2 * a)\n",
+    "    x2 = (-b - sqrt(D)) / (2 * a)\n",
+    "    return x1, x2\n",
+    "print(root(1, 3, 4), root(-1, 3, 4), root(1, 0, 0))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Cython in production code"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "http://docs.cython.org/en/latest/src/tutorial/cython_tutorial.html"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Steps to optimizing code:\n",
+    "- Profile where the bottleneck is (%prun)\n",
+    "- Is there a faster algorithm for the bottleneck?\n",
+    "- If the bottleneck is vectorized: can we optimize with numexpr?\n",
+    "- If the internal part of the loop can not be vectorized:\n",
+    "    - numba jit if simple enough otherwise cython\n",
+    "    - the loop itself can be sped up with numpy.vectorize\n",
+    "- If the bottleneck gets called too often: cache the result\n",
+    "- If even that fails: pycuda\n",
+    "- If that is too slow: learn to optimize cuda code or find an easier problem"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.6.2"
+  },
+  "toc": {
+   "colors": {
+    "hover_highlight": "#DAA520",
+    "running_highlight": "#FF0000",
+    "selected_highlight": "#FFD700"
+   },
+   "moveMenuLeft": true,
+   "nav_menu": {
+    "height": "179px",
+    "width": "252px"
+   },
+   "navigate_menu": true,
+   "number_sections": true,
+   "sideBar": true,
+   "threshold": 4,
+   "toc_cell": false,
+   "toc_section_display": "block",
+   "toc_window_display": true
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
-- 
GitLab