[med-svn] [python-multipletau] 01/02: Imported Upstream version 0.1.4

Alex Mestiashvili malex-guest at moszumanska.debian.org
Thu Apr 24 19:46:52 UTC 2014


This is an automated email from the git hooks/post-receive script.

malex-guest pushed a commit to branch master
in repository python-multipletau.

commit f10b637e2e564e60d43971cee784d32c51fd3029
Author: Alexandre Mestiashvili <alex at biotec.tu-dresden.de>
Date:   Thu Apr 24 19:08:30 2014 +0200

    Imported Upstream version 0.1.4
---
 .gitignore                                      |  39 ++
 CHANGELOG.md                                    |   8 +
 LICENSE                                         |  29 +
 MANIFEST.in                                     |   3 +
 README.md                                       |  29 +
 README.txt                                      |  16 +
 doc/Makefile                                    | 177 ++++++
 doc/README.md                                   |  17 +
 doc/_extensions/apigen.py                       | 427 ++++++++++++++
 doc/_extensions/copybutton.js                   |  51 ++
 doc/_extensions/docscrape.py                    | 497 +++++++++++++++++
 doc/_extensions/docscrape_sphinx.py             | 136 +++++
 doc/_extensions/github.py                       | 714 ++++++++++++++++++++++++
 doc/_extensions/hidden_code_block.py            | 128 +++++
 doc/_extensions/inheritance_diagram.py          | 407 ++++++++++++++
 doc/_extensions/ipython_console_highlighting.py | 114 ++++
 doc/_extensions/ipython_directive.py            |   0
 doc/_extensions/numpydoc.py                     | 116 ++++
 doc/conf.py                                     | 305 ++++++++++
 doc/index.rst                                   |  11 +
 doc/make.bat                                    | 242 ++++++++
 doc/make.py                                     |  14 +
 doc/testing_multipletau.png                     | Bin 0 -> 77102 bytes
 multipletau/__init__.py                         |  31 +
 multipletau/_multipletau.py                     | 466 ++++++++++++++++
 setup.cfg                                       |   5 +
 setup.py                                        |  24 +
 test/__init__.py                                | 138 +++++
 test/__main__.py                                | 108 ++++
 29 files changed, 4252 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b00b084
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,39 @@
+*.py[cod]
+
+# C extensions
+*.so
+
+# Packages
+*.egg
+*.egg-info
+dist
+build
+eggs
+parts
+bin
+var
+sdist
+develop-eggs
+.installed.cfg
+lib
+lib64
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.coverage
+.tox
+nosetests.xml
+
+# Translations
+*.mo
+
+# Mr Developer
+.mr.developer.cfg
+.project
+.pydevproject
+.settings*
+
+# doc build
+doc/_build/**
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..8044355
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,8 @@
+0.1.4
+- integer and boolean input types are now automatically converted to floats
+- `multipletau.correlate` now works with complex data types
+- `multipletau.correlate` now checks if input data are same objects
+- documentation now contains examples
+
+0.1.3
+- first non-cython implementation
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f2af26d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+Copyright (c) 2014 Paul Müller
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+   
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+
+  3. Neither the name of multipletau nor the names of its contributors 
+     may be used to endorse or promote products derived from this 
+     software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INFRAE OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..b2c1156
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,3 @@
+include CHANGELOG.md
+include README.txt
+include test/*.py
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..faf2982
--- /dev/null
+++ b/README.md
@@ -0,0 +1,29 @@
+multipletau
+===========
+
+This repo contains a multiple-tau algorithm for Python
+
+- **multipletau** multiple-tau package, implemented using [numpy](http://www.numpy.org/)
+- **test** testing the algorithm
+- **doc** the source of the [documentation](http://paulmueller.github.io/multipletau/)
+
+
+Installation
+============
+The package can be installed from the Python package index.
+
+    pip install multipletau
+
+
+Usage
+=====
+
+    >>> import numpy as np
+    >>> import multipletau
+    >>> a = np.linspace(2,5,42)
+    >>> v = np.linspace(1,6,42)
+    >>> multipletau.correlate(a, v, m=2)
+    array([[   1.        ,  549.87804878],
+           [   2.        ,  530.37477692],
+           [   4.        ,  491.85812017],
+           [   8.        ,  386.39500297]])
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..b8f0467
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,16 @@
+Multipe-tau correlation is computed on a logarithmic scale (less
+data points are computed) and is thus much faster than conventional
+correlation on a linear scale such as `numpy.correlate`.
+
+Reference
+=========
+The code is fully documented. An online reference is available
+at http://paulmueller.github.io/multipletau/.
+
+
+Installation
+============
+The package can be installed from the Python package index.
+
+    pip install multipletau
+
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 0000000..9ed47b9
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,177 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = _build
+
+# User-friendly check for sphinx-build
+ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
+$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
+endif
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html       to make standalone HTML files"
+	@echo "  dirhtml    to make HTML files named index.html in directories"
+	@echo "  singlehtml to make a single large HTML file"
+	@echo "  pickle     to make pickle files"
+	@echo "  json       to make JSON files"
+	@echo "  htmlhelp   to make HTML files and a HTML help project"
+	@echo "  qthelp     to make HTML files and a qthelp project"
+	@echo "  devhelp    to make HTML files and a Devhelp project"
+	@echo "  epub       to make an epub"
+	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
+	@echo "  latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
+	@echo "  text       to make text files"
+	@echo "  man        to make manual pages"
+	@echo "  texinfo    to make Texinfo files"
+	@echo "  info       to make Texinfo files and run them through makeinfo"
+	@echo "  gettext    to make PO message catalogs"
+	@echo "  changes    to make an overview of all changed/added/deprecated items"
+	@echo "  xml        to make Docutils-native XML files"
+	@echo "  pseudoxml  to make pseudoxml-XML files for display purposes"
+	@echo "  linkcheck  to check all external links for integrity"
+	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+	rm -rf $(BUILDDIR)/*
+
+html:
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+	@echo
+	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/multipletau.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/multipletau.qhc"
+
+devhelp:
+	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+	@echo
+	@echo "Build finished."
+	@echo "To view the help file:"
+	@echo "# mkdir -p $$HOME/.local/share/devhelp/multipletau"
+	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/multipletau"
+	@echo "# devhelp"
+
+epub:
+	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+	@echo
+	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make' in that directory to run these through (pdf)latex" \
+	      "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through pdflatex..."
+	$(MAKE) -C $(BUILDDIR)/latex all-pdf
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+latexpdfja:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through platex and dvipdfmx..."
+	$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+	@echo
+	@echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+	@echo
+	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo
+	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+	@echo "Run \`make' in that directory to run these through makeinfo" \
+	      "(use \`make info' here to do that automatically)."
+
+info:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo "Running Texinfo files through makeinfo..."
+	make -C $(BUILDDIR)/texinfo info
+	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+	@echo
+	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."
+
+xml:
+	$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
+	@echo
+	@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+
+pseudoxml:
+	$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
+	@echo
+	@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
diff --git a/doc/README.md b/doc/README.md
new file mode 100644
index 0000000..b0b54e4
--- /dev/null
+++ b/doc/README.md
@@ -0,0 +1,17 @@
+Documentation of multipletau
+============================
+
+In order to build the [documentation of multipletau](http://paulmueller.github.io/multipletau/) you need to have 
+[Sphinx](http://sphinx-doc.org/) installed.
+You can install it by using pip or via apt-get:
+
+    pip install sphinx  
+    apt-get install python-sphinx  
+    
+The documentation can be build with either of these commands:
+
+   python make.py  
+   make html  
+   make.bat (Windows)
+   
+    
diff --git a/doc/_extensions/apigen.py b/doc/_extensions/apigen.py
new file mode 100644
index 0000000..1237409
--- /dev/null
+++ b/doc/_extensions/apigen.py
@@ -0,0 +1,427 @@
+"""Attempt to generate templates for module reference with Sphinx
+
+XXX - we exclude extension modules
+
+To include extension modules, first identify them as valid in the
+``_uri2path`` method, then handle them in the ``_parse_module`` script.
+
+We get functions and classes by parsing the text of .py files.
+Alternatively we could import the modules for discovery, and we'd have
+to do that for extension modules.  This would involve changing the
+``_parse_module`` method to work via import and introspection, and
+might involve changing ``discover_modules`` (which determines which
+files are modules, and therefore which module URIs will be passed to
+``_parse_module``).
+
+NOTE: this is a modified version of a script originally shipped with the
+PyMVPA project, which we've adapted for NIPY use.  PyMVPA is an MIT-licensed
+project."""
+
+# Stdlib imports
+import os
+import re
+
+# Functions and classes
+class ApiDocWriter(object):
+    ''' Class for automatic detection and parsing of API docs
+    to Sphinx-parsable reST format'''
+
+    # only separating first two levels
+    rst_section_levels = ['*', '=', '-', '~', '^']
+
+    def __init__(self,
+                 package_name,
+                 rst_extension='.rst',
+                 package_skip_patterns=None,
+                 module_skip_patterns=None,
+                 ):
+        ''' Initialize package for parsing
+
+        Parameters
+        ----------
+        package_name : string
+            Name of the top-level package.  *package_name* must be the
+            name of an importable package
+        rst_extension : string, optional
+            Extension for reST files, default '.rst'
+        package_skip_patterns : None or sequence of {strings, regexps}
+            Sequence of strings giving URIs of packages to be excluded
+            Operates on the package path, starting at (including) the
+            first dot in the package path, after *package_name* - so,
+            if *package_name* is ``sphinx``, then ``sphinx.util`` will
+            result in ``.util`` being passed for earching by these
+            regexps.  If is None, gives default. Default is:
+            ['\.tests$']
+        module_skip_patterns : None or sequence
+            Sequence of strings giving URIs of modules to be excluded
+            Operates on the module name including preceding URI path,
+            back to the first dot after *package_name*.  For example
+            ``sphinx.util.console`` results in the string to search of
+            ``.util.console``
+            If is None, gives default. Default is:
+            ['\.setup$', '\._']
+        '''
+        if package_skip_patterns is None:
+            package_skip_patterns = ['\\.tests$']
+        if module_skip_patterns is None:
+            module_skip_patterns = ['\\.setup$', '\\._']
+        self.package_name = package_name
+        self.rst_extension = rst_extension
+        self.package_skip_patterns = package_skip_patterns
+        self.module_skip_patterns = module_skip_patterns
+
+    def get_package_name(self):
+        return self._package_name
+
+    def set_package_name(self, package_name):
+        ''' Set package_name
+
+        >>> docwriter = ApiDocWriter('sphinx')
+        >>> import sphinx
+        >>> docwriter.root_path == sphinx.__path__[0]
+        True
+        >>> docwriter.package_name = 'docutils'
+        >>> import docutils
+        >>> docwriter.root_path == docutils.__path__[0]
+        True
+        '''
+        # It's also possible to imagine caching the module parsing here
+        self._package_name = package_name
+        self.root_module = __import__(package_name)
+        self.root_path = self.root_module.__path__[0]
+        self.written_modules = None
+
+    package_name = property(get_package_name, set_package_name, None,
+                            'get/set package_name')
+
+    def _get_object_name(self, line):
+        ''' Get second token in line
+        >>> docwriter = ApiDocWriter('sphinx')
+        >>> docwriter._get_object_name("  def func():  ")
+        'func'
+        >>> docwriter._get_object_name("  class Klass(object):  ")
+        'Klass'
+        >>> docwriter._get_object_name("  class Klass:  ")
+        'Klass'
+        '''
+        name = line.split()[1].split('(')[0].strip()
+        # in case we have classes which are not derived from object
+        # ie. old style classes
+        return name.rstrip(':')
+
+    def _uri2path(self, uri):
+        ''' Convert uri to absolute filepath
+
+        Parameters
+        ----------
+        uri : string
+            URI of python module to return path for
+
+        Returns
+        -------
+        path : None or string
+            Returns None if there is no valid path for this URI
+            Otherwise returns absolute file system path for URI
+
+        Examples
+        --------
+        >>> docwriter = ApiDocWriter('sphinx')
+        >>> import sphinx
+        >>> modpath = sphinx.__path__[0]
+        >>> res = docwriter._uri2path('sphinx.builder')
+        >>> res == os.path.join(modpath, 'builder.py')
+        True
+        >>> res = docwriter._uri2path('sphinx')
+        >>> res == os.path.join(modpath, '__init__.py')
+        True
+        >>> docwriter._uri2path('sphinx.does_not_exist')
+
+        '''
+        if uri == self.package_name:
+            return os.path.join(self.root_path, '__init__.py')
+        path = uri.replace('.', os.path.sep)
+        path = path.replace(self.package_name + os.path.sep, '')
+        path = os.path.join(self.root_path, path)
+        # XXX maybe check for extensions as well?
+        if os.path.exists(path + '.py'): # file
+            path += '.py'
+        elif os.path.exists(os.path.join(path, '__init__.py')):
+            path = os.path.join(path, '__init__.py')
+        else:
+            return None
+        return path
+
+    def _path2uri(self, dirpath):
+        ''' Convert directory path to uri '''
+        relpath = dirpath.replace(self.root_path, self.package_name)
+        if relpath.startswith(os.path.sep):
+            relpath = relpath[1:]
+        return relpath.replace(os.path.sep, '.')
+
+    def _parse_module(self, uri):
+        ''' Parse module defined in *uri* '''
+        filename = self._uri2path(uri)
+        if filename is None:
+            # nothing that we could handle here.
+            return ([],[])
+        f = open(filename, 'rt')
+        functions, classes = self._parse_lines(f)
+        f.close()
+        return functions, classes
+    
+    def _parse_lines(self, linesource):
+        ''' Parse lines of text for functions and classes '''
+        functions = []
+        classes = []
+        for line in linesource:
+            if line.startswith('def ') and line.count('('):
+                # exclude private stuff
+                name = self._get_object_name(line)
+                if not name.startswith('_'):
+                    functions.append(name)
+            elif line.startswith('class '):
+                # exclude private stuff
+                name = self._get_object_name(line)
+                if not name.startswith('_'):
+                    classes.append(name)
+            else:
+                pass
+        functions.sort()
+        classes.sort()
+        return functions, classes
+
+    def generate_api_doc(self, uri):
+        '''Make autodoc documentation template string for a module
+
+        Parameters
+        ----------
+        uri : string
+            python location of module - e.g 'sphinx.builder'
+
+        Returns
+        -------
+        S : string
+            Contents of API doc
+        '''
+        # get the names of all classes and functions
+        functions, classes = self._parse_module(uri)
+        if not len(functions) and not len(classes):
+            print 'WARNING: Empty -',uri  # dbg
+            return ''
+
+        # Make a shorter version of the uri that omits the package name for
+        # titles 
+        uri_short = re.sub(r'^%s\.' % self.package_name,'',uri)
+        
+        ad = '.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n'
+
+        chap_title = uri_short
+        ad += (chap_title+'\n'+ self.rst_section_levels[1] * len(chap_title)
+               + '\n\n')
+
+        # Set the chapter title to read 'module' for all modules except for the
+        # main packages
+        if '.' in uri:
+            title = 'Module: :mod:`' + uri_short + '`'
+        else:
+            title = ':mod:`' + uri_short + '`'
+        ad += title + '\n' + self.rst_section_levels[2] * len(title)
+
+        if len(classes):
+            ad += '\nInheritance diagram for ``%s``:\n\n' % uri
+            ad += '.. inheritance-diagram:: %s \n' % uri
+            ad += '   :parts: 3\n'
+
+        ad += '\n.. automodule:: ' + uri + '\n'
+        ad += '\n.. currentmodule:: ' + uri + '\n'
+        multi_class = len(classes) > 1
+        multi_fx = len(functions) > 1
+        if multi_class:
+            ad += '\n' + 'Classes' + '\n' + \
+                  self.rst_section_levels[2] * 7 + '\n'
+        elif len(classes) and multi_fx:
+            ad += '\n' + 'Class' + '\n' + \
+                  self.rst_section_levels[2] * 5 + '\n'
+        for c in classes:
+            ad += '\n:class:`' + c + '`\n' \
+                  + self.rst_section_levels[multi_class + 2 ] * \
+                  (len(c)+9) + '\n\n'
+            ad += '\n.. autoclass:: ' + c + '\n'
+            # must NOT exclude from index to keep cross-refs working
+            ad += '  :members:\n' \
+                  '  :undoc-members:\n' \
+                  '  :show-inheritance:\n' \
+                  '  :inherited-members:\n' \
+                  '\n' \
+                  '  .. automethod:: __init__\n'
+        if multi_fx:
+            ad += '\n' + 'Functions' + '\n' + \
+                  self.rst_section_levels[2] * 9 + '\n\n'
+        elif len(functions) and multi_class:
+            ad += '\n' + 'Function' + '\n' + \
+                  self.rst_section_levels[2] * 8 + '\n\n'
+        for f in functions:
+            # must NOT exclude from index to keep cross-refs working
+            ad += '\n.. autofunction:: ' + uri + '.' + f + '\n\n'
+        return ad
+
+    def _survives_exclude(self, matchstr, match_type):
+        ''' Returns True if *matchstr* does not match patterns
+
+        ``self.package_name`` removed from front of string if present
+
+        Examples
+        --------
+        >>> dw = ApiDocWriter('sphinx')
+        >>> dw._survives_exclude('sphinx.okpkg', 'package')
+        True
+        >>> dw.package_skip_patterns.append('^\\.badpkg$')
+        >>> dw._survives_exclude('sphinx.badpkg', 'package')
+        False
+        >>> dw._survives_exclude('sphinx.badpkg', 'module')
+        True
+        >>> dw._survives_exclude('sphinx.badmod', 'module')
+        True
+        >>> dw.module_skip_patterns.append('^\\.badmod$')
+        >>> dw._survives_exclude('sphinx.badmod', 'module')
+        False
+        '''
+        if match_type == 'module':
+            patterns = self.module_skip_patterns
+        elif match_type == 'package':
+            patterns = self.package_skip_patterns
+        else:
+            raise ValueError('Cannot interpret match type "%s"' 
+                             % match_type)
+        # Match to URI without package name
+        L = len(self.package_name)
+        if matchstr[:L] == self.package_name:
+            matchstr = matchstr[L:]
+        for pat in patterns:
+            try:
+                pat.search
+            except AttributeError:
+                pat = re.compile(pat)
+            if pat.search(matchstr):
+                return False
+        return True
+
+    def discover_modules(self):
+        ''' Return module sequence discovered from ``self.package_name`` 
+
+
+        Parameters
+        ----------
+        None
+
+        Returns
+        -------
+        mods : sequence
+            Sequence of module names within ``self.package_name``
+
+        Examples
+        --------
+        >>> dw = ApiDocWriter('sphinx')
+        >>> mods = dw.discover_modules()
+        >>> 'sphinx.util' in mods
+        True
+        >>> dw.package_skip_patterns.append('\.util$')
+        >>> 'sphinx.util' in dw.discover_modules()
+        False
+        >>> 
+        '''
+        modules = [self.package_name]
+        # raw directory parsing
+        for dirpath, dirnames, filenames in os.walk(self.root_path):
+            # Check directory names for packages
+            root_uri = self._path2uri(os.path.join(self.root_path,
+                                                   dirpath))
+            for dirname in dirnames[:]: # copy list - we modify inplace
+                package_uri = '.'.join((root_uri, dirname))
+                if (self._uri2path(package_uri) and
+                    self._survives_exclude(package_uri, 'package')):
+                    modules.append(package_uri)
+                else:
+                    dirnames.remove(dirname)
+            # Check filenames for modules
+            for filename in filenames:
+                module_name = filename[:-3]
+                module_uri = '.'.join((root_uri, module_name))
+                if (self._uri2path(module_uri) and
+                    self._survives_exclude(module_uri, 'module')):
+                    modules.append(module_uri)
+        return sorted(modules)
+    
+    def write_modules_api(self, modules,outdir):
+        # write the list
+        written_modules = []
+        for m in modules:
+            api_str = self.generate_api_doc(m)
+            if not api_str:
+                continue
+            # write out to file
+            outfile = os.path.join(outdir,
+                                   m + self.rst_extension)
+            fileobj = open(outfile, 'wt')
+            fileobj.write(api_str)
+            fileobj.close()
+            written_modules.append(m)
+        self.written_modules = written_modules
+
+    def write_api_docs(self, outdir):
+        """Generate API reST files.
+
+        Parameters
+        ----------
+        outdir : string
+            Directory name in which to store files
+            We create automatic filenames for each module
+            
+        Returns
+        -------
+        None
+
+        Notes
+        -----
+        Sets self.written_modules to list of written modules
+        """
+        if not os.path.exists(outdir):
+            os.mkdir(outdir)
+        # compose list of modules
+        modules = self.discover_modules()
+        self.write_modules_api(modules,outdir)
+        
+    def write_index(self, outdir, froot='gen', relative_to=None):
+        """Make a reST API index file from written files
+
+        Parameters
+        ----------
+        path : string
+            Filename to write index to
+        outdir : string
+            Directory to which to write generated index file
+        froot : string, optional
+            root (filename without extension) of filename to write to
+            Defaults to 'gen'.  We add ``self.rst_extension``.
+        relative_to : string
+            path to which written filenames are relative.  This
+            component of the written file path will be removed from
+            outdir, in the generated index.  Default is None, meaning,
+            leave path as it is.
+        """
+        if self.written_modules is None:
+            raise ValueError('No modules written')
+        # Get full filename path
+        path = os.path.join(outdir, froot+self.rst_extension)
+        # Path written into index is relative to rootpath
+        if relative_to is not None:
+            relpath = outdir.replace(relative_to + os.path.sep, '')
+        else:
+            relpath = outdir
+        idx = open(path,'wt')
+        w = idx.write
+        w('.. AUTO-GENERATED FILE -- DO NOT EDIT!\n\n')
+        w('.. toctree::\n\n')
+        for f in self.written_modules:
+            w('   %s\n' % os.path.join(relpath,f))
+        idx.close()
diff --git a/doc/_extensions/copybutton.js b/doc/_extensions/copybutton.js
new file mode 100644
index 0000000..fe39f87
--- /dev/null
+++ b/doc/_extensions/copybutton.js
@@ -0,0 +1,51 @@
+$(document).ready(function() {
+    /* Add a [>>>] button on the top-right corner of code samples to hide
+     * the >>> and ... prompts and the output and thus make the code
+     * copyable. */
+    var div = $('.highlight-python .highlight,' +
+                '.highlight-python3 .highlight')
+    var pre = div.find('pre');
+    // get the styles from the current theme
+    var hide_text = 'Hide the prompts and output';
+    var show_text = 'Show the prompts and output';
+    var border_width = pre.css('border-top-width');
+    var border_style = pre.css('border-top-style');
+    var border_color = pre.css('border-top-color');
+    var button_styles = {
+        'cursor':'pointer', 'float': 'right', 'margin-top': '-6px', 'margin-right': '-5px',
+        'border-color': border_color, 'border-style': border_style,
+        'border-width': border_width, 'color': border_color, 'text-size': '75%',
+        'font-family': 'monospace', 'padding-left': '0.2em', 'padding-right': '0.2em'
+    }
+    // create and add the button to all the code blocks that contain >>>
+    div.each(function(index) {
+        var jthis = $(this);
+        if (jthis.find('.gp').length > 0) {
+            var button = $('<span class="copybutton">>>></span>');
+            button.css(button_styles)
+            button.attr('title', hide_text);
+            jthis.find('pre').prepend(button);
+        }
+        // tracebacks (.gt) contain bare text elements that need to be
+        // wrapped in a span to work with .nextUntil() (see later)
+        jthis.find('pre:has(.gt)').contents().filter(function() {
+            return ((this.nodeType == 3) && (this.data.trim().length > 0));
+        }).wrap('<span>');
+    });
+    // define the behavior of the button when it's clicked
+    $('.copybutton').toggle(
+        function() {
+            var button = $(this);
+            button.parent().find('.go, .gp, .gt').hide();
+            button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'hidden');
+            button.css('text-decoration', 'line-through');
+            button.attr('title', show_text);
+        },
+        function() {
+            var button = $(this);
+            button.parent().find('.go, .gp, .gt').show();
+            button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'visible');
+            button.css('text-decoration', 'none');
+            button.attr('title', hide_text);
+        });
+});
diff --git a/doc/_extensions/docscrape.py b/doc/_extensions/docscrape.py
new file mode 100644
index 0000000..f374b3d
--- /dev/null
+++ b/doc/_extensions/docscrape.py
@@ -0,0 +1,497 @@
+"""Extract reference documentation from the NumPy source tree.
+
+"""
+
+import inspect
+import textwrap
+import re
+import pydoc
+from StringIO import StringIO
+from warnings import warn
+4
+class Reader(object):
+    """A line-based string reader.
+
+    """
+    def __init__(self, data):
+        """
+        Parameters
+        ----------
+        data : str
+           String with lines separated by '\n'.
+
+        """
+        if isinstance(data,list):
+            self._str = data
+        else:
+            self._str = data.split('\n') # store string as list of lines
+
+        self.reset()
+
+    def __getitem__(self, n):
+        return self._str[n]
+
+    def reset(self):
+        self._l = 0 # current line nr
+
+    def read(self):
+        if not self.eof():
+            out = self[self._l]
+            self._l += 1
+            return out
+        else:
+            return ''
+
+    def seek_next_non_empty_line(self):
+        for l in self[self._l:]:
+            if l.strip():
+                break
+            else:
+                self._l += 1
+
+    def eof(self):
+        return self._l >= len(self._str)
+
+    def read_to_condition(self, condition_func):
+        start = self._l
+        for line in self[start:]:
+            if condition_func(line):
+                return self[start:self._l]
+            self._l += 1
+            if self.eof():
+                return self[start:self._l+1]
+        return []
+
+    def read_to_next_empty_line(self):
+        self.seek_next_non_empty_line()
+        def is_empty(line):
+            return not line.strip()
+        return self.read_to_condition(is_empty)
+
+    def read_to_next_unindented_line(self):
+        def is_unindented(line):
+            return (line.strip() and (len(line.lstrip()) == len(line)))
+        return self.read_to_condition(is_unindented)
+
+    def peek(self,n=0):
+        if self._l + n < len(self._str):
+            return self[self._l + n]
+        else:
+            return ''
+
+    def is_empty(self):
+        return not ''.join(self._str).strip()
+
+
+class NumpyDocString(object):
+    def __init__(self,docstring):
+        docstring = textwrap.dedent(docstring).split('\n')
+
+        self._doc = Reader(docstring)
+        self._parsed_data = {
+            'Signature': '',
+            'Summary': [''],
+            'Extended Summary': [],
+            'Parameters': [],
+            'Returns': [],
+            'Raises': [],
+            'Warns': [],
+            'Other Parameters': [],
+            'Attributes': [],
+            'Methods': [],
+            'See Also': [],
+            'Notes': [],
+            'Warnings': [],
+            'References': '',
+            'Examples': '',
+            'index': {}
+            }
+
+        self._parse()
+
+    def __getitem__(self,key):
+        return self._parsed_data[key]
+
+    def __setitem__(self,key,val):
+        if not self._parsed_data.has_key(key):
+            warn("Unknown section %s" % key)
+        else:
+            self._parsed_data[key] = val
+
+    def _is_at_section(self):
+        self._doc.seek_next_non_empty_line()
+
+        if self._doc.eof():
+            return False
+
+        l1 = self._doc.peek().strip()  # e.g. Parameters
+
+        if l1.startswith('.. index::'):
+            return True
+
+        l2 = self._doc.peek(1).strip() #    ---------- or ==========
+        return l2.startswith('-'*len(l1)) or l2.startswith('='*len(l1))
+
+    def _strip(self,doc):
+        i = 0
+        j = 0
+        for i,line in enumerate(doc):
+            if line.strip(): break
+
+        for j,line in enumerate(doc[::-1]):
+            if line.strip(): break
+
+        return doc[i:len(doc)-j]
+
+    def _read_to_next_section(self):
+        section = self._doc.read_to_next_empty_line()
+
+        while not self._is_at_section() and not self._doc.eof():
+            if not self._doc.peek(-1).strip(): # previous line was empty
+                section += ['']
+
+            section += self._doc.read_to_next_empty_line()
+
+        return section
+
+    def _read_sections(self):
+        while not self._doc.eof():
+            data = self._read_to_next_section()
+            name = data[0].strip()
+
+            if name.startswith('..'): # index section
+                yield name, data[1:]
+            elif len(data) < 2:
+                yield StopIteration
+            else:
+                yield name, self._strip(data[2:])
+
+    def _parse_param_list(self,content):
+        r = Reader(content)
+        params = []
+        while not r.eof():
+            header = r.read().strip()
+            if ' : ' in header:
+                arg_name, arg_type = header.split(' : ')[:2]
+            else:
+                arg_name, arg_type = header, ''
+
+            desc = r.read_to_next_unindented_line()
+            desc = dedent_lines(desc)
+
+            params.append((arg_name,arg_type,desc))
+
+        return params
+
+    
+    _name_rgx = re.compile(r"^\s*(:(?P<role>\w+):`(?P<name>[a-zA-Z0-9_.-]+)`|"
+                           r" (?P<name2>[a-zA-Z0-9_.-]+))\s*", re.X)
+    def _parse_see_also(self, content):
+        """
+        func_name : Descriptive text
+            continued text
+        another_func_name : Descriptive text
+        func_name1, func_name2, :meth:`func_name`, func_name3
+
+        """
+        items = []
+
+        def parse_item_name(text):
+            """Match ':role:`name`' or 'name'"""
+            m = self._name_rgx.match(text)
+            if m:
+                g = m.groups()
+                if g[1] is None:
+                    return g[3], None
+                else:
+                    return g[2], g[1]
+            raise ValueError("%s is not a item name" % text)
+
+        def push_item(name, rest):
+            if not name:
+                return
+            name, role = parse_item_name(name)
+            items.append((name, list(rest), role))
+            del rest[:]
+
+        current_func = None
+        rest = []
+        
+        for line in content:
+            if not line.strip(): continue
+
+            m = self._name_rgx.match(line)
+            if m and line[m.end():].strip().startswith(':'):
+                push_item(current_func, rest)
+                current_func, line = line[:m.end()], line[m.end():]
+                rest = [line.split(':', 1)[1].strip()]
+                if not rest[0]:
+                    rest = []
+            elif not line.startswith(' '):
+                push_item(current_func, rest)
+                current_func = None
+                if ',' in line:
+                    for func in line.split(','):
+                        push_item(func, [])
+                elif line.strip():
+                    current_func = line
+            elif current_func is not None:
+                rest.append(line.strip())
+        push_item(current_func, rest)
+        return items
+
+    def _parse_index(self, section, content):
+        """
+        .. index: default
+           :refguide: something, else, and more
+
+        """
+        def strip_each_in(lst):
+            return [s.strip() for s in lst]
+
+        out = {}
+        section = section.split('::')
+        if len(section) > 1:
+            out['default'] = strip_each_in(section[1].split(','))[0]
+        for line in content:
+            line = line.split(':')
+            if len(line) > 2:
+                out[line[1]] = strip_each_in(line[2].split(','))
+        return out
+    
+    def _parse_summary(self):
+        """Grab signature (if given) and summary"""
+        if self._is_at_section():
+            return
+
+        summary = self._doc.read_to_next_empty_line()
+        summary_str = " ".join([s.strip() for s in summary]).strip()
+        if re.compile('^([\w., ]+=)?\s*[\w\.]+\(.*\)$').match(summary_str):
+            self['Signature'] = summary_str
+            if not self._is_at_section():
+                self['Summary'] = self._doc.read_to_next_empty_line()
+        else:
+            self['Summary'] = summary
+
+        if not self._is_at_section():
+            self['Extended Summary'] = self._read_to_next_section()
+    
+    def _parse(self):
+        self._doc.reset()
+        self._parse_summary()
+
+        for (section,content) in self._read_sections():
+            if not section.startswith('..'):
+                section = ' '.join([s.capitalize() for s in section.split(' ')])
+            if section in ('Parameters', 'Attributes', 'Methods',
+                           'Returns', 'Raises', 'Warns'):
+                self[section] = self._parse_param_list(content)
+            elif section.startswith('.. index::'):
+                self['index'] = self._parse_index(section, content)
+            elif section == 'See Also':
+                self['See Also'] = self._parse_see_also(content)
+            else:
+                self[section] = content
+
+    # string conversion routines
+
+    def _str_header(self, name, symbol='-'):
+        return [name, len(name)*symbol]
+
+    def _str_indent(self, doc, indent=4):
+        out = []
+        for line in doc:
+            out += [' '*indent + line]
+        return out
+
+    def _str_signature(self):
+        if self['Signature']:
+            return [self['Signature'].replace('*','\*')] + ['']
+        else:
+            return ['']
+
+    def _str_summary(self):
+        if self['Summary']:
+            return self['Summary'] + ['']
+        else:
+            return []
+
+    def _str_extended_summary(self):
+        if self['Extended Summary']:
+            return self['Extended Summary'] + ['']
+        else:
+            return []
+
+    def _str_param_list(self, name):
+        out = []
+        if self[name]:
+            out += self._str_header(name)
+            for param,param_type,desc in self[name]:
+                out += ['%s : %s' % (param, param_type)]
+                out += self._str_indent(desc)
+            out += ['']
+        return out
+
+    def _str_section(self, name):
+        out = []
+        if self[name]:
+            out += self._str_header(name)
+            out += self[name]
+            out += ['']
+        return out
+
+    def _str_see_also(self, func_role):
+        if not self['See Also']: return []
+        out = []
+        out += self._str_header("See Also")
+        last_had_desc = True
+        for func, desc, role in self['See Also']:
+            if role:
+                link = ':%s:`%s`' % (role, func)
+            elif func_role:
+                link = ':%s:`%s`' % (func_role, func)
+            else:
+                link = "`%s`_" % func
+            if desc or last_had_desc:
+                out += ['']
+                out += [link]
+            else:
+                out[-1] += ", %s" % link
+            if desc:
+                out += self._str_indent([' '.join(desc)])
+                last_had_desc = True
+            else:
+                last_had_desc = False
+        out += ['']
+        return out
+
+    def _str_index(self):
+        idx = self['index']
+        out = []
+        out += ['.. index:: %s' % idx.get('default','')]
+        for section, references in idx.iteritems():
+            if section == 'default':
+                continue
+            out += ['   :%s: %s' % (section, ', '.join(references))]
+        return out
+
+    def __str__(self, func_role=''):
+        out = []
+        out += self._str_signature()
+        out += self._str_summary()
+        out += self._str_extended_summary()
+        for param_list in ('Parameters','Returns','Raises'):
+            out += self._str_param_list(param_list)
+        out += self._str_section('Warnings')
+        out += self._str_see_also(func_role)
+        for s in ('Notes','References','Examples'):
+            out += self._str_section(s)
+        out += self._str_index()
+        return '\n'.join(out)
+
+
+def indent(str,indent=4):
+    indent_str = ' '*indent
+    if str is None:
+        return indent_str
+    lines = str.split('\n')
+    return '\n'.join(indent_str + l for l in lines)
+
+def dedent_lines(lines):
+    """Deindent a list of lines maximally"""
+    return textwrap.dedent("\n".join(lines)).split("\n")
+
+def header(text, style='-'):
+    return text + '\n' + style*len(text) + '\n'
+
+
+class FunctionDoc(NumpyDocString):
+    def __init__(self, func, role='func', doc=None):
+        self._f = func
+        self._role = role # e.g. "func" or "meth"
+        if doc is None:
+            doc = inspect.getdoc(func) or ''
+        try:
+            NumpyDocString.__init__(self, doc)
+        except ValueError, e:
+            print '*'*78
+            print "ERROR: '%s' while parsing `%s`" % (e, self._f)
+            print '*'*78
+            #print "Docstring follows:"
+            #print doclines
+            #print '='*78
+
+        if not self['Signature']:
+            func, func_name = self.get_func()
+            try:
+                # try to read signature
+                argspec = inspect.getargspec(func)
+                argspec = inspect.formatargspec(*argspec)
+                argspec = argspec.replace('*','\*')
+                signature = '%s%s' % (func_name, argspec)
+            except TypeError, e:
+                signature = '%s()' % func_name
+            self['Signature'] = signature
+
+    def get_func(self):
+        func_name = getattr(self._f, '__name__', self.__class__.__name__)
+        if inspect.isclass(self._f):
+            func = getattr(self._f, '__call__', self._f.__init__)
+        else:
+            func = self._f
+        return func, func_name
+            
+    def __str__(self):
+        out = ''
+
+        func, func_name = self.get_func()
+        signature = self['Signature'].replace('*', '\*')
+
+        roles = {'func': 'function',
+                 'meth': 'method'}
+
+        if self._role:
+            if not roles.has_key(self._role):
+                print "Warning: invalid role %s" % self._role
+            out += '.. %s:: %s\n    \n\n' % (roles.get(self._role,''),
+                                             func_name)
+
+        out += super(FunctionDoc, self).__str__(func_role=self._role)
+        return out
+
+
+class ClassDoc(NumpyDocString):
+    def __init__(self,cls,modulename='',func_doc=FunctionDoc,doc=None):
+        if not inspect.isclass(cls):
+            raise ValueError("Initialise using a class. Got %r" % cls)
+        self._cls = cls
+
+        if modulename and not modulename.endswith('.'):
+            modulename += '.'
+        self._mod = modulename
+        self._name = cls.__name__
+        self._func_doc = func_doc
+
+        if doc is None:
+            doc = pydoc.getdoc(cls)
+
+        NumpyDocString.__init__(self, doc)
+
+    @property
+    def methods(self):
+        return [name for name,func in inspect.getmembers(self._cls)
+                if not name.startswith('_') and callable(func)]
+
+    def __str__(self):
+        out = ''
+        out += super(ClassDoc, self).__str__()
+        out += "\n\n"
+
+        #for m in self.methods:
+        #    print "Parsing `%s`" % m
+        #    out += str(self._func_doc(getattr(self._cls,m), 'meth')) + '\n\n'
+        #    out += '.. index::\n   single: %s; %s\n\n' % (self._name, m)
+
+        return out
+
+
diff --git a/doc/_extensions/docscrape_sphinx.py b/doc/_extensions/docscrape_sphinx.py
new file mode 100644
index 0000000..77ed271
--- /dev/null
+++ b/doc/_extensions/docscrape_sphinx.py
@@ -0,0 +1,136 @@
+import re, inspect, textwrap, pydoc
+from docscrape import NumpyDocString, FunctionDoc, ClassDoc
+
+class SphinxDocString(NumpyDocString):
+    # string conversion routines
+    def _str_header(self, name, symbol='`'):
+        return ['.. rubric:: ' + name, '']
+
+    def _str_field_list(self, name):
+        return [':' + name + ':']
+
+    def _str_indent(self, doc, indent=4):
+        out = []
+        for line in doc:
+            out += [' '*indent + line]
+        return out
+
+    def _str_signature(self):
+        return ['']
+        if self['Signature']:
+            return ['``%s``' % self['Signature']] + ['']
+        else:
+            return ['']
+
+    def _str_summary(self):
+        return self['Summary'] + ['']
+
+    def _str_extended_summary(self):
+        return self['Extended Summary'] + ['']
+
+    def _str_param_list(self, name):
+        out = []
+        if self[name]:
+            out += self._str_field_list(name)
+            out += ['']
+            for param,param_type,desc in self[name]:
+                out += self._str_indent(['**%s** : %s' % (param.strip(),
+                                                          param_type)])
+                out += ['']
+                out += self._str_indent(desc,8)
+                out += ['']
+        return out
+
+    def _str_section(self, name):
+        out = []
+        if self[name]:
+            out += self._str_header(name)
+            out += ['']
+            content = textwrap.dedent("\n".join(self[name])).split("\n")
+            out += content
+            out += ['']
+        return out
+
+    def _str_see_also(self, func_role):
+        out = []
+        if self['See Also']:
+            see_also = super(SphinxDocString, self)._str_see_also(func_role)
+            out = ['.. seealso::', '']
+            out += self._str_indent(see_also[2:])
+        return out
+
+    def _str_warnings(self):
+        out = []
+        if self['Warnings']:
+            out = ['.. warning::', '']
+            out += self._str_indent(self['Warnings'])
+        return out
+
+    def _str_index(self):
+        idx = self['index']
+        out = []
+        if len(idx) == 0:
+            return out
+
+        out += ['.. index:: %s' % idx.get('default','')]
+        for section, references in idx.iteritems():
+            if section == 'default':
+                continue
+            elif section == 'refguide':
+                out += ['   single: %s' % (', '.join(references))]
+            else:
+                out += ['   %s: %s' % (section, ','.join(references))]
+        return out
+
+    def _str_references(self):
+        out = []
+        if self['References']:
+            out += self._str_header('References')
+            if isinstance(self['References'], str):
+                self['References'] = [self['References']]
+            out.extend(self['References'])
+            out += ['']
+        return out
+
+    def __str__(self, indent=0, func_role="obj"):
+        out = []
+        out += self._str_signature()
+        out += self._str_index() + ['']
+        out += self._str_summary()
+        out += self._str_extended_summary()
+        for param_list in ('Parameters', 'Attributes', 'Methods',
+                           'Returns','Raises'):
+            out += self._str_param_list(param_list)
+        out += self._str_warnings()
+        out += self._str_see_also(func_role)
+        out += self._str_section('Notes')
+        out += self._str_references()
+        out += self._str_section('Examples')
+        out = self._str_indent(out,indent)
+        return '\n'.join(out)
+
+class SphinxFunctionDoc(SphinxDocString, FunctionDoc):
+    pass
+
+class SphinxClassDoc(SphinxDocString, ClassDoc):
+    pass
+
+def get_doc_object(obj, what=None, doc=None):
+    if what is None:
+        if inspect.isclass(obj):
+            what = 'class'
+        elif inspect.ismodule(obj):
+            what = 'module'
+        elif callable(obj):
+            what = 'function'
+        else:
+            what = 'object'
+    if what == 'class':
+        return SphinxClassDoc(obj, '', func_doc=SphinxFunctionDoc, doc=doc)
+    elif what in ('function', 'method'):
+        return SphinxFunctionDoc(obj, '', doc=doc)
+    else:
+        if doc is None:
+            doc = pydoc.getdoc(obj)
+        return SphinxDocString(doc)
+
diff --git a/doc/_extensions/github.py b/doc/_extensions/github.py
new file mode 100644
index 0000000..a19f599
--- /dev/null
+++ b/doc/_extensions/github.py
@@ -0,0 +1,714 @@
+  
+
+
+<!DOCTYPE html>
+<html>
+  <head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# githubog: http://ogp.me/ns/fb/githubog#">
+    <meta charset='utf-8'>
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+        <title>ipython/docs/sphinxext/github.py at master · ipython/ipython · GitHub</title>
+    <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="GitHub" />
+    <link rel="fluid-icon" href="https://github.com/fluidicon.png" title="GitHub" />
+    <link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-114.png" />
+    <link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114.png" />
+    <link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-144.png" />
+    <link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144.png" />
+    <link rel="logo" type="image/svg" href="http://github-media-downloads.s3.amazonaws.com/github-logo.svg" />
+    <link rel="xhr-socket" href="/_sockets" />
+
+
+    <meta name="msapplication-TileImage" content="/windows-tile.png" />
+    <meta name="msapplication-TileColor" content="#ffffff" />
+    <meta name="selected-link" value="repo_source" data-pjax-transient />
+    <meta content="collector.githubapp.com" name="octolytics-host" /><meta content="github" name="octolytics-app-id" />
+
+    
+    
+    <link rel="icon" type="image/x-icon" href="/favicon.ico" />
+
+    <meta content="authenticity_token" name="csrf-param" />
+<meta content="KxkHbu6uuzb5nYpICaiM/tuRIpoRIcTZQEgfU8NliZg=" name="csrf-token" />
+
+    <link href="https://a248.e.akamai.net/assets.github.com/assets/github-d63f89e307e2e357d3b7160b3cd4020463f9bbc1.css" media="all" rel="stylesheet" type="text/css" />
+    <link href="https://a248.e.akamai.net/assets.github.com/assets/github2-4a2696ec075bd8d27843df00793c7e9d6525dded.css" media="all" rel="stylesheet" type="text/css" />
+    
+
+
+      <script src="https://a248.e.akamai.net/assets.github.com/assets/frameworks-92d138f450f2960501e28397a2f63b0f100590f0.js" type="text/javascript"></script>
+      <script src="https://a248.e.akamai.net/assets.github.com/assets/github-60bb3beedc339be272bd2acfce1cae3b79371737.js" type="text/javascript"></script>
+      
+      <meta http-equiv="x-pjax-version" content="7159fafc2fc92e02281814323bde3687">
+
+        <link data-pjax-transient rel='permalink' href='/ipython/ipython/blob/831d4eb049fa74f8be85ea7495a9724cb754390f/docs/sphinxext/github.py'>
+    <meta property="og:title" content="ipython"/>
+    <meta property="og:type" content="githubog:gitrepository"/>
+    <meta property="og:url" content="https://github.com/ipython/ipython"/>
+    <meta property="og:image" content="https://secure.gravatar.com/avatar/f72497397dd9a0a79c654c8182460bb1?s=420&d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png"/>
+    <meta property="og:site_name" content="GitHub"/>
+    <meta property="og:description" content="ipython - Official repository for IPython itself. Other repos in the IPython organization contain things like the website, documentation builds, etc."/>
+    <meta property="twitter:card" content="summary"/>
+    <meta property="twitter:site" content="@GitHub">
+    <meta property="twitter:title" content="ipython/ipython"/>
+
+    <meta name="description" content="ipython - Official repository for IPython itself. Other repos in the IPython organization contain things like the website, documentation builds, etc." />
+
+
+    <meta content="230453" name="octolytics-dimension-user_id" /><meta content="658518" name="octolytics-dimension-repository_id" />
+  <link href="https://github.com/ipython/ipython/commits/master.atom" rel="alternate" title="Recent Commits to ipython:master" type="application/atom+xml" />
+
+  </head>
+
+
+  <body class="logged_out page-blob windows vis-public env-production  ">
+    <div id="wrapper">
+
+      
+
+      
+      
+      
+
+      
+      <div class="header header-logged-out">
+  <div class="container clearfix">
+
+    <a class="header-logo-wordmark" href="https://github.com/">Github</a>
+
+    <div class="header-actions">
+      <a class="button primary" href="https://github.com/signup">Sign up</a>
+      <a class="button" href="https://github.com/login?return_to=%2Fipython%2Fipython%2Fblob%2Fmaster%2Fdocs%2Fsphinxext%2Fgithub.py">Sign in</a>
+    </div>
+
+    <div class="command-bar js-command-bar  in-repository">
+
+
+      <ul class="top-nav">
+          <li class="explore"><a href="https://github.com/explore">Explore</a></li>
+        <li class="features"><a href="https://github.com/features">Features</a></li>
+          <li class="blog"><a href="https://github.com/blog">Blog</a></li>
+      </ul>
+        <form accept-charset="UTF-8" action="/search" class="command-bar-form" id="top_search_form" method="get">
+  <a href="/search/advanced" class="advanced-search-icon tooltipped downwards command-bar-search" id="advanced_search" title="Advanced search"><span class="octicon octicon-gear "></span></a>
+
+  <input type="text" data-hotkey="/ s" name="q" id="js-command-bar-field" placeholder="Search or type a command" tabindex="1" autocapitalize="off"
+    
+      data-repo="ipython/ipython"
+      data-branch="master"
+      data-sha="26bb5f152ca86cb76eb66799c54e217638cd4b6d"
+  >
+
+    <input type="hidden" name="nwo" value="ipython/ipython" />
+
+    <div class="select-menu js-menu-container js-select-menu search-context-select-menu">
+      <span class="minibutton select-menu-button js-menu-target">
+        <span class="js-select-button">This repository</span>
+      </span>
+
+      <div class="select-menu-modal-holder js-menu-content js-navigation-container">
+        <div class="select-menu-modal">
+
+          <div class="select-menu-item js-navigation-item selected">
+            <span class="select-menu-item-icon octicon octicon-check"></span>
+            <input type="radio" class="js-search-this-repository" name="search_target" value="repository" checked="checked" />
+            <div class="select-menu-item-text js-select-button-text">This repository</div>
+          </div> <!-- /.select-menu-item -->
+
+          <div class="select-menu-item js-navigation-item">
+            <span class="select-menu-item-icon octicon octicon-check"></span>
+            <input type="radio" name="search_target" value="global" />
+            <div class="select-menu-item-text js-select-button-text">All repositories</div>
+          </div> <!-- /.select-menu-item -->
+
+        </div>
+      </div>
+    </div>
+
+  <span class="octicon help tooltipped downwards" title="Show command bar help">
+    <span class="octicon octicon-question"></span>
+  </span>
+
+
+  <input type="hidden" name="ref" value="cmdform">
+
+  <div class="divider-vertical"></div>
+
+</form>
+    </div>
+
+  </div>
+</div>
+
+
+      
+
+      
+
+
+            <div class="site hfeed" itemscope itemtype="http://schema.org/WebPage">
+      <div class="hentry">
+        
+        <div class="pagehead repohead instapaper_ignore readability-menu ">
+          <div class="container">
+            <div class="title-actions-bar">
+              
+
+<ul class="pagehead-actions">
+
+
+
+    <li>
+      <a href="/login?return_to=%2Fipython%2Fipython"
+        class="minibutton js-toggler-target star-button entice tooltipped upwards"
+        title="You must be signed in to use this feature" rel="nofollow">
+        <span class="octicon octicon-star"></span>Star
+      </a>
+      <a class="social-count js-social-count" href="/ipython/ipython/stargazers">
+        1,895
+      </a>
+    </li>
+    <li>
+      <a href="/login?return_to=%2Fipython%2Fipython"
+        class="minibutton js-toggler-target fork-button entice tooltipped upwards"
+        title="You must be signed in to fork a repository" rel="nofollow">
+        <span class="octicon octicon-git-branch"></span>Fork
+      </a>
+      <a href="/ipython/ipython/network" class="social-count">
+        586
+      </a>
+    </li>
+</ul>
+
+              <h1 itemscope itemtype="http://data-vocabulary.org/Breadcrumb" class="entry-title public">
+                <span class="repo-label"><span>public</span></span>
+                <span class="mega-octicon octicon-repo"></span>
+                <span class="author vcard">
+                  <a href="/ipython" class="url fn" itemprop="url" rel="author">
+                  <span itemprop="title">ipython</span>
+                  </a></span> /
+                <strong><a href="/ipython/ipython" class="js-current-repository">ipython</a></strong>
+              </h1>
+            </div>
+
+            
+  <ul class="tabs">
+    <li class="pulse-nav"><a href="/ipython/ipython/pulse" class="js-selected-navigation-item " data-selected-links="pulse /ipython/ipython/pulse" rel="nofollow"><span class="octicon octicon-pulse"></span></a></li>
+    <li><a href="/ipython/ipython" class="js-selected-navigation-item selected" data-selected-links="repo_source repo_downloads repo_commits repo_tags repo_branches /ipython/ipython">Code</a></li>
+    <li><a href="/ipython/ipython/network" class="js-selected-navigation-item " data-selected-links="repo_network /ipython/ipython/network">Network</a></li>
+    <li><a href="/ipython/ipython/pulls" class="js-selected-navigation-item " data-selected-links="repo_pulls /ipython/ipython/pulls">Pull Requests <span class='counter'>15</span></a></li>
+
+      <li><a href="/ipython/ipython/issues" class="js-selected-navigation-item " data-selected-links="repo_issues /ipython/ipython/issues">Issues <span class='counter'>567</span></a></li>
+
+      <li><a href="/ipython/ipython/wiki" class="js-selected-navigation-item " data-selected-links="repo_wiki /ipython/ipython/wiki">Wiki</a></li>
+
+
+    <li><a href="/ipython/ipython/graphs" class="js-selected-navigation-item " data-selected-links="repo_graphs repo_contributors /ipython/ipython/graphs">Graphs</a></li>
+
+
+  </ul>
+  
+<div class="tabnav">
+
+  <span class="tabnav-right">
+    <ul class="tabnav-tabs">
+          <li><a href="/ipython/ipython/tags" class="js-selected-navigation-item tabnav-tab" data-selected-links="repo_tags /ipython/ipython/tags">Tags <span class="counter ">13</span></a></li>
+    </ul>
+  </span>
+
+  <div class="tabnav-widget scope">
+
+
+    <div class="select-menu js-menu-container js-select-menu js-branch-menu">
+      <a class="minibutton select-menu-button js-menu-target" data-hotkey="w" data-ref="master">
+        <span class="octicon octicon-branch"></span>
+        <i>branch:</i>
+        <span class="js-select-button">master</span>
+      </a>
+
+      <div class="select-menu-modal-holder js-menu-content js-navigation-container">
+
+        <div class="select-menu-modal">
+          <div class="select-menu-header">
+            <span class="select-menu-title">Switch branches/tags</span>
+            <span class="octicon octicon-remove-close js-menu-close"></span>
+          </div> <!-- /.select-menu-header -->
+
+          <div class="select-menu-filters">
+            <div class="select-menu-text-filter">
+              <input type="text" id="commitish-filter-field" class="js-filterable-field js-navigation-enable" placeholder="Filter branches/tags">
+            </div>
+            <div class="select-menu-tabs">
+              <ul>
+                <li class="select-menu-tab">
+                  <a href="#" data-tab-filter="branches" class="js-select-menu-tab">Branches</a>
+                </li>
+                <li class="select-menu-tab">
+                  <a href="#" data-tab-filter="tags" class="js-select-menu-tab">Tags</a>
+                </li>
+              </ul>
+            </div><!-- /.select-menu-tabs -->
+          </div><!-- /.select-menu-filters -->
+
+          <div class="select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket css-truncate" data-tab-filter="branches">
+
+            <div data-filterable-for="commitish-filter-field" data-filterable-type="substring">
+
+                <div class="select-menu-item js-navigation-item ">
+                  <span class="select-menu-item-icon octicon octicon-check"></span>
+                  <a href="/ipython/ipython/blob/0.10.2/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="0.10.2" rel="nofollow" title="0.10.2">0.10.2</a>
+                </div> <!-- /.select-menu-item -->
+                <div class="select-menu-item js-navigation-item ">
+                  <span class="select-menu-item-icon octicon octicon-check"></span>
+                  <a href="/ipython/ipython/blob/0.12.1/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="0.12.1" rel="nofollow" title="0.12.1">0.12.1</a>
+                </div> <!-- /.select-menu-item -->
+                <div class="select-menu-item js-navigation-item ">
+                  <span class="select-menu-item-icon octicon octicon-check"></span>
+                  <a href="/ipython/ipython/blob/0.13.x/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="0.13.x" rel="nofollow" title="0.13.x">0.13.x</a>
+                </div> <!-- /.select-menu-item -->
+                <div class="select-menu-item js-navigation-item selected">
+                  <span class="select-menu-item-icon octicon octicon-check"></span>
+                  <a href="/ipython/ipython/blob/master/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="master" rel="nofollow" title="master">master</a>
+                </div> <!-- /.select-menu-item -->
+            </div>
+
+              <div class="select-menu-no-results">Nothing to show</div>
+          </div> <!-- /.select-menu-list -->
+
+
+          <div class="select-menu-list select-menu-tab-bucket js-select-menu-tab-bucket css-truncate" data-tab-filter="tags">
+            <div data-filterable-for="commitish-filter-field" data-filterable-type="substring">
+
+                <div class="select-menu-item js-navigation-item ">
+                  <span class="select-menu-item-icon octicon octicon-check"></span>
+                  <a href="/ipython/ipython/blob/rel-0.13.2/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.13.2" rel="nofollow" title="rel-0.13.2">rel-0.13.2</a>
+                </div> <!-- /.select-menu-item -->
+                <div class="select-menu-item js-navigation-item ">
+                  <span class="select-menu-item-icon octicon octicon-check"></span>
+                  <a href="/ipython/ipython/blob/rel-0.13.1/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.13.1" rel="nofollow" title="rel-0.13.1">rel-0.13.1</a>
+                </div> <!-- /.select-menu-item -->
+                <div class="select-menu-item js-navigation-item ">
+                  <span class="select-menu-item-icon octicon octicon-check"></span>
+                  <a href="/ipython/ipython/blob/rel-0.13/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.13" rel="nofollow" title="rel-0.13">rel-0.13</a>
+                </div> <!-- /.select-menu-item -->
+                <div class="select-menu-item js-navigation-item ">
+                  <span class="select-menu-item-icon octicon octicon-check"></span>
+                  <a href="/ipython/ipython/blob/rel-0.12.1/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.12.1" rel="nofollow" title="rel-0.12.1">rel-0.12.1</a>
+                </div> <!-- /.select-menu-item -->
+                <div class="select-menu-item js-navigation-item ">
+                  <span class="select-menu-item-icon octicon octicon-check"></span>
+                  <a href="/ipython/ipython/blob/rel-0.12/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.12" rel="nofollow" title="rel-0.12">rel-0.12</a>
+                </div> <!-- /.select-menu-item -->
+                <div class="select-menu-item js-navigation-item ">
+                  <span class="select-menu-item-icon octicon octicon-check"></span>
+                  <a href="/ipython/ipython/blob/rel-0.11/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.11" rel="nofollow" title="rel-0.11">rel-0.11</a>
+                </div> <!-- /.select-menu-item -->
+                <div class="select-menu-item js-navigation-item ">
+                  <span class="select-menu-item-icon octicon octicon-check"></span>
+                  <a href="/ipython/ipython/blob/rel-0.10.2/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.10.2" rel="nofollow" title="rel-0.10.2">rel-0.10.2</a>
+                </div> <!-- /.select-menu-item -->
+                <div class="select-menu-item js-navigation-item ">
+                  <span class="select-menu-item-icon octicon octicon-check"></span>
+                  <a href="/ipython/ipython/blob/rel-0.10.1/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.10.1" rel="nofollow" title="rel-0.10.1">rel-0.10.1</a>
+                </div> <!-- /.select-menu-item -->
+                <div class="select-menu-item js-navigation-item ">
+                  <span class="select-menu-item-icon octicon octicon-check"></span>
+                  <a href="/ipython/ipython/blob/rel-0.10/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.10" rel="nofollow" title="rel-0.10">rel-0.10</a>
+                </div> <!-- /.select-menu-item -->
+                <div class="select-menu-item js-navigation-item ">
+                  <span class="select-menu-item-icon octicon octicon-check"></span>
+                  <a href="/ipython/ipython/blob/rel-0.9.1/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.9.1" rel="nofollow" title="rel-0.9.1">rel-0.9.1</a>
+                </div> <!-- /.select-menu-item -->
+                <div class="select-menu-item js-navigation-item ">
+                  <span class="select-menu-item-icon octicon octicon-check"></span>
+                  <a href="/ipython/ipython/blob/rel-0.9/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.9" rel="nofollow" title="rel-0.9">rel-0.9</a>
+                </div> <!-- /.select-menu-item -->
+                <div class="select-menu-item js-navigation-item ">
+                  <span class="select-menu-item-icon octicon octicon-check"></span>
+                  <a href="/ipython/ipython/blob/rel-0.8.4/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="rel-0.8.4" rel="nofollow" title="rel-0.8.4">rel-0.8.4</a>
+                </div> <!-- /.select-menu-item -->
+                <div class="select-menu-item js-navigation-item ">
+                  <span class="select-menu-item-icon octicon octicon-check"></span>
+                  <a href="/ipython/ipython/blob/dev-0.11/docs/sphinxext/github.py" class="js-navigation-open select-menu-item-text js-select-button-text css-truncate-target" data-name="dev-0.11" rel="nofollow" title="dev-0.11">dev-0.11</a>
+                </div> <!-- /.select-menu-item -->
+            </div>
+
+            <div class="select-menu-no-results">Nothing to show</div>
+
+          </div> <!-- /.select-menu-list -->
+
+        </div> <!-- /.select-menu-modal -->
+      </div> <!-- /.select-menu-modal-holder -->
+    </div> <!-- /.select-menu -->
+
+  </div> <!-- /.scope -->
+
+  <ul class="tabnav-tabs">
+    <li><a href="/ipython/ipython" class="selected js-selected-navigation-item tabnav-tab" data-selected-links="repo_source /ipython/ipython">Files</a></li>
+    <li><a href="/ipython/ipython/commits/master" class="js-selected-navigation-item tabnav-tab" data-selected-links="repo_commits /ipython/ipython/commits/master">Commits</a></li>
+    <li><a href="/ipython/ipython/branches" class="js-selected-navigation-item tabnav-tab" data-selected-links="repo_branches /ipython/ipython/branches" rel="nofollow">Branches <span class="counter ">4</span></a></li>
+  </ul>
+
+</div>
+
+  
+  
+  
+
+
+            
+          </div>
+        </div><!-- /.repohead -->
+
+        <div id="js-repo-pjax-container" class="container context-loader-container" data-pjax-container>
+          
+
+
+<!-- blob contrib key: blob_contributors:v21:5a715cc18d66347c4a5af2652afee082 -->
+<!-- blob contrib frag key: views10/v8/blob_contributors:v21:5a715cc18d66347c4a5af2652afee082 -->
+
+
+<div id="slider">
+    <div class="frame-meta">
+
+      <p title="This is a placeholder element" class="js-history-link-replace hidden"></p>
+
+        <div class="breadcrumb">
+          <span class='bold'><span itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"><a href="/ipython/ipython" class="js-slide-to" data-branch="master" data-direction="back" itemscope="url"><span itemprop="title">ipython</span></a></span></span><span class="separator"> / </span><span itemscope="" itemtype="http://data-vocabulary.org/Breadcrumb"><a href="/ipython/ipython/tree/master/docs" class="js-slide-to" data-branch="master" data-direction="back" itemscope="url"><span ite [...]
+        </div>
+
+      <a href="/ipython/ipython/find/master" class="js-slide-to" data-hotkey="t" style="display:none">Show File Finder</a>
+
+
+        <div class="commit commit-loader file-history-tease js-deferred-content" data-url="/ipython/ipython/contributors/master/docs/sphinxext/github.py">
+          Fetching contributors…
+
+          <div class="participation">
+            <p class="loader-loading"><img alt="Octocat-spinner-32-eaf2f5" height="16" src="https://a248.e.akamai.net/assets.github.com/images/spinners/octocat-spinner-32-EAF2F5.gif?1340659530" width="16" /></p>
+            <p class="loader-error">Cannot retrieve contributors at this time</p>
+          </div>
+        </div>
+
+    </div><!-- ./.frame-meta -->
+
+    <div class="frames">
+      <div class="frame" data-permalink-url="/ipython/ipython/blob/831d4eb049fa74f8be85ea7495a9724cb754390f/docs/sphinxext/github.py" data-title="ipython/docs/sphinxext/github.py at master · ipython/ipython · GitHub" data-type="blob">
+
+        <div id="files" class="bubble">
+          <div class="file">
+            <div class="meta">
+              <div class="info">
+                <span class="icon"><b class="octicon octicon-file-text"></b></span>
+                <span class="mode" title="File Mode">file</span>
+                  <span>156 lines (132 sloc)</span>
+                <span>5.379 kb</span>
+              </div>
+              <div class="actions">
+                <div class="button-group">
+                      <a class="minibutton js-entice" href=""
+                         data-entice="You must be signed in and on a branch to make or propose changes">Edit</a>
+                  <a href="/ipython/ipython/raw/master/docs/sphinxext/github.py" class="button minibutton " id="raw-url">Raw</a>
+                    <a href="/ipython/ipython/blame/master/docs/sphinxext/github.py" class="button minibutton ">Blame</a>
+                  <a href="/ipython/ipython/commits/master/docs/sphinxext/github.py" class="button minibutton " rel="nofollow">History</a>
+                </div><!-- /.button-group -->
+              </div><!-- /.actions -->
+
+            </div>
+                <div class="blob-wrapper data type-python js-blob-data">
+      <table class="file-code file-diff">
+        <tr class="file-code-line">
+          <td class="blob-line-nums">
+            <span id="L1" rel="#L1">1</span>
+<span id="L2" rel="#L2">2</span>
+<span id="L3" rel="#L3">3</span>
+<span id="L4" rel="#L4">4</span>
+<span id="L5" rel="#L5">5</span>
+<span id="L6" rel="#L6">6</span>
+<span id="L7" rel="#L7">7</span>
+<span id="L8" rel="#L8">8</span>
+<span id="L9" rel="#L9">9</span>
+<span id="L10" rel="#L10">10</span>
+<span id="L11" rel="#L11">11</span>
+<span id="L12" rel="#L12">12</span>
+<span id="L13" rel="#L13">13</span>
+<span id="L14" rel="#L14">14</span>
+<span id="L15" rel="#L15">15</span>
+<span id="L16" rel="#L16">16</span>
+<span id="L17" rel="#L17">17</span>
+<span id="L18" rel="#L18">18</span>
+<span id="L19" rel="#L19">19</span>
+<span id="L20" rel="#L20">20</span>
+<span id="L21" rel="#L21">21</span>
+<span id="L22" rel="#L22">22</span>
+<span id="L23" rel="#L23">23</span>
+<span id="L24" rel="#L24">24</span>
+<span id="L25" rel="#L25">25</span>
+<span id="L26" rel="#L26">26</span>
+<span id="L27" rel="#L27">27</span>
+<span id="L28" rel="#L28">28</span>
+<span id="L29" rel="#L29">29</span>
+<span id="L30" rel="#L30">30</span>
+<span id="L31" rel="#L31">31</span>
+<span id="L32" rel="#L32">32</span>
+<span id="L33" rel="#L33">33</span>
+<span id="L34" rel="#L34">34</span>
+<span id="L35" rel="#L35">35</span>
+<span id="L36" rel="#L36">36</span>
+<span id="L37" rel="#L37">37</span>
+<span id="L38" rel="#L38">38</span>
+<span id="L39" rel="#L39">39</span>
+<span id="L40" rel="#L40">40</span>
+<span id="L41" rel="#L41">41</span>
+<span id="L42" rel="#L42">42</span>
+<span id="L43" rel="#L43">43</span>
+<span id="L44" rel="#L44">44</span>
+<span id="L45" rel="#L45">45</span>
+<span id="L46" rel="#L46">46</span>
+<span id="L47" rel="#L47">47</span>
+<span id="L48" rel="#L48">48</span>
+<span id="L49" rel="#L49">49</span>
+<span id="L50" rel="#L50">50</span>
+<span id="L51" rel="#L51">51</span>
+<span id="L52" rel="#L52">52</span>
+<span id="L53" rel="#L53">53</span>
+<span id="L54" rel="#L54">54</span>
+<span id="L55" rel="#L55">55</span>
+<span id="L56" rel="#L56">56</span>
+<span id="L57" rel="#L57">57</span>
+<span id="L58" rel="#L58">58</span>
+<span id="L59" rel="#L59">59</span>
+<span id="L60" rel="#L60">60</span>
+<span id="L61" rel="#L61">61</span>
+<span id="L62" rel="#L62">62</span>
+<span id="L63" rel="#L63">63</span>
+<span id="L64" rel="#L64">64</span>
+<span id="L65" rel="#L65">65</span>
+<span id="L66" rel="#L66">66</span>
+<span id="L67" rel="#L67">67</span>
+<span id="L68" rel="#L68">68</span>
+<span id="L69" rel="#L69">69</span>
+<span id="L70" rel="#L70">70</span>
+<span id="L71" rel="#L71">71</span>
+<span id="L72" rel="#L72">72</span>
+<span id="L73" rel="#L73">73</span>
+<span id="L74" rel="#L74">74</span>
+<span id="L75" rel="#L75">75</span>
+<span id="L76" rel="#L76">76</span>
+<span id="L77" rel="#L77">77</span>
+<span id="L78" rel="#L78">78</span>
+<span id="L79" rel="#L79">79</span>
+<span id="L80" rel="#L80">80</span>
+<span id="L81" rel="#L81">81</span>
+<span id="L82" rel="#L82">82</span>
+<span id="L83" rel="#L83">83</span>
+<span id="L84" rel="#L84">84</span>
+<span id="L85" rel="#L85">85</span>
+<span id="L86" rel="#L86">86</span>
+<span id="L87" rel="#L87">87</span>
+<span id="L88" rel="#L88">88</span>
+<span id="L89" rel="#L89">89</span>
+<span id="L90" rel="#L90">90</span>
+<span id="L91" rel="#L91">91</span>
+<span id="L92" rel="#L92">92</span>
+<span id="L93" rel="#L93">93</span>
+<span id="L94" rel="#L94">94</span>
+<span id="L95" rel="#L95">95</span>
+<span id="L96" rel="#L96">96</span>
+<span id="L97" rel="#L97">97</span>
+<span id="L98" rel="#L98">98</span>
+<span id="L99" rel="#L99">99</span>
+<span id="L100" rel="#L100">100</span>
+<span id="L101" rel="#L101">101</span>
+<span id="L102" rel="#L102">102</span>
+<span id="L103" rel="#L103">103</span>
+<span id="L104" rel="#L104">104</span>
+<span id="L105" rel="#L105">105</span>
+<span id="L106" rel="#L106">106</span>
+<span id="L107" rel="#L107">107</span>
+<span id="L108" rel="#L108">108</span>
+<span id="L109" rel="#L109">109</span>
+<span id="L110" rel="#L110">110</span>
+<span id="L111" rel="#L111">111</span>
+<span id="L112" rel="#L112">112</span>
+<span id="L113" rel="#L113">113</span>
+<span id="L114" rel="#L114">114</span>
+<span id="L115" rel="#L115">115</span>
+<span id="L116" rel="#L116">116</span>
+<span id="L117" rel="#L117">117</span>
+<span id="L118" rel="#L118">118</span>
+<span id="L119" rel="#L119">119</span>
+<span id="L120" rel="#L120">120</span>
+<span id="L121" rel="#L121">121</span>
+<span id="L122" rel="#L122">122</span>
+<span id="L123" rel="#L123">123</span>
+<span id="L124" rel="#L124">124</span>
+<span id="L125" rel="#L125">125</span>
+<span id="L126" rel="#L126">126</span>
+<span id="L127" rel="#L127">127</span>
+<span id="L128" rel="#L128">128</span>
+<span id="L129" rel="#L129">129</span>
+<span id="L130" rel="#L130">130</span>
+<span id="L131" rel="#L131">131</span>
+<span id="L132" rel="#L132">132</span>
+<span id="L133" rel="#L133">133</span>
+<span id="L134" rel="#L134">134</span>
+<span id="L135" rel="#L135">135</span>
+<span id="L136" rel="#L136">136</span>
+<span id="L137" rel="#L137">137</span>
+<span id="L138" rel="#L138">138</span>
+<span id="L139" rel="#L139">139</span>
+<span id="L140" rel="#L140">140</span>
+<span id="L141" rel="#L141">141</span>
+<span id="L142" rel="#L142">142</span>
+<span id="L143" rel="#L143">143</span>
+<span id="L144" rel="#L144">144</span>
+<span id="L145" rel="#L145">145</span>
+<span id="L146" rel="#L146">146</span>
+<span id="L147" rel="#L147">147</span>
+<span id="L148" rel="#L148">148</span>
+<span id="L149" rel="#L149">149</span>
+<span id="L150" rel="#L150">150</span>
+<span id="L151" rel="#L151">151</span>
+<span id="L152" rel="#L152">152</span>
+<span id="L153" rel="#L153">153</span>
+<span id="L154" rel="#L154">154</span>
+<span id="L155" rel="#L155">155</span>
+
+          </td>
+          <td class="blob-line-code">
+                  <div class="highlight"><pre><div class='line' id='LC1'><span class="sd">"""Define text roles for GitHub</span></div><div class='line' id='LC2'><br/></div><div class='line' id='LC3'><span class="sd">* ghissue - Issue</span></div><div class='line' id='LC4'><span class="sd">* ghpull - Pull Request</span></div><div class='line' id='LC5'><span class="sd">* ghuser - User</span></div><div class='line' id='LC6'><br/></div><div class='line' id='LC7'><span class="s [...]
+          </td>
+        </tr>
+      </table>
+  </div>
+
+          </div>
+        </div>
+
+        <a href="#jump-to-line" rel="facebox" data-hotkey="l" class="js-jump-to-line" style="display:none">Jump to Line</a>
+        <div id="jump-to-line" style="display:none">
+          <h2>Jump to Line</h2>
+          <form accept-charset="UTF-8" class="js-jump-to-line-form">
+            <input class="textfield js-jump-to-line-field" type="text">
+            <div class="full-button">
+              <button type="submit" class="button">Go</button>
+            </div>
+          </form>
+        </div>
+
+      </div>
+    </div>
+</div>
+
+<div id="js-frame-loading-template" class="frame frame-loading large-loading-area" style="display:none;">
+  <img class="js-frame-loading-spinner" src="https://a248.e.akamai.net/assets.github.com/images/spinners/octocat-spinner-128.gif?1347543525" height="64" width="64">
+</div>
+
+
+        </div>
+      </div>
+      <div class="modal-backdrop"></div>
+    </div>
+
+      <div id="footer-push"></div><!-- hack for sticky footer -->
+    </div><!-- end of wrapper - hack for sticky footer -->
+
+      <!-- footer -->
+      <div id="footer">
+  <div class="container clearfix">
+
+      <dl class="footer_nav">
+        <dt>GitHub</dt>
+        <dd><a href="https://github.com/about">About us</a></dd>
+        <dd><a href="https://github.com/blog">Blog</a></dd>
+        <dd><a href="https://github.com/contact">Contact & support</a></dd>
+        <dd><a href="http://enterprise.github.com/">GitHub Enterprise</a></dd>
+        <dd><a href="http://status.github.com/">Site status</a></dd>
+      </dl>
+
+      <dl class="footer_nav">
+        <dt>Applications</dt>
+        <dd><a href="http://mac.github.com/">GitHub for Mac</a></dd>
+        <dd><a href="http://windows.github.com/">GitHub for Windows</a></dd>
+        <dd><a href="http://eclipse.github.com/">GitHub for Eclipse</a></dd>
+        <dd><a href="http://mobile.github.com/">GitHub mobile apps</a></dd>
+      </dl>
+
+      <dl class="footer_nav">
+        <dt>Services</dt>
+        <dd><a href="http://get.gaug.es/">Gauges: Web analytics</a></dd>
+        <dd><a href="http://speakerdeck.com">Speaker Deck: Presentations</a></dd>
+        <dd><a href="https://gist.github.com">Gist: Code snippets</a></dd>
+        <dd><a href="http://jobs.github.com/">Job board</a></dd>
+      </dl>
+
+      <dl class="footer_nav">
+        <dt>Documentation</dt>
+        <dd><a href="http://help.github.com/">GitHub Help</a></dd>
+        <dd><a href="http://developer.github.com/">Developer API</a></dd>
+        <dd><a href="http://github.github.com/github-flavored-markdown/">GitHub Flavored Markdown</a></dd>
+        <dd><a href="http://pages.github.com/">GitHub Pages</a></dd>
+      </dl>
+
+      <dl class="footer_nav">
+        <dt>More</dt>
+        <dd><a href="http://training.github.com/">Training</a></dd>
+        <dd><a href="https://github.com/edu">Students & teachers</a></dd>
+        <dd><a href="http://shop.github.com">The Shop</a></dd>
+        <dd><a href="/plans">Plans & pricing</a></dd>
+        <dd><a href="http://octodex.github.com/">The Octodex</a></dd>
+      </dl>
+
+      <hr class="footer-divider">
+
+
+    <p class="right">© 2013 <span title="0.12974s from fe17.rs.github.com">GitHub</span>, Inc. All rights reserved.</p>
+    <a class="left" href="https://github.com/">
+      <span class="mega-octicon octicon-mark-github"></span>
+    </a>
+    <ul id="legal">
+        <li><a href="https://github.com/site/terms">Terms of Service</a></li>
+        <li><a href="https://github.com/site/privacy">Privacy</a></li>
+        <li><a href="https://github.com/security">Security</a></li>
+    </ul>
+
+  </div><!-- /.container -->
+
+</div><!-- /.#footer -->
+
+
+    <div class="fullscreen-overlay js-fullscreen-overlay" id="fullscreen_overlay">
+  <div class="fullscreen-container js-fullscreen-container">
+    <div class="textarea-wrap">
+      <textarea name="fullscreen-contents" id="fullscreen-contents" class="js-fullscreen-contents" placeholder="" data-suggester="fullscreen_suggester"></textarea>
+          <div class="suggester-container">
+              <div class="suggester fullscreen-suggester js-navigation-container" id="fullscreen_suggester"
+                 data-url="/ipython/ipython/suggestions/commit">
+              </div>
+          </div>
+    </div>
+  </div>
+  <div class="fullscreen-sidebar">
+    <a href="#" class="exit-fullscreen js-exit-fullscreen tooltipped leftwards" title="Exit Zen Mode">
+      <span class="mega-octicon octicon-screen-normal"></span>
+    </a>
+    <a href="#" class="theme-switcher js-theme-switcher tooltipped leftwards"
+      title="Switch themes">
+      <span class="octicon octicon-color-mode"></span>
+    </a>
+  </div>
+</div>
+
+
+
+    <div id="ajax-error-message" class="flash flash-error">
+      <span class="octicon octicon-alert"></span>
+      Something went wrong with that request. Please try again.
+      <a href="#" class="octicon octicon-remove-close ajax-error-dismiss"></a>
+    </div>
+
+    
+    
+    <span id='server_response_time' data-time='0.13024' data-host='fe17'></span>
+    
+  </body>
+</html>
+
diff --git a/doc/_extensions/hidden_code_block.py b/doc/_extensions/hidden_code_block.py
new file mode 100644
index 0000000..e9ee9fe
--- /dev/null
+++ b/doc/_extensions/hidden_code_block.py
@@ -0,0 +1,128 @@
+"""Simple, inelegant Sphinx extension which adds a directive for a
+highlighted code-block that may be toggled hidden and shown in HTML.  
+This is possibly useful for teaching courses.
+
+The directive, like the standard code-block directive, takes
+a language argument and an optional linenos parameter.  The
+hidden-code-block adds starthidden and label as optional 
+parameters.
+
+Examples:
+
+.. hidden-code-block:: python
+    :starthidden: False
+
+    a = 10
+    b = a + 5
+
+.. hidden-code-block:: python
+    :label: --- SHOW/HIDE ---
+
+    x = 10
+    y = x + 5
+
+Thanks to http://www.javascriptkit.com/javatutors/dom3.shtml for 
+inspiration on the javascript.  
+
+Thanks to Milad 'animal' Fatenejad for suggesting this extension 
+in the first place.
+
+Written by Anthony 'el Scopz' Scopatz, January 2012.
+
+Released under the WTFPL (http://sam.zoy.org/wtfpl/).
+"""
+
+from docutils import nodes
+from docutils.parsers.rst import directives
+from sphinx.directives.code import CodeBlock
+from sphinx.util.compat import make_admonition
+
+HCB_COUNTER = 0
+
+js_showhide = """\
+<script type="text/javascript">
+    function showhide(element){
+        if (!document.getElementById)
+            return
+
+        if (element.style.display == "block")
+            element.style.display = "none"
+        else
+            element.style.display = "block"
+    };
+</script>
+"""
+
+def nice_bool(arg):
+    tvalues = ('true',  't', 'yes', 'y')
+    fvalues = ('false', 'f', 'no',  'n')
+    arg = directives.choice(arg, tvalues + fvalues)
+    return arg in tvalues
+
+
+class hidden_code_block(nodes.General, nodes.FixedTextElement):
+    pass
+
+
+class HiddenCodeBlock(CodeBlock):
+    """Hidden code block is Hidden"""
+
+    option_spec = dict(starthidden=nice_bool, 
+                       label=str,
+                       **CodeBlock.option_spec)
+
+    def run(self):
+        # Body of the method is more or less copied from CodeBlock
+        code = u'\n'.join(self.content)
+        hcb = hidden_code_block(code, code)
+        hcb['language'] = self.arguments[0]
+        hcb['linenos'] = 'linenos' in self.options
+        hcb['starthidden'] = self.options.get('starthidden', True)
+        hcb['label'] = self.options.get('label', '+ show/hide code')
+        hcb.line = self.lineno
+        return [hcb]
+
+
+def visit_hcb_html(self, node):
+    """Visit hidden code block"""
+    global HCB_COUNTER
+    HCB_COUNTER += 1
+
+    # We want to use the original highlighter so that we don't
+    # have to reimplement it.  However it raises a SkipNode 
+    # error at the end of the function call.  Thus we intercept
+    # it and raise it again later.
+    try: 
+        self.visit_literal_block(node)
+    except nodes.SkipNode:
+        pass
+
+    # The last element of the body should be the literal code 
+    # block that was just made.
+    code_block = self.body[-1]
+
+    fill_header = {'divname': 'hiddencodeblock{}'.format(HCB_COUNTER), 
+                   'startdisplay': 'none' if node['starthidden'] else 'block', 
+                   'label': node.get('label'), 
+                   }
+
+    divheader = ("""<a href="javascript:showhide(document.getElementById('{divname}'))">"""
+                 """{label}</a><br />"""
+                 '''<div id="{divname}" style="display: {startdisplay}">'''
+                 ).format(**fill_header)
+
+    code_block = js_showhide + divheader + code_block + "</div>"
+
+    # reassign and exit
+    self.body[-1] = code_block
+    raise nodes.SkipNode
+
+
+def depart_hcb_html(self, node):
+    """Depart hidden code block"""
+    # Stub because of SkipNode in visit
+
+
+def setup(app):
+    app.add_directive('hidden-code-block', HiddenCodeBlock)
+    app.add_node(hidden_code_block, html=(visit_hcb_html, depart_hcb_html))
diff --git a/doc/_extensions/inheritance_diagram.py b/doc/_extensions/inheritance_diagram.py
new file mode 100644
index 0000000..407fc13
--- /dev/null
+++ b/doc/_extensions/inheritance_diagram.py
@@ -0,0 +1,407 @@
+"""
+Defines a docutils directive for inserting inheritance diagrams.
+
+Provide the directive with one or more classes or modules (separated
+by whitespace).  For modules, all of the classes in that module will
+be used.
+
+Example::
+
+   Given the following classes:
+
+   class A: pass
+   class B(A): pass
+   class C(A): pass
+   class D(B, C): pass
+   class E(B): pass
+
+   .. inheritance-diagram: D E
+
+   Produces a graph like the following:
+
+               A
+              / \
+             B   C
+            / \ /
+           E   D
+
+The graph is inserted as a PNG+image map into HTML and a PDF in
+LaTeX.
+"""
+
+import inspect
+import os
+import re
+import subprocess
+try:
+    from hashlib import md5
+except ImportError:
+    from md5 import md5
+
+from docutils.nodes import Body, Element
+from docutils.parsers.rst import directives
+from sphinx.roles import xfileref_role
+
+def my_import(name):
+    """Module importer - taken from the python documentation.
+
+    This function allows importing names with dots in them."""
+    
+    mod = __import__(name)
+    components = name.split('.')
+    for comp in components[1:]:
+        mod = getattr(mod, comp)
+    return mod
+
+class DotException(Exception):
+    pass
+
+class InheritanceGraph(object):
+    """
+    Given a list of classes, determines the set of classes that
+    they inherit from all the way to the root "object", and then
+    is able to generate a graphviz dot graph from them.
+    """
+    def __init__(self, class_names, show_builtins=False):
+        """
+        *class_names* is a list of child classes to show bases from.
+
+        If *show_builtins* is True, then Python builtins will be shown
+        in the graph.
+        """
+        self.class_names = class_names
+        self.classes = self._import_classes(class_names)
+        self.all_classes = self._all_classes(self.classes)
+        if len(self.all_classes) == 0:
+            raise ValueError("No classes found for inheritance diagram")
+        self.show_builtins = show_builtins
+
+    py_sig_re = re.compile(r'''^([\w.]*\.)?    # class names
+                           (\w+)  \s* $        # optionally arguments
+                           ''', re.VERBOSE)
+
+    def _import_class_or_module(self, name):
+        """
+        Import a class using its fully-qualified *name*.
+        """
+        try:
+            path, base = self.py_sig_re.match(name).groups()
+        except:
+            raise ValueError(
+                "Invalid class or module '%s' specified for inheritance diagram" % name)
+        fullname = (path or '') + base
+        path = (path and path.rstrip('.'))
+        if not path:
+            path = base
+        try:
+            module = __import__(path, None, None, [])
+            # We must do an import of the fully qualified name.  Otherwise if a
+            # subpackage 'a.b' is requested where 'import a' does NOT provide
+            # 'a.b' automatically, then 'a.b' will not be found below.  This
+            # second call will force the equivalent of 'import a.b' to happen
+            # after the top-level import above.
+            my_import(fullname)
+            
+        except ImportError:
+            raise ValueError(
+                "Could not import class or module '%s' specified for inheritance diagram" % name)
+
+        try:
+            todoc = module
+            for comp in fullname.split('.')[1:]:
+                todoc = getattr(todoc, comp)
+        except AttributeError:
+            raise ValueError(
+                "Could not find class or module '%s' specified for inheritance diagram" % name)
+
+        # If a class, just return it
+        if inspect.isclass(todoc):
+            return [todoc]
+        elif inspect.ismodule(todoc):
+            classes = []
+            for cls in todoc.__dict__.values():
+                if inspect.isclass(cls) and cls.__module__ == todoc.__name__:
+                    classes.append(cls)
+            return classes
+        raise ValueError(
+            "'%s' does not resolve to a class or module" % name)
+
+    def _import_classes(self, class_names):
+        """
+        Import a list of classes.
+        """
+        classes = []
+        for name in class_names:
+            classes.extend(self._import_class_or_module(name))
+        return classes
+
+    def _all_classes(self, classes):
+        """
+        Return a list of all classes that are ancestors of *classes*.
+        """
+        all_classes = {}
+
+        def recurse(cls):
+            all_classes[cls] = None
+            for c in cls.__bases__:
+                if c not in all_classes:
+                    recurse(c)
+
+        for cls in classes:
+            recurse(cls)
+
+        return all_classes.keys()
+
+    def class_name(self, cls, parts=0):
+        """
+        Given a class object, return a fully-qualified name.  This
+        works for things I've tested in matplotlib so far, but may not
+        be completely general.
+        """
+        module = cls.__module__
+        if module == '__builtin__':
+            fullname = cls.__name__
+        else:
+            fullname = "%s.%s" % (module, cls.__name__)
+        if parts == 0:
+            return fullname
+        name_parts = fullname.split('.')
+        return '.'.join(name_parts[-parts:])
+
+    def get_all_class_names(self):
+        """
+        Get all of the class names involved in the graph.
+        """
+        return [self.class_name(x) for x in self.all_classes]
+
+    # These are the default options for graphviz
+    default_graph_options = {
+        "rankdir": "LR",
+        "size": '"8.0, 12.0"'
+        }
+    default_node_options = {
+        "shape": "box",
+        "fontsize": 10,
+        "height": 0.25,
+        "fontname": "Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans",
+        "style": '"setlinewidth(0.5)"'
+        }
+    default_edge_options = {
+        "arrowsize": 0.5,
+        "style": '"setlinewidth(0.5)"'
+        }
+
+    def _format_node_options(self, options):
+        return ','.join(["%s=%s" % x for x in options.items()])
+    def _format_graph_options(self, options):
+        return ''.join(["%s=%s;\n" % x for x in options.items()])
+
+    def generate_dot(self, fd, name, parts=0, urls={},
+                     graph_options={}, node_options={},
+                     edge_options={}):
+        """
+        Generate a graphviz dot graph from the classes that
+        were passed in to __init__.
+
+        *fd* is a Python file-like object to write to.
+
+        *name* is the name of the graph
+
+        *urls* is a dictionary mapping class names to http urls
+
+        *graph_options*, *node_options*, *edge_options* are
+        dictionaries containing key/value pairs to pass on as graphviz
+        properties.
+        """
+        g_options = self.default_graph_options.copy()
+        g_options.update(graph_options)
+        n_options = self.default_node_options.copy()
+        n_options.update(node_options)
+        e_options = self.default_edge_options.copy()
+        e_options.update(edge_options)
+
+        fd.write('digraph %s {\n' % name)
+        fd.write(self._format_graph_options(g_options))
+
+        for cls in self.all_classes:
+            if not self.show_builtins and cls in __builtins__.values():
+                continue
+
+            name = self.class_name(cls, parts)
+
+            # Write the node
+            this_node_options = n_options.copy()
+            url = urls.get(self.class_name(cls))
+            if url is not None:
+                this_node_options['URL'] = '"%s"' % url
+            fd.write('  "%s" [%s];\n' %
+                     (name, self._format_node_options(this_node_options)))
+
+            # Write the edges
+            for base in cls.__bases__:
+                if not self.show_builtins and base in __builtins__.values():
+                    continue
+
+                base_name = self.class_name(base, parts)
+                fd.write('  "%s" -> "%s" [%s];\n' %
+                         (base_name, name,
+                          self._format_node_options(e_options)))
+        fd.write('}\n')
+
+    def run_dot(self, args, name, parts=0, urls={},
+                graph_options={}, node_options={}, edge_options={}):
+        """
+        Run graphviz 'dot' over this graph, returning whatever 'dot'
+        writes to stdout.
+
+        *args* will be passed along as commandline arguments.
+
+        *name* is the name of the graph
+
+        *urls* is a dictionary mapping class names to http urls
+
+        Raises DotException for any of the many os and
+        installation-related errors that may occur.
+        """
+        try:
+            dot = subprocess.Popen(['dot'] + list(args),
+                                   stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+                                   close_fds=True)
+        except OSError:
+            raise DotException("Could not execute 'dot'.  Are you sure you have 'graphviz' installed?")
+        except ValueError:
+            raise DotException("'dot' called with invalid arguments")
+        except:
+            raise DotException("Unexpected error calling 'dot'")
+
+        self.generate_dot(dot.stdin, name, parts, urls, graph_options,
+                          node_options, edge_options)
+        dot.stdin.close()
+        result = dot.stdout.read()
+        returncode = dot.wait()
+        if returncode != 0:
+            raise DotException("'dot' returned the errorcode %d" % returncode)
+        return result
+
+class inheritance_diagram(Body, Element):
+    """
+    A docutils node to use as a placeholder for the inheritance
+    diagram.
+    """
+    pass
+
+def inheritance_diagram_directive(name, arguments, options, content, lineno,
+                                  content_offset, block_text, state,
+                                  state_machine):
+    """
+    Run when the inheritance_diagram directive is first encountered.
+    """
+    node = inheritance_diagram()
+
+    class_names = arguments
+
+    # Create a graph starting with the list of classes
+    graph = InheritanceGraph(class_names)
+
+    # Create xref nodes for each target of the graph's image map and
+    # add them to the doc tree so that Sphinx can resolve the
+    # references to real URLs later.  These nodes will eventually be
+    # removed from the doctree after we're done with them.
+    for name in graph.get_all_class_names():
+        refnodes, x = xfileref_role(
+            'class', ':class:`%s`' % name, name, 0, state)
+        node.extend(refnodes)
+    # Store the graph object so we can use it to generate the
+    # dot file later
+    node['graph'] = graph
+    # Store the original content for use as a hash
+    node['parts'] = options.get('parts', 0)
+    node['content'] = " ".join(class_names)
+    return [node]
+
+def get_graph_hash(node):
+    return md5(node['content'] + str(node['parts'])).hexdigest()[-10:]
+
+def html_output_graph(self, node):
+    """
+    Output the graph for HTML.  This will insert a PNG with clickable
+    image map.
+    """
+    graph = node['graph']
+    parts = node['parts']
+
+    graph_hash = get_graph_hash(node)
+    name = "inheritance%s" % graph_hash
+    path = '_images'
+    dest_path = os.path.join(setup.app.builder.outdir, path)
+    if not os.path.exists(dest_path):
+        os.makedirs(dest_path)
+    png_path = os.path.join(dest_path, name + ".png")
+    path = setup.app.builder.imgpath
+
+    # Create a mapping from fully-qualified class names to URLs.
+    urls = {}
+    for child in node:
+        if child.get('refuri') is not None:
+            urls[child['reftitle']] = child.get('refuri')
+        elif child.get('refid') is not None:
+            urls[child['reftitle']] = '#' + child.get('refid')
+
+    # These arguments to dot will save a PNG file to disk and write
+    # an HTML image map to stdout.
+    image_map = graph.run_dot(['-Tpng', '-o%s' % png_path, '-Tcmapx'],
+                              name, parts, urls)
+    return ('<img src="%s/%s.png" usemap="#%s" class="inheritance"/>%s' %
+            (path, name, name, image_map))
+
+def latex_output_graph(self, node):
+    """
+    Output the graph for LaTeX.  This will insert a PDF.
+    """
+    graph = node['graph']
+    parts = node['parts']
+
+    graph_hash = get_graph_hash(node)
+    name = "inheritance%s" % graph_hash
+    dest_path = os.path.abspath(os.path.join(setup.app.builder.outdir, '_images'))
+    if not os.path.exists(dest_path):
+        os.makedirs(dest_path)
+    pdf_path = os.path.abspath(os.path.join(dest_path, name + ".pdf"))
+
+    graph.run_dot(['-Tpdf', '-o%s' % pdf_path],
+                  name, parts, graph_options={'size': '"6.0,6.0"'})
+    return '\n\\includegraphics{%s}\n\n' % pdf_path
+
+def visit_inheritance_diagram(inner_func):
+    """
+    This is just a wrapper around html/latex_output_graph to make it
+    easier to handle errors and insert warnings.
+    """
+    def visitor(self, node):
+        try:
+            content = inner_func(self, node)
+        except DotException, e:
+            # Insert the exception as a warning in the document
+            warning = self.document.reporter.warning(str(e), line=node.line)
+            warning.parent = node
+            node.children = [warning]
+        else:
+            source = self.document.attributes['source']
+            self.body.append(content)
+            node.children = []
+    return visitor
+
+def do_nothing(self, node):
+    pass
+
+def setup(app):
+    setup.app = app
+    setup.confdir = app.confdir
+
+    app.add_node(
+        inheritance_diagram,
+        latex=(visit_inheritance_diagram(latex_output_graph), do_nothing),
+        html=(visit_inheritance_diagram(html_output_graph), do_nothing))
+    app.add_directive(
+        'inheritance-diagram', inheritance_diagram_directive,
+        False, (1, 100, 0), parts = directives.nonnegative_int)
diff --git a/doc/_extensions/ipython_console_highlighting.py b/doc/_extensions/ipython_console_highlighting.py
new file mode 100644
index 0000000..217b779
--- /dev/null
+++ b/doc/_extensions/ipython_console_highlighting.py
@@ -0,0 +1,114 @@
+"""reST directive for syntax-highlighting ipython interactive sessions.
+
+XXX - See what improvements can be made based on the new (as of Sept 2009)
+'pycon' lexer for the python console.  At the very least it will give better
+highlighted tracebacks.
+"""
+
+#-----------------------------------------------------------------------------
+# Needed modules
+
+# Standard library
+import re
+
+# Third party
+from pygments.lexer import Lexer, do_insertions
+from pygments.lexers.agile import (PythonConsoleLexer, PythonLexer, 
+                                   PythonTracebackLexer)
+from pygments.token import Comment, Generic
+
+from sphinx import highlighting
+
+#-----------------------------------------------------------------------------
+# Global constants
+line_re = re.compile('.*?\n')
+
+#-----------------------------------------------------------------------------
+# Code begins - classes and functions
+
+class IPythonConsoleLexer(Lexer):
+    """
+    For IPython console output or doctests, such as:
+
+    .. sourcecode:: ipython
+
+      In [1]: a = 'foo'
+
+      In [2]: a
+      Out[2]: 'foo'
+
+      In [3]: print a
+      foo
+
+      In [4]: 1 / 0
+
+    Notes:
+
+      - Tracebacks are not currently supported.
+
+      - It assumes the default IPython prompts, not customized ones.
+    """
+    
+    name = 'IPython console session'
+    aliases = ['ipython']
+    mimetypes = ['text/x-ipython-console']
+    input_prompt = re.compile("(In \[[0-9]+\]: )|(   \.\.\.+:)")
+    output_prompt = re.compile("(Out\[[0-9]+\]: )|(   \.\.\.+:)")
+    continue_prompt = re.compile("   \.\.\.+:")
+    tb_start = re.compile("\-+")
+
+    def get_tokens_unprocessed(self, text):
+        pylexer = PythonLexer(**self.options)
+        tblexer = PythonTracebackLexer(**self.options)
+
+        curcode = ''
+        insertions = []
+        for match in line_re.finditer(text):
+            line = match.group()
+            input_prompt = self.input_prompt.match(line)
+            continue_prompt = self.continue_prompt.match(line.rstrip())
+            output_prompt = self.output_prompt.match(line)
+            if line.startswith("#"):
+                insertions.append((len(curcode),
+                                   [(0, Comment, line)]))
+            elif input_prompt is not None:
+                insertions.append((len(curcode),
+                                   [(0, Generic.Prompt, input_prompt.group())]))
+                curcode += line[input_prompt.end():]
+            elif continue_prompt is not None:
+                insertions.append((len(curcode),
+                                   [(0, Generic.Prompt, continue_prompt.group())]))
+                curcode += line[continue_prompt.end():]
+            elif output_prompt is not None:
+                # Use the 'error' token for output.  We should probably make
+                # our own token, but error is typicaly in a bright color like
+                # red, so it works fine for our output prompts.
+                insertions.append((len(curcode),
+                                   [(0, Generic.Error, output_prompt.group())]))
+                curcode += line[output_prompt.end():]
+            else:
+                if curcode:
+                    for item in do_insertions(insertions,
+                                              pylexer.get_tokens_unprocessed(curcode)):
+                        yield item
+                        curcode = ''
+                        insertions = []
+                yield match.start(), Generic.Output, line
+        if curcode:
+            for item in do_insertions(insertions,
+                                      pylexer.get_tokens_unprocessed(curcode)):
+                yield item
+
+
+def setup(app):
+    """Setup as a sphinx extension."""
+
+    # This is only a lexer, so adding it below to pygments appears sufficient.
+    # But if somebody knows that the right API usage should be to do that via
+    # sphinx, by all means fix it here.  At least having this setup.py
+    # suppresses the sphinx warning we'd get without it.
+    pass
+
+#-----------------------------------------------------------------------------
+# Register the extension as a valid pygments lexer
+highlighting.lexers['ipython'] = IPythonConsoleLexer()
diff --git a/doc/_extensions/ipython_directive.py b/doc/_extensions/ipython_directive.py
new file mode 100644
index 0000000..e69de29
diff --git a/doc/_extensions/numpydoc.py b/doc/_extensions/numpydoc.py
new file mode 100644
index 0000000..ff6c44c
--- /dev/null
+++ b/doc/_extensions/numpydoc.py
@@ -0,0 +1,116 @@
+"""
+========
+numpydoc
+========
+
+Sphinx extension that handles docstrings in the Numpy standard format. [1]
+
+It will:
+
+- Convert Parameters etc. sections to field lists.
+- Convert See Also section to a See also entry.
+- Renumber references.
+- Extract the signature from the docstring, if it can't be determined otherwise.
+
+.. [1] http://projects.scipy.org/scipy/numpy/wiki/CodingStyleGuidelines#docstring-standard
+
+"""
+
+import os, re, pydoc
+from docscrape_sphinx import get_doc_object, SphinxDocString
+import inspect
+
+def mangle_docstrings(app, what, name, obj, options, lines,
+                      reference_offset=[0]):
+    if what == 'module':
+        # Strip top title
+        title_re = re.compile(r'^\s*[#*=]{4,}\n[a-z0-9 -]+\n[#*=]{4,}\s*',
+                              re.I|re.S)
+        lines[:] = title_re.sub('', "\n".join(lines)).split("\n")
+    else:
+        doc = get_doc_object(obj, what, "\n".join(lines))
+        lines[:] = str(doc).split("\n")
+
+    if app.config.numpydoc_edit_link and hasattr(obj, '__name__') and \
+           obj.__name__:
+        if hasattr(obj, '__module__'):
+            v = dict(full_name="%s.%s" % (obj.__module__, obj.__name__))
+        else:
+            v = dict(full_name=obj.__name__)
+        lines += ['', '.. htmlonly::', '']
+        lines += ['    %s' % x for x in
+                  (app.config.numpydoc_edit_link % v).split("\n")]
+
+    # replace reference numbers so that there are no duplicates
+    references = []
+    for l in lines:
+        l = l.strip()
+        if l.startswith('.. ['):
+            try:
+                references.append(int(l[len('.. ['):l.index(']')]))
+            except ValueError:
+                print "WARNING: invalid reference in %s docstring" % name
+
+    # Start renaming from the biggest number, otherwise we may
+    # overwrite references.
+    references.sort()
+    if references:
+        for i, line in enumerate(lines):
+            for r in references:
+                new_r = reference_offset[0] + r
+                lines[i] = lines[i].replace('[%d]_' % r,
+                                            '[%d]_' % new_r)
+                lines[i] = lines[i].replace('.. [%d]' % r,
+                                            '.. [%d]' % new_r)
+
+    reference_offset[0] += len(references)
+
+def mangle_signature(app, what, name, obj, options, sig, retann):
+    # Do not try to inspect classes that don't define `__init__`
+    if (inspect.isclass(obj) and
+        'initializes x; see ' in pydoc.getdoc(obj.__init__)):
+        return '', ''
+
+    if not (callable(obj) or hasattr(obj, '__argspec_is_invalid_')): return
+    if not hasattr(obj, '__doc__'): return
+
+    doc = SphinxDocString(pydoc.getdoc(obj))
+    if doc['Signature']:
+        sig = re.sub("^[^(]*", "", doc['Signature'])
+        return sig, ''
+
+def initialize(app):
+    try:
+        app.connect('autodoc-process-signature', mangle_signature)
+    except:
+        monkeypatch_sphinx_ext_autodoc()
+
+def setup(app, get_doc_object_=get_doc_object):
+    global get_doc_object
+    get_doc_object = get_doc_object_
+    
+    app.connect('autodoc-process-docstring', mangle_docstrings)
+    app.connect('builder-inited', initialize)
+    app.add_config_value('numpydoc_edit_link', None, True)
+
+#------------------------------------------------------------------------------
+# Monkeypatch sphinx.ext.autodoc to accept argspecless autodocs (Sphinx < 0.5)
+#------------------------------------------------------------------------------
+
+def monkeypatch_sphinx_ext_autodoc():
+    global _original_format_signature
+    import sphinx.ext.autodoc
+
+    if sphinx.ext.autodoc.format_signature is our_format_signature:
+        return
+
+    print "[numpydoc] Monkeypatching sphinx.ext.autodoc ..."
+    _original_format_signature = sphinx.ext.autodoc.format_signature
+    sphinx.ext.autodoc.format_signature = our_format_signature
+
+def our_format_signature(what, obj):
+    r = mangle_signature(None, what, None, obj, None, None, None)
+    if r is not None:
+        return r[0]
+    else:
+        return _original_format_signature(what, obj)
diff --git a/doc/conf.py b/doc/conf.py
new file mode 100644
index 0000000..ed3d742
--- /dev/null
+++ b/doc/conf.py
@@ -0,0 +1,305 @@
+# -*- coding: utf-8 -*-
+#
+# multipletau documentation build configuration file, created by
+# sphinx-quickstart on Sat Feb 22 09:35:49 2014.
+#
+# This file is execfile()d with the current directory set to its
+# containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys
+import os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+sys.path.insert(0, os.path.abspath('../multipletau'))
+sys.path.insert(0, os.path.abspath('../'))
+
+sys.path.append(os.path.abspath('_extensions'))
+
+import multipletau
+# -- General configuration ------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+#extensions = [
+#    'sphinx.ext.autodoc',
+#    'sphinx.ext.doctest',
+#    'sphinx.ext.coverage',
+#    'sphinx.ext.pngmath',
+#    'sphinx.ext.viewcode',
+#]
+
+
+extensions = [
+#              'matplotlib.sphinxext.mathmpl',
+#              'matplotlib.sphinxext.only_directives',
+#              'matplotlib.sphinxext.plot_directive',
+#              'sphinx.ext.viewcode',
+#               'ipython_directive',
+              'sphinx.ext.intersphinx',
+#               'sphinx.ext.autosummary',
+              'sphinx.ext.autodoc',
+#              'sphinx.ext.doctest',
+#              'ipython_console_highlighting',
+#               'sphinx.ext.pngmath',
+              'sphinx.ext.mathjax',
+#              'sphinx.ext.todo',
+#             'inheritance_diagram',
+              'numpydoc',
+#              'hidden_code_block',
+#              'sphinx.ext.autosummary'
+              ]
+
+
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'multipletau'
+copyright = u'2014, Paul Müller'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = multipletau.__version__
+# The full version, including alpha/beta/rc tags.
+release = multipletau.__version__
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all
+# documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+#pygments_style = 'default'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+
+# -- Options for HTML output ----------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+html_theme = 'nature'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+#html_static_path = ['_static']
+
+# Add any extra paths that contain custom files (such as robots.txt or
+# .htaccess) here, relative to this directory. These files are copied
+# directly to the root of the documentation.
+#html_extra_path = []
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'multipletaudoc'
+
+
+# -- Options for LaTeX output ---------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+  ('index', 'multipletau.tex', u'multipletau Documentation',
+   u'Paul Müller', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output ---------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    ('index', 'multipletau', u'multipletau Documentation',
+     [u'Paul Müller'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output -------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+  ('index', 'multipletau', u'multipletau Documentation',
+   u'Paul Müller', 'multipletau',
+   'A multiple-tau algorithm that utilizes numpy.',
+   'Numeric'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False
+
+# -----------------------------------------------------------------------------
+# intersphinx
+# -----------------------------------------------------------------------------
+_python_doc_base = 'http://docs.python.org/2.7'
+intersphinx_mapping = {
+    _python_doc_base: None,
+    'http://docs.scipy.org/doc/numpy': None,
+    'http://docs.scipy.org/doc/scipy/reference': None,
+}
+
diff --git a/doc/index.rst b/doc/index.rst
new file mode 100644
index 0000000..2ae9de5
--- /dev/null
+++ b/doc/index.rst
@@ -0,0 +1,11 @@
+multipletau reference
+=====================
+.. automodule:: multipletau
+   :members: autocorrelate, correlate, correlate_numpy
+
+
+testing multipletau
+===================
+.. automodule:: test
+   :members:
+
diff --git a/doc/make.bat b/doc/make.bat
new file mode 100644
index 0000000..cd549e4
--- /dev/null
+++ b/doc/make.bat
@@ -0,0 +1,242 @@
+ at ECHO OFF
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+	set SPHINXBUILD=sphinx-build
+)
+set BUILDDIR=_build
+set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
+set I18NSPHINXOPTS=%SPHINXOPTS% .
+if NOT "%PAPER%" == "" (
+	set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
+	set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
+)
+
+if "%1" == "" goto help
+
+if "%1" == "help" (
+	:help
+	echo.Please use `make ^<target^>` where ^<target^> is one of
+	echo.  html       to make standalone HTML files
+	echo.  dirhtml    to make HTML files named index.html in directories
+	echo.  singlehtml to make a single large HTML file
+	echo.  pickle     to make pickle files
+	echo.  json       to make JSON files
+	echo.  htmlhelp   to make HTML files and a HTML help project
+	echo.  qthelp     to make HTML files and a qthelp project
+	echo.  devhelp    to make HTML files and a Devhelp project
+	echo.  epub       to make an epub
+	echo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter
+	echo.  text       to make text files
+	echo.  man        to make manual pages
+	echo.  texinfo    to make Texinfo files
+	echo.  gettext    to make PO message catalogs
+	echo.  changes    to make an overview over all changed/added/deprecated items
+	echo.  xml        to make Docutils-native XML files
+	echo.  pseudoxml  to make pseudoxml-XML files for display purposes
+	echo.  linkcheck  to check all external links for integrity
+	echo.  doctest    to run all doctests embedded in the documentation if enabled
+	goto end
+)
+
+if "%1" == "clean" (
+	for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
+	del /q /s %BUILDDIR%\*
+	goto end
+)
+
+
+%SPHINXBUILD% 2> nul
+if errorlevel 9009 (
+	echo.
+	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+	echo.installed, then set the SPHINXBUILD environment variable to point
+	echo.to the full path of the 'sphinx-build' executable. Alternatively you
+	echo.may add the Sphinx directory to PATH.
+	echo.
+	echo.If you don't have Sphinx installed, grab it from
+	echo.http://sphinx-doc.org/
+	exit /b 1
+)
+
+if "%1" == "html" (
+	%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/html.
+	goto end
+)
+
+if "%1" == "dirhtml" (
+	%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
+	goto end
+)
+
+if "%1" == "singlehtml" (
+	%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
+	goto end
+)
+
+if "%1" == "pickle" (
+	%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can process the pickle files.
+	goto end
+)
+
+if "%1" == "json" (
+	%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can process the JSON files.
+	goto end
+)
+
+if "%1" == "htmlhelp" (
+	%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can run HTML Help Workshop with the ^
+.hhp project file in %BUILDDIR%/htmlhelp.
+	goto end
+)
+
+if "%1" == "qthelp" (
+	%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; now you can run "qcollectiongenerator" with the ^
+.qhcp project file in %BUILDDIR%/qthelp, like this:
+	echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PKSLiver.qhcp
+	echo.To view the help file:
+	echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PKSLiver.ghc
+	goto end
+)
+
+if "%1" == "devhelp" (
+	%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished.
+	goto end
+)
+
+if "%1" == "epub" (
+	%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The epub file is in %BUILDDIR%/epub.
+	goto end
+)
+
+if "%1" == "latex" (
+	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
+	goto end
+)
+
+if "%1" == "latexpdf" (
+	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+	cd %BUILDDIR%/latex
+	make all-pdf
+	cd %BUILDDIR%/..
+	echo.
+	echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+	goto end
+)
+
+if "%1" == "latexpdfja" (
+	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
+	cd %BUILDDIR%/latex
+	make all-pdf-ja
+	cd %BUILDDIR%/..
+	echo.
+	echo.Build finished; the PDF files are in %BUILDDIR%/latex.
+	goto end
+)
+
+if "%1" == "text" (
+	%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The text files are in %BUILDDIR%/text.
+	goto end
+)
+
+if "%1" == "man" (
+	%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The manual pages are in %BUILDDIR%/man.
+	goto end
+)
+
+if "%1" == "texinfo" (
+	%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
+	goto end
+)
+
+if "%1" == "gettext" (
+	%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
+	goto end
+)
+
+if "%1" == "changes" (
+	%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.The overview file is in %BUILDDIR%/changes.
+	goto end
+)
+
+if "%1" == "linkcheck" (
+	%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Link check complete; look for any errors in the above output ^
+or in %BUILDDIR%/linkcheck/output.txt.
+	goto end
+)
+
+if "%1" == "doctest" (
+	%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Testing of doctests in the sources finished, look at the ^
+results in %BUILDDIR%/doctest/output.txt.
+	goto end
+)
+
+if "%1" == "xml" (
+	%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The XML files are in %BUILDDIR%/xml.
+	goto end
+)
+
+if "%1" == "pseudoxml" (
+	%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
+	if errorlevel 1 exit /b 1
+	echo.
+	echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
+	goto end
+)
+
+:end
diff --git a/doc/make.py b/doc/make.py
new file mode 100644
index 0000000..adec117
--- /dev/null
+++ b/doc/make.py
@@ -0,0 +1,14 @@
+import os
+import sys
+sys.path.insert(0, '..')
+import shutil
+import fnmatch
+
+
+os.system('make html')
+
+# os.system('make latex')
+# olddir = os.curdir
+# os.chdir('_build\\latex\\')
+# os.system('pdflatex multipletau.tex')
+# os.chdir(olddir)
diff --git a/doc/testing_multipletau.png b/doc/testing_multipletau.png
new file mode 100644
index 0000000..c1bdb0e
Binary files /dev/null and b/doc/testing_multipletau.png differ
diff --git a/multipletau/__init__.py b/multipletau/__init__.py
new file mode 100644
index 0000000..79b5fbc
--- /dev/null
+++ b/multipletau/__init__.py
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+""" 
+    Provides a multiple-τ algorithm for Python 2.7 and Python 3.x and
+    requires the package :py:mod:`numpy`.
+
+    Multipe-τ correlation is computed on a logarithmic scale (less
+    data points are computed) and is thus much faster than conventional
+    correlation on a linear scale such as  :py:func:`numpy.correlate`.
+
+    Recommended literature: 
+    
+    - Klaus Schaetzel and Rainer Peters; *Noise on multiple-tau photon
+      correlation data*. Proc. SPIE 1430, Photon Correlation
+      Spectroscopy: Multicomponent Systems, 109 (June 1, 1991);    
+      http://doi.org/10.1117/12.44160
+      
+    - Thorsten Wohland, Rudolf Rigler, and Horst Vogel; *The Standard 
+      Deviation in Fluorescence Correlation Spectroscopy*. Biophysical
+      Journal, 80 (June 1, 2001);  
+      http://dx.doi.org/10.1016/S0006-3495(01)76264-9
+      
+    The source code of multipletau is available at
+    https://github.com/paulmueller/multipletau.
+"""
+from ._multipletau import *
+
+__version__ = "0.1.4"
+__author__ = "Paul Mueller"
+__email__ = "paul.mueller at biotec.tu-dresden.de"
+__license__ = "OpenBSD"
diff --git a/multipletau/_multipletau.py b/multipletau/_multipletau.py
new file mode 100755
index 0000000..159b2e9
--- /dev/null
+++ b/multipletau/_multipletau.py
@@ -0,0 +1,466 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+""" 
+    A multiple-τ algorithm for Python 2.7 and 3.x.
+    
+    Copyright (c) 2014 Paul Müller
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are
+    met:
+
+      1. Redistributions of source code must retain the above copyright
+         notice, this list of conditions and the following disclaimer.
+       
+      2. Redistributions in binary form must reproduce the above copyright
+         notice, this list of conditions and the following disclaimer in
+         the documentation and/or other materials provided with the
+         distribution.
+
+      3. Neither the name of multipletau nor the names of its contributors
+         may be used to endorse or promote products derived from this
+         software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INFRAE OR
+    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+
+from __future__ import division
+
+import numpy as np
+import warnings
+
+__all__ = ["autocorrelate", "correlate", "correlate_numpy"]
+
+
+def autocorrelate(a, m=16, deltat=1, normalize=False,
+                  copy=True, dtype=None):
+    """ 
+    Autocorrelation of a 1-dimensional sequence on a log2-scale.
+    
+    This computes the correlation according to 
+    :py:func:`numpy.correlate` for positive :math:`k`  on a base 2
+    logarithmic scale.
+
+        numpy.correlate(a, a, mode="full")[len(a)-1:]  
+
+        :math:`z_k = \Sigma_n a_n a_{n+k}`
+
+
+    Note that only the correlation in the positive direction is
+    computed.
+
+    Parameters
+    ----------
+    a : array_like
+        input sequence of real numbers
+    m : even integer
+        defines the number of points on one level, must be an
+        even integer
+    deltat : float
+        distance between bins
+    normalize : bool
+        normalize the result to the square of the average input
+        signal and the factor `M-k`.
+    copy : bool
+        copy input array, set to False to save memory
+    dtype : dtype, optional
+        The type of the returned array and of the accumulator in 
+        which the elements are summed.  By default, the dtype of 
+        `a` is used.
+
+
+    Returns
+    -------
+    autocorrelation : ndarray
+        Nx2 array containing lag time and autocorrelation
+
+
+    Notes
+    -----
+    The algorithm computes the correlation with the convention of the
+    curve decaying to zero.
+    
+    For experiments like e.g. fluorescence correlation spectroscopy,
+    the signal can be normalized to `M-k` by invoking:
+    
+           normalize = True
+
+    For emulating the numpy.correlate behavior on a logarithmic
+    scale (default behavior) use:
+
+           normalize = False
+
+
+    Examples
+    --------
+    >>> from numpy import dtype
+    >>> from multipletau import autocorrelate
+    >>> autocorrelate(range(42), m=2, dtype=dtype(float))
+    array([[  1.00000000e+00,   2.29600000e+04],
+           [  2.00000000e+00,   2.21000000e+04],
+           [  4.00000000e+00,   2.03775000e+04],
+           [  8.00000000e+00,   1.50612000e+04]])
+
+    """
+    traceavg = np.average(a)
+    if normalize and traceavg == 0:
+        raise ZeroDivisionError("Normalization not possible. "+
+                     "The average of the input *binned_array* is zero.")
+
+    trace = np.array(a, dtype=dtype, copy=copy)
+    dtype = trace.dtype
+
+    if dtype.kind in ["b","i","u"]:
+        warnings.warn("Converting input data type ({}) to float.".
+                      format(dtype))
+        dtype = np.dtype(float)
+        trace = np.array(a, dtype=dtype, copy=copy)
+    
+    # Complex data
+    if dtype.kind == "c":
+        raise NotImplementedError(
+              "Please use `multipletau.correlate` for complex data.")
+
+    
+    # Check parameters
+    if np.around(m/2) != m/2:
+        mold = 1*m
+        m = int((np.around(m/2)+1) * 2)
+        warnings.warn("Invalid value of m={}. Using m={} instead"
+                      .format(mold,m))
+    else:
+        m = int(m)
+
+    N = N0 = len(trace)
+    
+    # Find out the length of the correlation function.
+    # The integer k defines how many times we can average over
+    # two neighboring array elements in order to obtain an array of
+    # length just larger than m.
+    k = int(np.floor(np.log2(N/m)))
+
+    # In the base2 multiple-tau scheme, the length of the correlation
+    # array is (only taking into account values that are computed from
+    # traces that are just larger than m):  
+    lenG = np.int(np.floor(m + k*m/2))
+        
+    G = np.zeros((lenG, 2), dtype=dtype)
+
+    normstat = np.zeros(lenG, dtype=dtype)
+    normnump = np.zeros(lenG, dtype=dtype)
+    
+    # We use the fluctuation of the signal around the mean
+    if normalize:
+        trace -= traceavg
+    if N < 2*m:
+        # Otherwise the following for-loop will fail:
+        raise ValueError("len(binned_array) must be larger than 2m.")
+    ## Calculate autocorrelation function for first m bins
+    # Discrete convolution of m elements
+    for n in range(1,m+1):
+        G[n-1,0] = deltat * n
+        # This is the computationally intensive step
+        G[n-1,1] = np.sum(trace[:N-n]*trace[n:], dtype=dtype)
+        normstat[n-1] = N-n
+        normnump[n-1] = N
+    # Now that we calculated the first m elements of G, let us
+    # go on with the next m/2 elements.
+    # Check if len(trace) is even:
+    if N%2 == 1:
+        N -= 1
+    # Add up every second element
+    trace = (trace[:N:2]+trace[1:N+1:2])/2
+    N /= 2
+    ## Start iteration for each m/2 values
+    for step in range(1,k+1):
+        ## Get the next m/2 values via correlation of the trace
+        for n in range(1,int(m/2)+1):
+            idx = int(m + n - 1 + (step-1)*m/2)
+            if len(trace[:N-(n+m/2)]) == 0:
+                # This is a shortcut that stops the iteration once the
+                # length of the trace is too small to compute a corre- 
+                # lation. The actual length of the correlation function 
+                # does not only depend on k - We also must be able to 
+                # perform the sum with repect to k for all elements.
+                # For small N, the sum over zero elements would be
+                # computed here.
+                #
+                # One could make this for loop go up to maxval, where
+                #   maxval1 = int(m/2)
+                #   maxval2 = int(N-m/2-1)
+                #   maxval = min(maxval1, maxval2)
+                # However, we then would also need to find out which 
+                # element in G is the last element...
+                G = G[:idx-1]
+                normstat = normstat[:idx-1]
+                normnump = normnump[:idx-1]
+                # Note that this break only breaks out of the current
+                # for loop. However, we are already in the last loop
+                # of the step-for-loop. That is because we calculated
+                # k in advance.
+                break
+            else:
+                G[idx,0] = deltat * (n+m/2) * 2**step
+                # This is the computationally intensive step
+                G[idx,1] = np.sum(trace[:N-(n+m/2)]*trace[(n+m/2):],
+                                  dtype=dtype)
+                normstat[idx] = N-(n+m/2)
+                normnump[idx] = N
+        # Check if len(trace) is even:
+        if N%2 == 1:
+            N -= 1
+        # Add up every second element
+        trace = (trace[:N:2]+trace[1:N+1:2])/2
+        N /= 2
+
+    if normalize:
+        G[:,1] /= traceavg**2 * normstat
+    else:
+        G[:,1] *= N0/normnump 
+    
+    return G
+
+
+
+def correlate(a, v, m=16, deltat=1, normalize=False,
+              copy=True, dtype=None):
+    """ 
+    Cross-correlation of two 1-dimensional sequences
+    on a log2-scale.
+    
+    This computes the cross-correlation according to
+    :py:func:`numpy.correlate` for positive :math:`k`  on a base 2
+    logarithmic scale.
+    
+        numpy.correlate(a, v, mode="full")[len(a)-1:]
+        
+        :math:`z_k = \Sigma_n a_n v_{n+k}`
+    
+    Note that only the correlation
+    in the positive direction is computed.
+    
+    
+    Parameters
+    ----------
+    a, v : array_like
+        input sequences with equal length
+    m : even integer
+        defines the number of points on one level, must be an
+        even integer
+    deltat : float
+        distance between bins
+    normalize : bool
+        normalize the result to the square of the average input
+        signal and the factor `M-k`.
+    copy : bool
+        copy input array, set to False to save memory
+    dtype : dtype, optional
+        The type of the returned array and of the accumulator in 
+        which the elements are summed.  By default, the dtype of 
+        `a` is used.
+
+
+    Returns
+    -------
+    crosscorrelation : ndarray
+        Nx2 array containing lag time and cross-correlation
+        
+    
+    Notes
+    -----
+    The algorithm computes the correlation with the convention of the
+    curve decaying to zero.
+    
+    For experiments like e.g. fluorescence correlation spectroscopy,
+    the signal can be normalized to `M-k` by invoking:
+    
+           normalize = True
+
+    For emulating the numpy.correlate behavior on a logarithmic
+    scale (default behavior) use:
+
+           normalize = False
+
+
+    Examples
+    --------
+    >>> from numpy import dtype
+    >>> from multipletau import correlate
+    >>> correlate(range(42), range(1,43), m=2, dtype=dtype(float))
+    array([[  1.00000000e+00,   2.38210000e+04],
+           [  2.00000000e+00,   2.29600000e+04],
+           [  4.00000000e+00,   2.12325000e+04],
+           [  8.00000000e+00,   1.58508000e+04]])
+
+    """
+    ## See `autocorrelation` for better documented code.
+    traceavg1 = np.average(v)
+    traceavg2 = np.average(a)
+    if normalize and traceavg1*traceavg2 == 0:
+        raise ZeroDivisionError("Normalization not possible. "+
+                     "The average of the input *binned_array* is zero.")
+
+    trace1 = np.array(v, dtype=dtype, copy=copy)
+    dtype = trace1.dtype
+
+    if dtype.kind in ["b","i","u"]:
+        warnings.warn(
+              "Converting input data type ({}) to float.".format(dtype))
+        dtype = np.dtype(float)
+        trace1 = np.array(v, dtype=dtype, copy=copy)
+    
+    # Prevent traces from overwriting each other
+    if a is v:
+        # Force copying trace 2
+        copy = True
+        
+    trace2 = np.array(a, dtype=dtype, copy=copy)
+
+    # Complex data
+    if dtype.kind == "c":
+        trace1.imag *= -1
+   
+    # Check parameters
+    if np.around(m/2) != m/2:
+        mold = 1*m
+        m = int((np.around(m/2)+1) * 2)
+        warnings.warn("Invalid value of m={}. Using m={} instead"
+                      .format(mold,m))
+    else:
+        m = int(m)
+        
+    if len(a) != len(v):
+        raise ValueError("Input arrays must be of equal length.")
+        
+    N = N0 = len(trace1)
+    # Find out the length of the correlation function.
+    # The integer k defines how many times we can average over
+    # two neighboring array elements in order to obtain an array of
+    # length just larger than m.
+    k = int(np.floor(np.log2(N/m)))
+    
+    # In the base2 multiple-tau scheme, the length of the correlation
+    # array is (only taking into account values that are computed from
+    # traces that are just larger than m):   
+    lenG = np.int(np.floor(m + k*m/2))
+        
+    G = np.zeros((lenG, 2), dtype=dtype)
+    normstat = np.zeros(lenG, dtype=dtype)
+    normnump = np.zeros(lenG, dtype=dtype)
+    
+    # We use the fluctuation of the signal around the mean
+    if normalize:
+        trace1 -= traceavg1
+        trace2 -= traceavg2
+    if N < 2*m:
+        # Otherwise the following for-loop will fail:
+        raise ValueError("len(binned_array) must be larger than 2m.")
+    # Calculate autocorrelation function for first m bins
+    for n in range(1,m+1):
+        G[n-1,0] = deltat * n
+        G[n-1,1] = np.sum(trace1[:N-n]*trace2[n:])
+        normstat[n-1] = N-n
+        normnump[n-1] = N
+    # Check if len(trace) is even:
+    if N%2 == 1:
+        N -= 1
+    # Add up every second element
+    trace1 = (trace1[:N:2]+trace1[1:N+1:2])/2
+    trace2 = (trace2[:N:2]+trace2[1:N+1:2])/2
+    N /= 2
+
+    for step in range(1,k+1):
+        # Get the next m/2 values of the trace
+        for n in range(1,int(m/2)+1):
+            idx = int(m + n - 1 + (step-1)*m/2)
+            if len(trace1[:N-(n+m/2)]) == 0:
+                # Abort
+                G = G[:idx-1]
+                normstat = normstat[:idx-1]
+                normnump = normnump[:idx-1]
+                break
+            else:
+                G[idx,0] = deltat * (n+m/2) * 2**step
+                G[idx,1] = np.sum(trace1[:N-(n+m/2)]*trace2[(n+m/2):])
+                normstat[idx] = N-(n+m/2)
+                normnump[idx] = N
+
+        # Check if len(trace) is even:
+        if N%2 == 1:
+            N -= 1
+        # Add up every second element
+        trace1 = (trace1[:N:2]+trace1[1:N+1:2])/2
+        trace2 = (trace2[:N:2]+trace2[1:N+1:2])/2
+        N /= 2
+
+    if normalize:
+        G[:,1] /= traceavg1*traceavg2 * normstat
+    else:
+        G[:,1] *= N0/normnump 
+    
+    return G
+
+
+
+def correlate_numpy(a, v, deltat=1, normalize=False,
+                     dtype=None, copy=True):
+    """
+    Convenience function that wraps around numpy.correlate and
+    returns the data as multipletau.correlate does.
+        
+    Parameters
+    ----------
+    a, v : array_like
+        input sequences
+    deltat : float
+        distance between bins
+    normalize : bool
+        normalize the result to the square of the average input
+        signal and the factor (M-k). The resulting curve follows
+        the convention of decaying to zero for large lag times.
+    copy : bool
+        copy input array, set to False to save memory
+    dtype : dtype, optional
+        The type of the returned array and of the accumulator in 
+        which the elements are summed.  By default, the dtype of 
+        `a` is used.
+
+    Returns
+    -------
+    crosscorrelation : ndarray
+        Nx2 array containing lag time and cross-correlation
+    """
+    
+    avg = np.average(a)
+    vvg = np.average(v)
+    
+    if dtype is None:
+        dtype = a.dtype
+    
+    if len(a) != len(v):
+        raise ValueError("Arrays must be of same length.")
+
+    ab = np.array(a, dtype=dtype, copy=copy)
+    vb = np.array(v, dtype=dtype, copy=copy)
+
+    Gd = np.correlate(ab-avg, vb-vvg, mode="full")[len(ab)-1:]
+
+    if normalize:
+        N = len(Gd)
+        m = N - np.arange(N)
+        Gd /= m * avg * vvg
+    G = np.zeros((len(Gd),2))
+    G[:,1] = Gd        
+    G[:,0] = np.arange(len(Gd))*deltat
+    return G
+        
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..861a9f5
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,5 @@
+[egg_info]
+tag_build = 
+tag_date = 0
+tag_svn_revision = 0
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..783eb3a
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+# To create a distribution package for pip or easy-install:
+# python setup.py sdist
+from setuptools import setup, find_packages
+from os.path import join, dirname, realpath
+from warnings import warn
+
+import multipletau
+
+setup(
+    name='multipletau',
+    author='Paul Mueller',
+    author_email='paul.mueller at biotec.tu-dresden.de',
+    url='https://github.com/paulmueller/multipletau',
+    version=multipletau.__version__,
+    packages=['multipletau'],
+    package_dir={'multipletau': 'multipletau'},
+    license="OpenBSD",
+    description='A multiple-tau algorithm for Python/NumPy.',
+    long_description=open(join(dirname(__file__), 'README.txt')).read(),
+    install_requires=["NumPy >= 1.5.1"]
+    )
+
+
diff --git a/test/__init__.py b/test/__init__.py
new file mode 100644
index 0000000..a39687e
--- /dev/null
+++ b/test/__init__.py
@@ -0,0 +1,138 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+""" This module is used for testing the multiple-tau algorithm.
+    Executing this module as a package by invoking
+    
+        python -m test
+    
+    creates plots that illustrate the difference between 
+    :py:func:`multipletau.correlate` and :py:func:`numpy.correlate`.
+    
+    .. image:: testing_multipletau.png
+       :width: 500 px
+    
+"""
+
+from __future__ import division
+from __future__ import print_function
+
+import numpy as np
+
+__all__=["noise_exponential", "noise_cross_exponential"]
+
+def noise_exponential(N, tau=20, variance=1, deltat=1):
+    """
+       Generate exponentially correlated noise.
+       
+       Parameters
+       ----------
+       N : integer
+          Total number of samples
+       tau : float
+          Correlation time of the exponential in `deltat`
+       variance : float
+          Variance of the noise
+       deltat : float
+          Bin size of output array, defines the time scale of `tau`
+       
+       Returns
+       -------
+       a : ndarray
+          Exponentially correlated noise.
+    """
+    # time constant (inverse of correlationtime tau)
+    g = deltat/tau
+    # variance
+    s0 = variance
+    
+    # normalization factor (memory of the trace)
+    exp_g = np.exp(-g)
+    one_exp_g = 1-exp_g
+    z_norm_factor = np.sqrt(1-np.exp(-2*g))/one_exp_g
+    
+    # create random number array
+    # generates random numbers in interval [0,1)
+    randarray = np.random.random(N)
+    # make numbers random in interval [-1,1)
+    randarray = 2*(randarray-0.5)
+    
+    # simulate exponential random behavior
+    a = np.zeros(N)
+    a[0] = one_exp_g*randarray[0]
+    b = 1* a
+    for i in np.arange(N-1)+1:
+        a[i] = exp_g*a[i-1] + one_exp_g*randarray[i]
+    
+        # Solving the equation iteratively leads to this equation:
+        #j = np.arange(i)
+        #a[i] = a[0]*exp_g**(i) + \
+        #       one_exp_g)*np.sum(exp_g**(i-1-j)*randarray[1:i+1])
+        
+    a = a * z_norm_factor*s0
+    return a
+
+
+def noise_cross_exponential(N, tau=20, variance=1, deltat=1):
+    """
+       Generate exponentially cross-correlated noise.
+       
+       Parameters
+       ----------
+       N : integer
+          Total number of samples
+       tau : float
+          Correlation time of the exponential in `deltat`
+       variance : float
+          Variance of the noise
+       deltat : float
+          Bin size of output array, defines the time scale of `tau`
+       
+       Returns
+       -------
+       a, randarray : ndarrays
+          Array `a` has positive exponential correlation to the 'truly'
+          random array `randarray`.
+    """
+    # length of mean0 trace
+    N_steps = N
+    # time constant (inverse of correlationtime tau)
+    g = deltat/tau
+    # variance
+    s0 = variance
+    # normalization factor (memory of the trace)
+    exp_g = np.exp(-g)
+    one_exp_g = 1-exp_g
+    z_norm_factor = np.sqrt(1-np.exp(-2*g))/one_exp_g
+    
+    # create random number array
+    # generates random numbers in interval [0,1)
+    randarray = np.random.random(N)
+    # make numbers random in interval [-1,1)
+    randarray = 2*(randarray-0.5)
+    
+    # simulate exponential random behavior
+    a = np.zeros(N)
+    a[0] = one_exp_g*randarray[0]
+    
+    b = np.zeros(N)
+    b[0] = one_exp_g*randarray[0]
+    # slow
+    #for i in np.arange(N-1)+1:
+    #    for j in np.arange(i-1):
+    #        a[i] += exp_g**j*randarray[i-j]
+    #    a[i] += one_exp_g*randarray[i]
+    # faster
+    j = np.arange(N+5)
+    for i in np.arange(N-1)+1:
+        a[i] += np.sum(exp_g**j[2:i+1] * randarray[2:i+1][::-1])
+        a[i] += one_exp_g*randarray[i]
+   
+    a *= z_norm_factor*s0
+    randarray = randarray * z_norm_factor*s0
+    
+    return a, randarray
+
+def test():
+    from .__main__ import test
+    test()
+    
diff --git a/test/__main__.py b/test/__main__.py
new file mode 100644
index 0000000..a1f7bc9
--- /dev/null
+++ b/test/__main__.py
@@ -0,0 +1,108 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+from .__init__ import noise_exponential, noise_cross_exponential
+
+def test():
+    import numpy as np
+    import os
+    import sys
+    from matplotlib import pylab as plt
+    sys.path.append(os.path.realpath(os.path.dirname(__file__)+"/../"))
+    from multipletau import autocorrelate, correlate, correlate_numpy
+    ## Starting parameters
+    N = np.int(np.pi*1e3)
+    countrate = 250. * 1e-3 # in Hz
+    taudiff = 55. # in us
+    deltat = 2e-6 # time discretization [s]
+    normalize = True
+
+    # time factor
+    taudiff *= deltat
+
+    ##
+    ## Autocorrelation
+    ##
+    print("Creating noise for autocorrelation")
+    data = noise_exponential(N, taudiff, deltat=deltat)
+    data += - np.average(data)
+    if normalize:
+        data += countrate
+    # multipletau
+    print("Performing autocorrelation (multipletau).")
+    G = autocorrelate(data, deltat=deltat, normalize=normalize)
+    # numpy.correlate for comparison
+    if len(data) < 1e5:
+        print("Performing autocorrelation (numpy).")
+        Gd = correlate_numpy(data, data, deltat=deltat,
+                             normalize=normalize)
+    # Calculate the expected curve
+    x = G[:,0]
+    amp = np.correlate(data-np.average(data), data-np.average(data),
+                       mode="valid")
+    if normalize:
+        amp /= len(data) * countrate**2
+    y = amp*np.exp(-x/taudiff)
+
+    ##
+    ## Cross-correlation
+    ##
+    print("Creating noise for cross-correlation")
+    a, v = noise_cross_exponential(N, taudiff, deltat=deltat)
+    a += - np.average(a)
+    v += - np.average(v)
+    if normalize:
+        a += countrate
+        v += countrate
+    # multipletau
+    Gccforw = correlate(a, v, deltat=deltat, normalize=normalize)
+    Gccback = correlate(v, a, deltat=deltat, normalize=normalize)
+    if len(a) < 1e5:
+        print("Performing autocorrelation (numpy).")
+        Gdccforw = correlate_numpy(a, v, deltat=deltat, normalize=normalize)
+    # Calculate the expected curve
+    xcc = Gccforw[:,0]
+    ampcc = np.correlate(a-np.average(a), v-np.average(v), mode="valid")
+
+    if normalize:
+        ampcc /= len(a) * countrate**2
+    ycc = ampcc*np.exp(-xcc/taudiff)
+
+
+    ##
+    ## Plotting
+    ##
+
+    # AC
+    fig = plt.figure()
+    fig.canvas.set_window_title('testing multipletau')
+    ax = fig.add_subplot(2,1,1)
+    ax.set_xscale('log')
+    plt.plot(x, y, "g-", label="input model")
+    plt.plot(G[:,0], G[:,1], "r-", label="autocorrelate")
+    if len(data) < 1e5:
+        plt.plot(Gd[:,0], Gd[:,1] , "b--", label="correlate (numpy)")
+    plt.xlabel("lag channel")
+    plt.ylabel("autocorrelation")
+    plt.legend(loc=0, fontsize='small')
+    plt.ylim( -amp*.2, amp*1.2)
+
+
+    ## CC
+    ax = fig.add_subplot(2,1,2)
+    ax.set_xscale('log')
+    plt.plot(xcc, ycc, "g-", label="input model")
+    plt.plot(Gccforw[:,0], Gccforw[:,1], "r-", label="forward")
+    if len(data) < 1e5:
+        plt.plot(Gdccforw[:,0], Gdccforw[:,1] , "b--", label="forward (numpy)")
+    plt.plot(Gccback[:,0], Gccback[:,1], "r--", label="backward")
+    plt.xlabel("lag channel")
+    plt.ylabel("cross-correlation")
+    plt.legend(loc=0, fontsize='small')
+
+    plt.ylim( -ampcc*.2, ampcc*1.2)
+
+    plt.tight_layout()
+    plt.show()
+
+if __name__ == '__main__':
+    test()

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/python-multipletau.git



More information about the debian-med-commit mailing list