[yoda-svn] r514 - in trunk: . bin include/YODA pyext pyext/yoda pyext/yoda/include

blackhole at projects.hepforge.org blackhole at projects.hepforge.org
Fri Aug 3 03:14:45 BST 2012


Author: davemallows
Date: Fri Aug  3 03:14:44 2012
New Revision: 514

Log:
Rewrite and restructuring of python extension.

Numerous improvements, including the removal of operator shims;
increased inheritance and vastly improved memory handling. There are a
few regressions, notably some missing docstrings and the removal of
2D/3D AnalysisObjects. There have been some tidyings of the interface to
provide a more Pythonic style.

Added:
   trunk/pyext/Makefile.am
   trunk/pyext/setup.py.in
   trunk/pyext/template.py
   trunk/pyext/yoda/
   trunk/pyext/yoda/core.pyx
   trunk/pyext/yoda/declarations.pxd
   trunk/pyext/yoda/errors.cpp
   trunk/pyext/yoda/errors.hh
   trunk/pyext/yoda/include/
   trunk/pyext/yoda/include/AnalysisObject.pyx
   trunk/pyext/yoda/include/Annotations.pyx
   trunk/pyext/yoda/include/Axis1D_BIN1D_DBN.pyx
   trunk/pyext/yoda/include/Axis2D_BIN2D_DBN.pyx
   trunk/pyext/yoda/include/Bin.pyx
   trunk/pyext/yoda/include/Bin1D_DBN.pyx
   trunk/pyext/yoda/include/Bin2D_DBN.pyx
   trunk/pyext/yoda/include/Dbn1D.pyx
   trunk/pyext/yoda/include/Dbn2D.pyx
   trunk/pyext/yoda/include/Dbn3D.pyx
   trunk/pyext/yoda/include/Errors.pyx
   trunk/pyext/yoda/include/Histo1D.pyx
   trunk/pyext/yoda/include/Histo2D.pyx
   trunk/pyext/yoda/include/HistoBin1D.pyx
   trunk/pyext/yoda/include/HistoBin2D.pyx
   trunk/pyext/yoda/include/IO.pyx
   trunk/pyext/yoda/include/Point2D.pyx
   trunk/pyext/yoda/include/Point3D.pyx
   trunk/pyext/yoda/include/Profile1D.pyx
   trunk/pyext/yoda/include/Profile2D.pyx
   trunk/pyext/yoda/include/ProfileBin1D.pyx
   trunk/pyext/yoda/include/ProfileBin2D.pyx
   trunk/pyext/yoda/include/Scatter2D.pyx
   trunk/pyext/yoda/util.pxd
   trunk/pyext/yoda/util.pyx
Replaced:
   trunk/pyext/
Modified:
   trunk/ChangeLog
   trunk/TODO
   trunk/bin/yoda2aida
   trunk/configure.ac
   trunk/include/YODA/Profile1D.h
   trunk/include/YODA/ProfileBin2D.h

Modified: trunk/ChangeLog
==============================================================================
--- trunk/ChangeLog	Tue Jul 31 16:06:27 2012	(r513)
+++ trunk/ChangeLog	Fri Aug  3 03:14:44 2012	(r514)
@@ -1,3 +1,10 @@
+2012-08-02  Dave Mallows  <dave.mallows at gmail.com>
+	* Heavily refactored Cython bindings
+
+	* HistoBin1D, ProfileBin1D etc. now inherit from Bin1D[DBN]
+
+	* Temporarily removed Histo2D, Profile2D and Scatter3D mappings.
+
 2012-07-23  Andy Buckley  <andy.buckley at cern.ch>
 
 	* Installing scripts from bin dir, and making the yoda2aida interface nicer.

Modified: trunk/TODO
==============================================================================
--- trunk/TODO	Tue Jul 31 16:06:27 2012	(r513)
+++ trunk/TODO	Fri Aug  3 03:14:44 2012	(r514)
@@ -10,10 +10,6 @@
    Ignore anything after "#" on data lines -- use this to write convenience
    height & error info for each bin since not obvious from sumWX2 etc.
 
-* Remove nasty Cython shims (DM)
-   Does inheritance work automatically in Cython now? Add operator support
-   everywhere it's needed.
-
 * Add Axis2D -> Histo2D/Profile2D bin adding and erasing. (AB)
 
 * Make Python interface test scripts (DM)
@@ -22,6 +18,8 @@
 
 * Docstrings for Points, Dbns and Bins (AB)
 
+* Reintroduce missing docstrings (DM)
+
 * Add stdErr to HistoBinXD (AB)
 
 * Add Dbn3D, ProfileBin2D, Profile2D and Scatter3D in Cython (AB)

Modified: trunk/bin/yoda2aida
==============================================================================
--- trunk/bin/yoda2aida	Tue Jul 31 16:06:27 2012	(r513)
+++ trunk/bin/yoda2aida	Fri Aug  3 03:14:44 2012	(r514)
@@ -25,5 +25,5 @@
     sys.stderr.write("You must specify the YODA and AIDA file names\n")
     sys.exit(1)
 
-analysisobjects = yoda.ReaderYODA().read(INFILE)
-yoda.WriterAIDA().write(OUTFILE, analysisobjects)
+analysisobjects = yoda.readYODA(INFILE)
+yoda.WriteAIDA(analysisobjects, OUTFILE)

Modified: trunk/configure.ac
==============================================================================
--- trunk/configure.ac	Tue Jul 31 16:06:27 2012	(r513)
+++ trunk/configure.ac	Fri Aug  3 03:14:44 2012	(r514)
@@ -48,7 +48,7 @@
 ## Basic Python checks
 if test x$enable_pyext == xyes; then
   AZ_PYTHON_PATH
-  AZ_PYTHON_VERSION_ENSURE([2.4])
+  AZ_PYTHON_VERSION_ENSURE([2.6])
   PYTHON_VERSION=`$PYTHON -c "import sys; print '.'.join(map(str, sys.version_info@<:@:2@:>@));"`
   AC_SUBST(PYTHON_VERSION)
   RIVET_PYTHONPATH=`$PYTHON -c "import distutils.sysconfig; print distutils.sysconfig.get_python_lib(prefix='$prefix', plat_specific=True);"`
@@ -74,10 +74,9 @@
 
 ## Cython checks (Probably need some help...)
 if test x$enable_pyext == xyes; then
-  cython_required_version=0.13
-  AM_CHECK_CYTHON([$cython_required_version], [:], [:])
-  if test "x$CYTHON_FOUND" != "xyes"; then
-    AC_MSG_ERROR([Can't build Python extension since Cython >= 0.13 could not be found])
+  AM_CHECK_CYTHON([0.16], [:], [:])
+  if test x$CYTHON_FOUND != xyes; then
+    AC_MSG_ERROR([Can't build Python extension since Cython >= 0.16 could not be found])
     enable_pyext=no
   else
     cython_compiler=$CXX
@@ -142,7 +141,7 @@
 AC_CONFIG_FILES([src/Makefile src/tinyxml/Makefile])
 AC_CONFIG_FILES([tests/Makefile])
 AC_CONFIG_FILES([bin/Makefile])
-AC_CONFIG_FILES([pyext/Makefile pyext/yoda/Makefile pyext/setup.py])
+AC_CONFIG_FILES([pyext/Makefile pyext/setup.py])
 #AC_CONFIG_FILES([bin/Makefile bin/yoda-config])
 
 AC_OUTPUT

Modified: trunk/include/YODA/Profile1D.h
==============================================================================
--- trunk/include/YODA/Profile1D.h	Tue Jul 31 16:06:27 2012	(r513)
+++ trunk/include/YODA/Profile1D.h	Fri Aug  3 03:14:44 2012	(r514)
@@ -147,8 +147,8 @@
     }
 
     /// Bin addition operator
-    void addBin(size_t from, size_t to) {
-      _axis.addBin(from, to);
+    void addBin(double xlow, double xhigh) {
+      _axis.addBin(xlow, xhigh);
     }
 
     /// Bin addition operator

Modified: trunk/include/YODA/ProfileBin2D.h
==============================================================================
--- trunk/include/YODA/ProfileBin2D.h	Tue Jul 31 16:06:27 2012	(r513)
+++ trunk/include/YODA/ProfileBin2D.h	Fri Aug  3 03:14:44 2012	(r514)
@@ -106,6 +106,10 @@
       return _dbn.zStdErr();
     }
 
+    double rms() const {
+      return _dbn.zRMS();
+    }
+
     //@}
 
     /// @name Raw z distribution statistics

Added: trunk/pyext/Makefile.am
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/Makefile.am	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,25 @@
+if ENABLE_PYEXT
+
+SUBDIRS = .
+CYTHONFLAGS = @CYTHONFLAGS@
+
+all-local:
+	$(PYTHON) setup.py build
+	$(PYTHON) setup.py install --install-lib=build/
+
+install-exec-local:
+	$(PYTHON) setup.py install --prefix=$(DESTDIR)$(prefix)
+
+uninstall-local:
+	rm -rf $(DESTDIR)$(YODA_PYTHONPATH)/yoda
+
+clean-local:
+	$(PYTHON) setup.py clean --all
+	@rm -f $(top_builddir)/*.pyc
+	@rm -rf $(builddir)/build
+	@rm -rf dist
+
+distclean-local:
+	@rm -rf yoda.egg-info
+
+endif

Added: trunk/pyext/setup.py.in
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/setup.py.in	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,53 @@
+# setup.py
+
+from distutils.core import setup
+from distutils.extension import Extension
+from Cython.Distutils import build_ext
+from glob import glob
+from template import make_templates
+
+PKGNAME = 'yoda'
+
+## Extension definition
+import os.path
+incdir = os.path.abspath('@top_srcdir@/include')
+boostdir = os.path.abspath('@BOOSTINCPATH@')
+srcdir = os.path.abspath('@top_srcdir@/src')
+
+statics = os.path.join('yoda', 'errors.cpp')
+
+# A helper function (since we have two modules now...)
+def ext(name, depends=[], statics=[]):
+    return Extension(
+        '%s.%s' % (PKGNAME, name),
+        ['%s/%s.cpp' % (PKGNAME, name)] + statics,
+        language='c++',
+        depends=depends,
+        include_dirs=[incdir, PKGNAME, boostdir],
+        library_dirs=[srcdir, os.path.join(srcdir, '.libs')],
+        libraries=['stdc++', 'YODA'])
+ 
+extns = [ext('util'),
+         ext('core', statics=[statics],
+             depends=glob('yoda/include/*.pyx')
+            ),
+        ]
+
+# Make the templates
+make_templates('Axis1D_BIN1D_DBN',
+         dict(DBN='Dbn2D', BIN1D='ProfileBin1D'),
+         dict(DBN='Dbn1D', BIN1D='HistoBin1D'))
+
+make_templates('Axis2D_BIN2D_DBN',
+         dict(DBN='Dbn3D', BIN2D='ProfileBin2D'),
+         dict(DBN='Dbn2D', BIN2D='HistoBin2D'))
+
+make_templates('Bin1D_DBN', DBN=('Dbn1D', 'Dbn2D'))
+make_templates('Bin2D_DBN', DBN=('Dbn2D', 'Dbn3D'))
+
+setup(
+    name = PKGNAME,
+    ext_modules = extns,
+    cmdclass = dict(build_ext=build_ext),
+    packages = [PKGNAME]
+)

Added: trunk/pyext/template.py
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/template.py	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,67 @@
+from string import Template
+from itertools import product, izip
+
+# A total mess. Uses python's string.Template to substitute values in various
+# templated C++ wrappers, as Cython has no support for templates (and fused
+# types are currently useless).
+
+import os.path
+INCLUDE_DIR = 'include'
+GENERATED_DIR = os.path.join(INCLUDE_DIR, 'generated')
+BASE_DIR = 'yoda'
+
+def path(path):
+    return os.path.join(BASE_DIR, path)
+
+# Try and make the template (output) directory
+try:
+    os.mkdir(path(GENERATED_DIR))
+except OSError:
+    pass
+
+GENERATED_HEADER = Template("""
+
+#################################################
+#############       WARNING      ################
+#################################################
+# This file has been automatically generated.   
+# Any changes you make here will get overridden.
+# Instead, make your changes in 
+#               ${filename}.pyx
+#################################################
+
+""".lstrip())
+
+def make_templates(filename, *args, **kwargs):
+    individuals = args or [dict(izip(kwargs, line)) for line in 
+                           product(*kwargs.itervalues())]
+
+    full_filename = os.path.join(INCLUDE_DIR, filename + '.pyx')
+    with open(path(full_filename)) as f:
+        template = Template(f.read())
+
+    plain_name = filename
+    for i in individuals[0]:
+        plain_name = plain_name.replace(i, '')
+    plain_name = plain_name.rstrip('_')
+    #print plain_name
+
+    includes = []
+    for a in individuals:
+
+        newname = filename
+        for i, j in a.iteritems(): 
+            newname = newname.replace(i, j)
+
+        out_filename = os.path.join(GENERATED_DIR, newname + '.pyx')
+        with open(path(out_filename), 'w') as f:
+            f.write(GENERATED_HEADER.substitute(filename=filename))
+            f.write(template.substitute(**a))
+        includes.append(out_filename)
+        
+        inc_filename = os.path.join(INCLUDE_DIR, plain_name + '.pxi')
+
+        with open(path(inc_filename), 'w') as f:
+            f.write(GENERATED_HEADER.substitute(filename=filename))
+            f.write("\n".join('include "%s"' % i for i in includes))
+

Added: trunk/pyext/yoda/core.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/core.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,49 @@
+cimport yoda.declarations as c
+cimport yoda.util as util
+import yoda.util as util
+from cython.operator cimport dereference as deref, preincrement as preinc
+from libcpp.string cimport string
+from libcpp.vector cimport vector
+from libcpp.pair cimport pair
+from libcpp.map cimport map
+
+# Pure python imports
+from itertools import repeat, imap
+from operator import attrgetter
+
+
+include "include/Errors.pyx"
+include "include/Dbn1D.pyx"
+include "include/Dbn2D.pyx"
+include "include/Dbn3D.pyx"
+include "include/Point2D.pyx"
+include "include/Point3D.pyx"
+include "include/Bin.pyx"
+
+include "include/Axis1D.pxi"
+
+include "include/Bin1D.pxi"
+
+include "include/HistoBin1D.pyx"
+
+include "include/ProfileBin1D.pyx"
+
+include "include/Annotations.pyx"
+
+include "include/AnalysisObject.pyx"
+
+include "include/Histo1D.pyx"
+
+include "include/Profile1D.pyx"
+
+include "include/IO.pyx"
+
+include "include/Scatter2D.pyx"
+
+#include "include/Bin2D.pxi"
+#include "include/ProfileBin2D.pyx"
+#include "include/HistoBin2D.pyx"
+#include "include/Histo2D.pyx"
+#include "include/Profile2D.pyx"
+
+#include "include/Axis2D.pxi"

Added: trunk/pyext/yoda/declarations.pxd
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/declarations.pxd	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,840 @@
+from libcpp.map cimport map
+from libcpp.pair cimport pair
+from libcpp.vector cimport vector
+from libcpp cimport bool
+from libcpp.string cimport string
+from cython.operator cimport dereference as deref
+
+# Import the error handling C++ routine
+cdef extern from "errors.hh":
+    # Have a look in errors.cpp for implementation specifics
+    void err "translate_yoda_error" ()
+
+ctypedef map[string, string] Annotations
+
+
+# Distributions
+
+# Dbn1D {{{
+cdef extern from "YODA/Dbn1D.h" namespace "YODA":
+    cdef cppclass Dbn1D:
+        Dbn1D ()
+        Dbn1D (Dbn1D)
+
+        void fill(double val, double weight)
+        void reset()
+        void scaleW(double)
+        void scaleX(double)
+
+
+        double xMean() except+ err
+        double xVariance() except+ err
+        double xStdDev() except+ err
+        double xStdErr() except+ err
+        double xRMS() except+ err
+
+        # Raw distribution running sums
+        unsigned long numEntries() except+ err
+        unsigned long effNumEntries() except+ err
+        double sumW() except+ err
+        double sumW2() except+ err
+        double sumWX() except+ err
+        double sumWX2() except+ err
+
+        # Operators: TODO when Cython supports += and -= etc.
+        Dbn1D operator+ (Dbn1D) except+ err
+        Dbn1D operator- (Dbn1D) except+ err
+#}}} Dbn1D
+
+# Dbn2D {{{
+cdef extern from "YODA/Dbn2D.h" namespace "YODA":
+    cdef cppclass Dbn2D:
+        Dbn2D ()
+        Dbn2D (Dbn2D) 
+        
+        void fill(double x, double y, double weight) except+ err
+        void reset() except+ err
+        void scaleW(double) except+ err
+        void scaleX(double) except+ err
+        void scaleY(double) except+ err
+        void scaleXY(double, double) except+ err
+
+        double xMean() except+ err
+        double xVariance() except+ err
+        double xStdDev() except+ err
+        double xStdErr() except+ err
+        double xRMS() except+ err
+
+        double yMean() except+ err
+        double yVariance() except+ err
+        double yStdDev() except+ err
+        double yStdErr() except+ err
+        double yRMS() except+ err
+        
+        # Raw distribution running sums
+        unsigned long numEntries() except+ err
+        unsigned long effNumEntries() except+ err
+        double sumW() except+ err
+        double sumW2() except+ err
+        double sumWX() except+ err
+        double sumWX2() except+ err
+        double sumWY() except+ err
+        double sumWY2() except+ err
+        double sumWXY() except+ err
+
+        # Operators
+        void flipXY() except+ err
+        Dbn1D transformX() except+ err
+        Dbn1D transformY() except+ err
+
+        Dbn2D operator + (Dbn2D) except+ err
+        Dbn2D operator - (Dbn2D) except+ err
+        # TODO: += and -= operators in Cython (maybe 0.17?)
+#}}} Dbn2D
+
+# Dbn3D {{{
+cdef extern from "YODA/Dbn3D.h" namespace "YODA":
+    cdef cppclass Dbn3D:
+        Dbn3D ()
+        Dbn3D (Dbn3D)
+        void fill(double x, double y, double z, double weight)
+        void reset()
+
+        void scaleW(double)
+        void scaleX(double)
+        void scaleY(double)
+        void scaleZ(double)
+        void scaleXY(double, double)
+        void scaleXYZ(double, double, double)
+
+        # Each of these statistics can return a 3D named tuple
+        double xMean()
+        double xVariance()
+        double xStdDev()
+        double xStdErr()
+        double xRMS()
+
+        double yMean()
+        double yVariance()
+        double yStdDev()
+        double yStdErr()
+        double yRMS()
+
+        double zMean()
+        double zVariance()
+        double zStdDev()
+        double zStdErr()
+        double zRMS()
+
+        
+        # Raw distribution running sums
+        unsigned long numEntries()
+        unsigned long effNumEntries()
+        double sumW()
+        double sumW2()
+
+        double sumWX()
+        double sumWX2()
+
+        double sumWY()
+        double sumWY2()
+
+        double sumWZ()
+        double sumWZ2()
+
+        double sumWXY()
+        double sumWXZ()
+        double sumWYZ()
+
+        double sumWXYZ()
+
+        # Operators
+        void flipXY()
+        void flipXZ()
+        void flipYZ()
+
+        Dbn1D transformX()
+        Dbn1D transformY()
+        Dbn1D transformZ()
+
+        Dbn3D operator + (Dbn3D)
+        Dbn3D operator - (Dbn3D)
+        # TODO: += and -= operators in Cython (maybe 0.17?)
+#}}} Dbn3D
+
+# Points
+
+# Point2D {{{
+cdef extern from "YODA/Point2D.h" namespace "YODA":
+    cdef cppclass Point2D:
+        Point2D () except+ err
+        Point2D (Point2D p) except+ err
+
+        Point2D (double x, double y,
+                  double exminus, double explus,
+                 double eyminus, double eyplus) except+ err
+
+        double x() except+ err
+        double y() except+ err
+        void setX(double x) except+ err
+        void setY(double y) except+ err
+        double xErrAvg() except+ err
+        double yErrAvg() except+ err
+        pair[double,double] xErrs() except+ err
+        pair[double,double] yErrs() except+ err
+        void setXErr(pair[double, double]) except+ err
+        void setYErr(pair[double, double]) except+ err
+        double xMin() except+ err
+        double xMax() except+ err
+        void scale(double x, double y) except+ err
+        bool operator == (Point2D) except+ err
+        bool operator != (Point2D b) except+ err
+        bool operator < (Point2D b) except+ err
+        bool operator <= (Point2D b) except+ err
+        bool operator > (Point2D b) except+ err
+        bool operator >= (Point2D b) except+ err
+# }}} Point2D
+
+# Point3D {{{
+cdef extern from "YODA/Point3D.h" namespace "YODA":
+    cdef cppclass Point3D:
+        Point3D () except+ err
+        Point3D (Point3D &p) except+ err
+
+        Point3D (double x, double y, double z,
+                 double exminus, double explus,
+                 double eyminus, double eyplus,
+                 double ezminus, double ezplus) except+ err
+
+        double x() except+ err
+        double y() except+ err
+        double z() except+ err
+        void setX(double x) except+ err
+        void setY(double y) except+ err
+        void setZ(double z) except+ err
+        double xMin() except+ err
+        double xMax() except+ err
+        double yMin() except+ err
+        double yMax() except+ err
+        double zMin() except+ err
+        double zMax() except+ err
+        pair[double,double] xErrs() except+ err
+        pair[double,double] yErrs() except+ err
+        pair[double,double] zErrs() except+ err
+        void setXErr(pair[double, double]) except+ err
+        void setYErr(pair[double, double]) except+ err
+        void setZErr(pair[double, double]) except+ err
+        double xErrAvg()
+        double yErrAvg()
+        double zErrAvg()
+        void scale(double x, double y, double z) except+ err
+
+        bool operator == (Point3D b)
+        bool operator != (Point3D b)
+        bool operator < (Point3D b)
+        bool operator <= (Point3D b)
+        bool operator > (Point3D b)
+        bool operator >= (Point3D b)
+#}}} Point3D
+
+# Bins
+
+# Bin {{{
+cdef extern from "YODA/Bin.h" namespace "YODA":
+    cdef cppclass Bin:
+        pass
+# }}} Bin
+
+#Bin1D {{{
+cdef extern from "YODA/Bin1D.h" namespace "YODA":
+    cdef cppclass Bin1D[DBN](Bin):
+        Bin1D(pair[double, double] edges) except+ err
+        Bin1D(pair[double, double] edges, DBN dbn) except+ err
+        Bin1D(Bin1D) except+ err
+
+        # THIS IS A CYTHON LIMITATION... DO NOT CALL THIS
+        Bin1D() # (DO NOT CALL THIS DO NOT CALL THIS) ###
+        #################################################
+
+        #We're fine as long as we don't try to instantiate these from Python
+
+        void scaleW(double scale) except+ err
+        void scaleX(double scale) except+ err
+        void reset()  except+ err
+
+        double lowEdge() except+ err
+        double highEdge()  except+ err
+        pair[double, double] edges() except+ err
+        double width() except+ err
+        double focus() except+ err
+        double midpoint() except+ err
+        
+        # x statistics
+        double xMean() except+ err
+        double xVariance() except+ err
+        double xStdDev() except+ err
+        double xStdErr() except+ err
+        double xRMS() except+ err
+
+        # raw statistics
+        unsigned long numEntries() except+ err
+        unsigned long effNumEntries() except+ err
+        double sumW() except+ err
+        double sumW2() except+ err
+        double sumWX() except+ err
+        double sumWX2() except+ err
+
+        void merge (Bin1D &) except+ err
+        Bin1D operator + (Bin1D &) except+ err
+        Bin1D operator - (Bin1D &) except+ err
+
+ctypedef Bin1D[Dbn1D] Bin1D_Dbn1D
+ctypedef Bin1D[Dbn2D] Bin1D_Dbn2D
+ctypedef Bin1D[Dbn3D] Bin1D_Dbn3D
+#}}} Bin1D
+
+# Bin2D {{{
+cdef extern from "YODA/Bin2D.h" namespace "YODA":
+    cdef cppclass Bin2D[DBN](Bin):
+        Bin2D(pair[double, double] xedges,
+              pair[double, double] yedges) except+ 
+        Bin2D(Bin2D bin) except+ err
+
+        # CYTHON HACK DO NOT CALL THIS IT DOES NOT EXIST
+        Bin2D() # (DO NOT CALL DO NOT CALL)
+        ################################################
+
+        void scaleW(double scale) except+ err
+        void scaleXY(double, double) except+ err
+        void reset()  except+ err
+
+        # Also xMin, xMax etc...
+        pair[double, double] xedges() except+ err
+        pair[double, double] yedges() except+ err
+
+        double widthX() except+ err
+        double widthY() except+ err
+
+        pair[double, double] focus() except+ err
+        pair[double, double] midpoint() except+ err
+        
+        # x statistics
+        double xMean() except+ err
+        double xVariance() except+ err
+        double xStdDev() except+ err
+        double xStdErr() except+ err
+        double xRMS() except+ err
+
+        double yMean() except+ err
+        double yVariance() except+ err
+        double yStdDev() except+ err
+        double yStdErr() except+ err
+        double yRMS() except+ err
+
+        # raw statistics
+        unsigned long numEntries() except+ err
+        unsigned long effNumEntries() except+ err
+        double sumW() except+ err
+        double sumW2() except+ err
+        double sumWX() except+ err
+        double sumWY() except+ err
+        double sumWXY() except+ err
+        double sumWX2() except+ err
+        double sumWY2() except+ err
+
+        #void merge(Bin2D) except+ err
+        Bin2D operator + (Bin2D) except+ err
+        Bin2D operator - (Bin2D) except+ err
+
+ctypedef Bin2D[Dbn2D] Bin2D_Dbn2D
+ctypedef Bin2D[Dbn3D] Bin2D_Dbn3D
+# }}} Bin2D
+
+# ProfileBin1D {{{
+cdef extern from "YODA/ProfileBin1D.h" namespace "YODA":
+
+    cdef cppclass ProfileBin1D(Bin1D_Dbn2D):
+        ProfileBin1D(ProfileBin1D) except +err
+        ProfileBin1D(double, double) except +err
+        void fill(double x, double y, double weight) except+ err
+        void fillBin(double y, double weight) except+ err
+        void reset() except+ err
+
+        double mean() except+ err
+        double stdDev() except+ err
+        double variance() except+ err
+        double stdErr() except+ err
+        double rms() except+ err
+
+        double sumWY() except+ err
+        double sumWY2() except+ err
+        ProfileBin1D operator + (ProfileBin1D) except+ err
+        ProfileBin1D operator - (ProfileBin1D) except+ err
+
+# }}} ProfileBin1D
+
+
+# ProfileBin2D {{{
+cdef extern from "YODA/ProfileBin2D.h" namespace "YODA":
+
+    cdef cppclass ProfileBin2D(Bin2D_Dbn3D):
+        ProfileBin2D (ProfileBin2D h) except+ err
+        ProfileBin2D (double, double, double, double) except+ err
+        void fill(double x, double y, double z, double weight) except+ err
+        void fillBin(double z, double weight) except+ err
+
+        double mean() except+ err
+        double stdDev() except+ err
+        double variance() except+ err
+        double stdErr() except+ err
+        double rms() except+ err
+
+        double sumWZ() except+ err
+        double sumWZ2() except+ err
+        ProfileBin2D operator + (ProfileBin2D) except+ err
+        ProfileBin2D operator - (ProfileBin2D) except+ err
+
+# }}} ProfileBin2D
+
+
+# HistoBin1D {{{
+cdef extern from "YODA/HistoBin1D.h" namespace "YODA":
+    cdef cppclass HistoBin1D(Bin1D_Dbn1D):
+        HistoBin1D(double lowedge, double highedge) except +err
+        HistoBin1D(HistoBin1D) except +err
+        void fill(double x, double weight) except +err
+        void fillBin(double weight) except +err
+
+        double area() except +err
+        double height() except +err
+        double areaErr() except +err
+        double heightErr() except +err
+
+        HistoBin1D operator+(HistoBin1D) except +err
+        HistoBin1D operator-(HistoBin1D) except +err
+
+
+#}}} HistoBin1D
+
+
+# HistoBin2D {{{
+cdef extern from "YODA/HistoBin2D.h" namespace "YODA":
+    cdef cppclass HistoBin2D(Bin2D_Dbn2D):
+        HistoBin2D(double xmin, double xmax,
+                   double ymin, double ymax) except +err
+        HistoBin2D(HistoBin2D) except +err
+
+        void fill(double x, double y, double weight) except +err
+        void fillBin(double weight) except +err
+        void reset()
+
+        # Accessors
+        double volume() except +err
+        double volumeErr() except +err
+        double height() except +err
+        double heightErr() except +err
+
+        HistoBin2D operator+(HistoBin2D) except +err
+        HistoBin2D operator-(HistoBin2D) except +err
+
+        #Bin2D_Dbn2D merge(HistoBin2D b)
+#}}} HistoBin2D
+
+# Analaysis Objects
+
+# AnalysisObject {{{
+cdef extern from "YODA/AnalysisObject.h" namespace "YODA":
+    cdef cppclass AnalysisObject:
+        # Constructors
+        AnalysisObject(string type,
+                       string path,
+                       string title) except+ err
+        AnalysisObject(string type,
+                       string path,
+                       AnalysisObject ao,
+                       string title) except+ err
+        AnalysisObject()
+
+        # Annotations
+        map[string, string] annotations() except+ err
+
+        bool hasAnnotation(string key) except+ err
+        string annotation(string key) except+ err
+        string annotation(string key, string default) except+ err
+        void setAnnotation(string, string) except+ err
+        void rmAnnotation(string name) except+ err
+        void clearAnnotations() except+ err
+
+        # Standard annotations
+        string title() except+ err
+        void setTitle(string title) except+ err
+        string path() except+ err
+        void setPath(string title) except+ err
+        string type() except +err
+# }}} AnalysisObject
+
+cdef extern from "YODA/Utils/sortedvector.h" namespace "YODA::Utils":
+    cdef cppclass sortedvector[T](vector):
+        sortedvector(vector[T]) except +err
+        void insert(T) except +err
+
+# TODO: forward declarations for bin-copying constructors
+
+# Scatter2D {{{
+cdef extern from "YODA/Scatter2D.h" namespace "YODA":
+    cdef cppclass Scatter2D(AnalysisObject):
+        Scatter2D(string path, string title) except+ err
+
+        Scatter2D(sortedvector[Point2D], 
+                string path,
+                string title) except+ err
+
+        Scatter2D(vector[double], vector[double],
+                  vector[pair[double, double]],
+                  vector[pair[double, double]]) except+ err
+
+        Scatter2D(Scatter2D p, string path)
+
+        void reset() except+ err
+
+        size_t numPoints() except+ err
+        Point2D &point(size_t index)
+
+        Scatter2D addPoint(Point2D&)
+        Scatter2D addPoint(double, double)
+        Scatter2D addPoint(double, double,
+                            pair[double, double], pair[double, double]) except+ err
+
+        Scatter2D addPoints(sortedvector[Point2D])
+
+        Scatter2D combineWith(Scatter2D)
+        Scatter2D combineWith(vector[Scatter2D])
+
+#}}} Scatter2D
+
+# Scatter3D {{{
+cdef extern from "YODA/Scatter3D.h" namespace "YODA":
+    cdef cppclass Scatter3D(AnalysisObject):
+        Scatter3D(string path, string title) except+ err
+        Scatter3D(sortedvector[Point3D], 
+                string path,
+                string title) except+ err
+
+        Scatter3D(vector[double], vector[double],
+                  vector[pair[double, double]],
+                  vector[pair[double, double]],
+                  vector[pair[double, double]]) except+ err
+
+        Scatter3D(Scatter3D p, string path)
+
+        void reset() except+ err
+
+        size_t numPoints() except+ err
+        sortedvector[Point3D] points() except+ err
+        Point3D point(size_t index) except+ err
+
+        Scatter3D addPoint(Point3D)
+        Scatter3D addPoint(double, double,
+                            pair[double, double], pair[double, double]) except+ err
+
+        Scatter3D addPoints(sortedvector[Point3D])
+
+        Scatter3D combineWith(Scatter3D)
+        Scatter3D combineWith(vector[Scatter3D])
+#}}} Scatter3D
+
+# Profile1D {{{
+cdef extern from "YODA/Profile1D.h" namespace "YODA":
+    cdef cppclass Profile1D(AnalysisObject):
+        Profile1D(string path, string title) except+ err
+        Profile1D(size_t nxbins,
+                double xlower,
+                double xupper,
+                string path,
+                string title) except+ err
+        Profile1D(vector[double] xbinedges,
+                string path,
+                string title) except+ err
+        Profile1D(Profile1D p, string path) except+ err
+        Profile1D(Scatter2D s, string path) except+ err
+        #Profile1D(Histo1D p, string path)
+
+        void fill(double x, double y, double weight) except+ err
+
+        void reset() except+ err
+
+        void scaleW(double s) except+ err
+
+        void mergeBins(size_t, size_t) except+ err
+        void rebin(int n) except+ err
+
+        void addBin(double, double) except+ err
+        void addBins(vector[double] edges) except+ err
+        # void eraseBin(size_t index) except+ err
+
+        size_t numBins() except+ err
+        double lowEdge() except+ err
+        double highEdge() except+ err
+
+        vector[ProfileBin1D] bins()
+        ProfileBin1D bin(size_t ix) except+ err
+        ProfileBin1D binByCoord(double x) except+ err
+
+        # The trick here is to treat these not as references.
+        # I suppose when you think about it, it makes sense
+        Dbn2D &totalDbn()
+        Dbn2D &underflow()
+        Dbn2D &overflow()
+
+        double sumW(bool)
+        double sumW2(bool)
+
+        operator + (Profile1D)
+        operator - (Profile1D)
+        operator / (Profile1D)
+
+#}}} Profile1D
+
+# Profile2D {{{
+cdef extern from "YODA/Profile2D.h" namespace "YODA":
+    cdef cppclass Profile2D(AnalysisObject):
+        Profile2D(string path, string title) except+ err
+        Profile2D(size_t nbinsX, double lowerX, double upperX,
+                  size_t nbinsY, double lowerY, double upperY,
+                  string path, string title) except+ err
+
+        Profile2D(vector[double] xedges, vector[double] yedges,
+                  string path, string title) except+ err
+
+        Profile2D(Profile2D p, string path) except+ err
+        #Profile2D(Scatter3D s, string path) except+ err
+        #Profile2D(Histo2D p, string path)
+
+        void fill(double x, double y, double z, double weight) except+ err
+
+        void reset() except+ err
+
+        void scaleW(double s) except+ err
+
+        #void mergeBins(size_t, size_t) except+ err
+        #void rebin(int n) except+ err
+
+        # void addBin(double, double) except+ err
+        # void addBins(vector[double] edges) except+ err
+        # void eraseBin(size_t index) except+ err
+
+        size_t numBins() except+ err
+        double lowEdge() except+ err
+        double highEdge() except+ err
+
+        vector[ProfileBin2D] bins()
+        ProfileBin2D bin(size_t ix) except+ err
+        ProfileBin2D binByCoord(double x) except+ err
+        int findBinIndex(double, double)
+
+        # The trick here is to treat these not as references.
+        # I suppose when you think about it, it makes sense
+        # -- Cython is a code generator
+        Dbn3D totalDbn() except+ err
+        Dbn3D outflow(int, int) except+ err
+
+        double sumW(bool)
+        double sumW2(bool)
+
+        operator + (Profile2D)
+        operator - (Profile2D)
+        operator / (Profile2D)
+
+#}}} Profile2D
+
+# Histo1D#{{{
+cdef extern from "YODA/Histo1D.h" namespace "YODA":
+    cdef cppclass Histo1D(AnalysisObject):
+        Histo1D() except +err
+        Histo1D(string path, string title) except+ err
+        Histo1D(size_t nbins,
+                double lower,
+                double upper,
+                string path,
+                string title) except+ err
+        Histo1D(vector[double] binedges,
+                string path,
+                string title) except+ err
+        Histo1D(vector[Bin] bins, string path, string title) except+ err
+        Histo1D(Histo1D h, string path) except+ err
+        #Histo1D(Profile1D p, string path)
+        #Histo1D(Scatter2D p, string path)
+
+        void fill(double x, double weight) except+ err
+        void reset() except+ err
+
+        void scaleW(double s) except+ err
+        void normalize(double normto, bool includeoverflows) except+ err
+        void mergeBins(size_t, size_t) except+ err
+        void rebin(int n) except+ err
+        size_t numBins() except+ err
+        double lowEdge() except+ err
+        double highEdge() except+ err
+
+        vector[HistoBin1D] &bins()
+        HistoBin1D &bin(size_t ix)
+        HistoBin1D binByCoord(double x) except+ err
+
+        # The trick here is to treat these not as references.
+        # I suppose when you think about it, it makes sense
+        Dbn1D &totalDbn()
+        Dbn1D &underflow()
+        Dbn1D &overflow()
+
+        # NB, this is bugged and does not correctly report identical bins.
+        void addBin(double, double) except+ err
+        void addBins(vector[double] edges) except+ err
+        void eraseBin(size_t index) except+ err
+#}}} Histo1D
+
+# Histo2D {{{
+cdef extern from "YODA/Histo2D.h" namespace "YODA":
+    cdef cppclass Histo2D(AnalysisObject):
+        Histo2D(string path, string title) except+ err
+
+        Histo2D(size_t nBinsX, double lowerX, double upperX,
+                size_t nBinsY, double lowerY, double upperY,
+                string path, string title) except+ err
+
+        Histo2D(vector[double] xedges, vector[double] yedges,
+                string path, string title) except+ err
+
+        Histo2D(Histo2D, string path)
+        #Histo2D(Profile1D p, string path)
+        #Histo2D(Scatter2D p, string path)
+
+        void fill(double x, double y, double weight) except+ err
+        void reset() except+ err
+
+        void scaleW(double scalefactor) except+ err
+        void normalize(double normto, bool includeoverflows) except+ err
+        void scaleXY(double, double)
+
+        void mergeBins(size_t, size_t) except+ err
+        void rebin(int n) except+ err
+        size_t numBins() except+ err
+
+        vector[HistoBin1D] bins()
+        HistoBin2D bin(size_t ix) except+ err
+        HistoBin2D binByCoord(double x) except+ err
+
+        # These must be treated as references or the semantics is wrong.
+        # However, these can also throw exceptions! But the two cannot mix, or
+        # Cython puts out rubbish C++. Since this *is* a reported 'major' bug,
+        # we should expect it to be fixed sometime in the future.
+        Dbn2D totalDbn() except +err
+        Dbn2D outflow(int, int) except +err
+
+        # Bin accessors
+        #void addBin(double, double) except+ err
+        #void addBins(vector[double] edges) except+ err
+        void eraseBin(size_t index) except+ err
+
+        double lowEdgeX() except+ err
+        double lowEdgeY() except+ err
+
+        double highEdgeX() except+ err
+        double highEdgeY() except+ err
+
+        int findBinIndex(double, double)
+        size_t numBinsX()
+        size_t numBinsY()
+
+        # Whole histo data
+        double integral(bool)
+        double sumW(bool)
+        double sumW2(bool)
+        double xMean(bool)
+        double yMean(bool)
+        double xVariance(bool)
+        double yVariance(bool)
+        double xStdDev(bool)
+        double yStdDev(bool)
+        double xStdErr(bool)
+        double yStdErr(bool)
+
+        operator == (Histo2D)
+        operator != (Histo2D)
+        operator + (Histo2D)
+        operator - (Histo2D)
+        operator / (Histo2D)
+# Histo2D }}}
+
+"""INTERNAL CLASSES -- FOR TESTING PURPOSES"""
+
+# Axis1D {{{
+cdef extern from "YODA/Axis1D.h" namespace "YODA":
+    cdef cppclass Axis1D[BIN1D, DBN]:
+        Axis1D() except+ err
+        Axis1D(vector[double] binedges) except+ err
+        Axis1D(size_t, double, double) except+ err
+        Axis1D(vector[BIN1D] bins) except+ err
+        size_t numBins() except+ err
+        vector[BIN1D] &bins()
+        double lowEdge() except+ err
+        double highEdge() except+ err
+        long getBinIndex(double)
+        void reset()
+        DBN &totalDbn()
+        DBN &underflow()
+        DBN &overflow()
+# Axis1D }}}
+
+# Axis2D {{{
+cdef extern from "YODA/Axis2D.h" namespace "YODA":
+    cdef cppclass Axis2D[BIN2D, DBN]:
+        Axis2D() except+ err
+        Axis2D(vector[double] xedges, vector[double] yedges) except+ err
+        Axis2D(size_t, pair[double, double],
+               size_t, pair[double, double]) except+ err
+        Axis2D(vector[BIN2D] bins) except+ err
+        size_t numBins() except+ err
+        vector[BIN2D] &bins()
+        double lowEdgeX() except+ err
+        double highEdgeX() except+ err
+        double lowEdgeY() except+ err
+        double highEdgeY() except+ err
+        long getBinIndex(double, double)
+        void reset()
+        DBN totalDbn() except +err
+        DBN outflow(int, int) except +err
+
+# Axis2D }}}
+
+# Streams {{{
+cdef extern from "<sstream>" namespace "std":
+    cdef cppclass ostringstream:
+        ostringstream()
+        string& str()
+
+cdef extern from "<sstream>" namespace "std":
+    cdef cppclass istringstream:
+        istringstream()
+        string& str(string &)
+
+cdef extern from "YODA/Reader.h" namespace "YODA":
+    cdef cppclass Reader:
+        void read(istringstream &, vector[AnalysisObject*] &) except +err
+
+cdef extern from "YODA/ReaderYODA.h" namespace "YODA":
+    Reader& ReaderYODA_create "YODA::ReaderYODA::create" ()
+
+cdef extern from "YODA/ReaderAIDA.h" namespace "YODA":
+    Reader& ReaderAIDA_create "YODA::ReaderAIDA::create" ()
+
+
+cdef extern from "YODA/Writer.h" namespace "YODA":
+    cdef cppclass Writer:
+        void write(ostringstream &, vector[AnalysisObject*] &)
+
+cdef extern from "YODA/WriterYODA.h" namespace "YODA":
+    Writer& WriterYODA_create "YODA::WriterYODA::create" ()
+
+cdef extern from "YODA/WriterAIDA.h" namespace "YODA":
+    Writer& WriterAIDA_create "YODA::WriterAIDA::create" ()
+# Streams }}}

Added: trunk/pyext/yoda/errors.cpp
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/errors.cpp	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,61 @@
+#include "Python.h"
+
+#include <exception>
+#include <stdexcept>
+#include <iostream>
+#include <typeinfo>
+
+#include "core.h"
+#include "YODA/Exceptions.h"
+#include "pyerrors.h"
+
+void translate_yoda_error() {
+  try {
+    if (PyErr_Occurred())
+      ; // let the latest Python exn pass through and ignore the current one
+    else
+      throw;
+  } catch (const std::out_of_range& exn) {
+    PyErr_SetString(PyExc_IndexError, exn.what());
+  } catch (const std::bad_cast& exn) {
+    PyErr_SetString(PyExc_TypeError, exn.what());
+  } catch (const std::domain_error& exn) {
+    PyErr_SetString(PyExc_ValueError, exn.what());
+  } catch (const std::invalid_argument& exn) {
+    PyErr_SetString(PyExc_ValueError, exn.what());
+  } catch (const std::ios_base::failure& exn) {
+    PyErr_SetString(PyExc_IOError, exn.what());
+  } catch (const std::overflow_error& exn) {
+    PyErr_SetString(PyExc_OverflowError, exn.what());
+  } catch (const std::range_error& exn) {
+    PyErr_SetString(PyExc_ArithmeticError, exn.what());
+  } catch (const std::underflow_error& exn) {
+    PyErr_SetString(PyExc_ArithmeticError, exn.what());
+  } catch (const std::bad_alloc& exn) {
+    PyErr_SetString(PyExc_MemoryError, exn.what());
+  } catch (const YODA::BinningError& exn) {
+    PyErr_SetString(YodaExc_BinningError, exn.what());
+  } catch (const YODA::RangeError& exn) {
+    PyErr_SetString(YodaExc_RangeError, exn.what());
+  } catch (const YODA::LockError& exn) {
+    PyErr_SetString(YodaExc_LockError, exn.what());
+  } catch (const YODA::GridError& exn) {
+    PyErr_SetString(YodaExc_GridError, exn.what());
+  } catch (const YODA::LogicError& exn) {
+    PyErr_SetString(YodaExc_LogicError, exn.what());
+  } catch (const YODA::WeightError& exn) {
+    PyErr_SetString(YodaExc_WeightError, exn.what());
+  } catch (const YODA::LowStatsError& exn) {
+    PyErr_SetString(YodaExc_LowStatsError, exn.what());
+  } catch (const YODA::AnnotationError& exn) {
+    PyErr_SetString(YodaExc_AnnotationError, exn.what());
+  } catch (const YODA::ReadError& exn) {
+    PyErr_SetString(YodaExc_ReadError, exn.what());
+  } catch (const YODA::UserError& exn) {
+    PyErr_SetString(YodaExc_UserError, exn.what());
+  } catch (const YODA::Exception& exn) {
+    PyErr_SetString(YodaExc_Exception, exn.what());
+  } catch (...) {
+    PyErr_SetString(PyExc_RuntimeError, "Unknown exception");
+  }
+} 

Added: trunk/pyext/yoda/errors.hh
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/errors.hh	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,6 @@
+// Not sure how this works. Isn't that nice, familiar territory? :) I think what
+// actually happens is that when a C++ exception happens, the exception is
+// somehow handed over to this function (probably by stack magic) and allows us
+// to do our thing.
+
+void translate_yoda_error();

Added: trunk/pyext/yoda/include/AnalysisObject.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/AnalysisObject.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,92 @@
+from cython.operator cimport dereference as deref, preincrement as preinc
+from cStringIO import StringIO
+
+cdef void set_ann(c.AnalysisObject *ana, char *k, char *v):
+    ana.setAnnotation(string(k), string(v))
+
+# AnalysisObject is the base class of most of the user facing objects
+cdef class AnalysisObject(util.Base):
+
+    # Pointer upcasting mechanism
+    cdef inline c.AnalysisObject *_AnalysisObject(self) except NULL:
+        return <c.AnalysisObject *> self.ptr()
+
+    # Deallocator (only needed as a base class)
+    def __dealloc__(self):
+        cdef c.AnalysisObject *p = self._AnalysisObject()
+        if self._deallocate:
+            del p
+
+    def annotations(self):
+        """
+        Key value pairs of metadata, returned as a dictionary.
+        
+        """
+        ana = self._AnalysisObject().annotations()
+        it = ana.begin()
+        out_dict = {}
+        while it != ana.end():
+            out_dict[deref(it).first.c_str()] = deref(it).second.c_str()
+            preinc(it)
+        return out_dict
+
+    def string(self):
+        """
+        A human readable representation of this object as it would be
+        stored in a YODA file.
+
+        """
+        f = StringIO() 
+        writeYODA([self], f)
+        f.seek(0)
+        return f.read().strip()
+
+    
+    def updateAnnotations(self, E=None, **F):
+        """
+        AO.update([E, ]**F) -> None.  Update annotations of AO from
+        dict/iterable E and F.
+
+        If E present and has a .keys() method:
+            for k in E: AO[k] = E[k]
+        If E present and lacks .keys() method:
+            for (k, v) in E: AO[k] = v
+        In either case, this is followed by:
+            for k in F: AO[k] = F[k]
+
+        """
+        AO = self._AnalysisObject()
+        if E is not None:
+            if hasattr(E, 'keys'):
+                for k in E:
+                    set_ann(AO, k, E[k])
+            else:
+                for k, v in E:
+                    set_ann(AO, k, v)
+
+        for k in F:
+            set_ann(AO, k, F[k])
+
+    property path:
+        """
+        Used for persistence and as a unique identifier. Must begin with
+        a '/' if not the empty string.
+
+        """
+
+        def __get__(self):
+            return self._AnalysisObject().path().c_str()
+
+        def __set__(self, char *path):
+            self._AnalysisObject().setPath(string(path))
+
+    property title:
+        def __get__(self):
+            return self._AnalysisObject().title().c_str()
+
+        def __set__(self, char *title):
+            self._AnalysisObject().setTitle(string(title))
+
+
+    def __repr__(self):
+        return "<%s '%s'>" % (self.__class__.__name__, self.path)

Added: trunk/pyext/yoda/include/Annotations.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/Annotations.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,40 @@
+#
+# Most definitely not a good idea, but useful as a warning
+#
+#from libc.stdlib cimport malloc
+#
+
+
+#ctypedef map[string,string].iterator mapiter
+#
+#cdef class Annotations_itemiterator():
+#    cdef mapiter *it
+#
+#    def __cinit__(self):
+#        self.it = <mapiter *> malloc(sizeof(mapiter))
+#
+#
+#cdef class Annotations(util.Base):
+#    cdef c.AnalysisObject *_AnalysisObject(self) except NULL:
+#        return <c.AnalysisObject *> self.ptr()
+#
+#
+#    def __getitem__(self, char *key):
+#        return self._AnalysisObject().annotation(string(key)).c_str()
+#
+#    def __setitem__(self, char *key, char *value):
+#        self._AnalysisObject().setAnnotation(string(key), string(value))
+#    
+#    # Doesn't actually iterate...
+#    def iteritems(self):
+#        it = self.it
+#        it[0] = self._AnalysisObject().annotations().begin()
+#        
+#        while True:
+#            yield deref(it[0]).first.c_str(), deref(it[0]).second.c_str()
+#            preinc(it[0])
+#            if it[0] == self._AnalysisObject().annotations().end():
+#                return
+#
+#    def __repr__(self):
+#        return '{%s}' % ', '.join('%r: %r' % i for i in self.iteritems())

Added: trunk/pyext/yoda/include/Axis1D_BIN1D_DBN.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/Axis1D_BIN1D_DBN.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,47 @@
+cdef class Axis1D_${BIN1D}_${DBN}(util.Base):
+
+    def __init__(self, size_t nbins, double lower, double upper):
+        util.set_owned_ptr(
+            self, new c.Axis1D[c.${BIN1D}, c.${DBN}](
+                nbins, lower, upper))
+
+    def __len__(self):
+        return self._Axis1D().bins().size()
+
+    def __getitem__(self, py_ix):
+        cdef size_t i = util.pythonic_index(py_ix, self._Axis1D().bins().size())
+        return util.new_borrowed_cls(
+            ${BIN1D}, & self._Axis1D().bins().at(i), self)
+
+    def __repr__(self):
+        return "<Axis1D>"
+
+    @property
+    def totalDbn(self):
+        return util.new_borrowed_cls(
+            ${DBN}, &self._Axis1D().totalDbn(), self)
+
+    @property
+    def underflow(self):
+        return util.new_borrowed_cls(
+            ${DBN}, &self._Axis1D().underflow(), self)
+
+    @property
+    def overflow(self):
+        return util.new_borrowed_cls(
+            ${DBN}, &self._Axis1D().overflow(), self)
+
+    def reset(self):
+        self._Axis1D().reset()
+
+    def bin_at(self, x):
+        return self[self._Axis1D().getBinIndex(x)]
+
+    # BOILERPLATE STUFF
+    cdef inline c.Axis1D[c.${BIN1D}, c.${DBN}] *_Axis1D(self) except NULL:
+        return <c.Axis1D[c.${BIN1D}, c.${DBN}]*> self.ptr()
+
+    def __dealloc__(self):
+        cdef c.Axis1D[c.${BIN1D}, c.${DBN}] *p = self._Axis1D()
+        if self._deallocate:
+            del p

Added: trunk/pyext/yoda/include/Axis2D_BIN2D_DBN.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/Axis2D_BIN2D_DBN.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,53 @@
+cdef class Axis2D_${BIN2D}_${DBN}(util.Base):
+
+    def __init__(self, nx, xl, xu, ny, yl, yu):
+        util.set_owned_ptr(
+            self, new c.Axis2D[c.${BIN2D}, c.${DBN}](
+                nx, pair[double, double](xl, xu),
+                ny, pair[double, double](yl, yu)))
+
+    def __len__(self):
+        return self._Axis2D().bins().size()
+
+    def __getitem__(self, py_ix):
+        cdef size_t i = util.pythonic_index(py_ix, self._Axis2D().bins().size())
+        return util.new_borrowed_cls(
+            ${BIN2D}, & self._Axis2D().bins().at(i), self)
+
+    def __repr__(self):
+        return "<Axis2D>"
+
+    @property
+    def totalDbn(self):
+        return util.new_owned_cls(
+            ${DBN}, new c.${DBN}(self._Axis2D().totalDbn()))
+
+    @property
+    def outflow(self, ix, iy):
+        return util.new_owned_cls(
+            ${DBN}, new c.${DBN}(self._Axis2D().outflow(ix, iy)))
+    
+    @property
+    def edges(self):
+        return XY(
+            Edge(self._Axis2D().lowEdgeX(), self._Axis2D().highEdgeX()),
+            Edge(self._Axis2D().lowEdgeY(), self._Axis2D().highEdgeY())
+        )
+
+    def reset(self):
+        self._Axis2D().reset()
+
+    def binByCoord(self, x, y):
+        cdef int ix = self._Axis2D().getBinIndex(x, y)
+        if ix < 0:
+            raise YodaExc_RangeError('No bin found!')
+        return self[ix]
+
+    # BOILERPLATE STUFF
+    cdef inline c.Axis2D[c.${BIN2D}, c.${DBN}] *_Axis2D(self) except NULL:
+        return <c.Axis2D[c.${BIN2D}, c.${DBN}]*> self.ptr()
+
+    def __dealloc__(self):
+        cdef c.Axis2D[c.${BIN2D}, c.${DBN}] *p = self._Axis2D()
+        if self._deallocate:
+            del p

Added: trunk/pyext/yoda/include/Bin.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/Bin.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,8 @@
+cdef class Bin(util.Base):
+    cdef inline c.Bin *_Bin(self) except NULL:
+        return <c.Bin *> self.ptr()
+
+    def __dealloc__(self):
+        cdef c.Bin *p = self._Bin()
+        if self._deallocate:
+            del p

Added: trunk/pyext/yoda/include/Bin1D_DBN.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/Bin1D_DBN.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,157 @@
+cdef class Bin1D_${DBN}(Bin):
+    """
+    1D Bin based on an underlying ${DBN}.
+
+    ProfileBin1D(xlow, xhigh)
+
+    """
+
+    def __init__(self, xlow, xhigh):
+        util.set_owned_ptr(
+            self, new c.Bin1D_${DBN}(pair[double, double](xlow, xhigh)))
+
+    def scale(self, x=1.0, w=1.0):
+        """
+        scale(x=1.0, w=1.0) -> None. Scale this bin's arguments by
+        their respective scalefactors.
+
+        """
+        if w != 1.0:
+            self._Bin1D().scaleW(w)
+        if x != 1.0:
+            self._Bin1D().scaleX(x)
+
+    @property
+    def edges(self):
+        """The lower and upper edges."""
+        return util.Edges(self._Bin1D().lowEdge(),
+                          self._Bin1D().highEdge())
+
+    @property
+    def width(self):
+        """The width of the bin."""
+        return self._Bin1D().width()
+
+    @property
+    def focus(self):
+        """
+        The focus of the bin. If the bin has been filled, then this
+        is the mean fill on this bin. If the bin has not been filled,
+        then the focus is the midpoint of the bin.
+        
+        """
+        return self._Bin1D().focus()
+
+    @property
+    def midpoint(self):
+        """The point half-way between the two bin edges."""
+        return self._Bin1D().midpoint()
+
+    @property
+    def mean(self):
+        """The mean of the x-values that have filled the bin."""
+        return self._Bin1D().xMean()
+
+    @property
+    def stdDev(self):
+        """
+        The standard deviation of the x-values that have filled the bin.
+
+        """
+        return self._Bin1D().xStdDev()
+
+    @property
+    def stdErr(self):
+        """
+        The standard error of the x-values that have filled the bin.
+        
+        """
+        return self._Bin1D().xStdErr()
+
+    @property
+    def rms(self):
+        """
+        The root-mean-square of the x-values that have filled the bin.
+
+        """
+        return self._Bin1D().xRMS()
+
+    ##
+    ## Raw statistics
+    ##
+
+    @property
+    def numEntries(self):
+        """
+        The number of entries that have filled the bin.
+
+        """
+        return self._Bin1D().numEntries()
+
+    @property
+    def effNumEntries(self):
+        """
+        The effective number of entries in the bin. 
+
+        s.effNumEntries <==> (s.sumW ** 2) / s.sumW2
+
+        """
+        return self._Bin1D().effNumEntries()
+
+    @property
+    def sumW(self):
+        """
+        The sum of weights: sum(weights).
+
+        """
+        return self._Bin1D().sumW()
+
+    @property
+    def sumW2(self):
+        """
+        The sum of weights-squared: sum(weights * weights)
+
+        """
+        return self._Bin1D().sumW2()
+
+    @property
+    def sumWX(self):
+        """
+        The sum of weights-times-x: sum(weights * x)
+
+        """
+        return self._Bin1D().sumWX()
+
+    @property
+    def sumWX2(self):
+        """
+        The sum of weights-times-x-squared: sum(weights * x * x)
+
+        """
+        return self._Bin1D().sumWX2()
+
+    def merge(Bin1D_${DBN} self, Bin1D_${DBN} other):
+        """
+        merge(other) -> Bin1D_${DBN}. Merge this bin with another of the
+        same type. Only directly adjacent bins, i.e. those sharing a
+        common edge, can be merged.
+        
+        """
+        self._Bin1D().merge(deref(other._Bin1D()))
+        return self
+
+    def __repr__(self):
+        return '<%s[%g, %g)>' % ((self.__class__.__name__,) + self.edges)
+
+    def __add__(Bin1D_${DBN} self, Bin1D_${DBN} other):
+        return util.new_owned_cls(
+            Bin1D_${DBN},
+            new c.Bin1D_${DBN}(deref(self._Bin1D()) + deref(other._Bin1D())))
+
+    def __sub__(Bin1D_${DBN} self, Bin1D_${DBN} other):
+        return util.new_owned_cls(
+            Bin1D_${DBN},
+            new c.Bin1D_${DBN}(deref(self._Bin1D()) - deref(other._Bin1D())))
+    
+    cdef inline c.Bin1D_${DBN} *_Bin1D(self) except NULL:
+        return <c.Bin1D_${DBN} *> self.ptr()

Added: trunk/pyext/yoda/include/Bin2D_DBN.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/Bin2D_DBN.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,112 @@
+
+cdef class Bin2D_${DBN}(Bin):
+    """1D Bin class templated on a ${DBN}"""
+
+    def __init__(self, xlow, xhigh, ylow, yhigh):
+        util.set_owned_ptr(
+            self, new c.Bin2D_${DBN}(
+                pair[double, double](xlow, xhigh),
+                pair[double, double](ylow, yhigh),
+            ))
+
+    def scale(self, x=1.0, y=1.0, w=1.0):
+        if x != 1.0 or y != 1.0:
+            self.scaleXY(x, y)
+
+        if w != 1.0:
+            self.scaleW(w)
+
+    @property
+    def edges(self):
+        """The lower and upper edges."""
+        cdef pair[double, double] x = self._Bin2D().xedges()
+        cdef pair[double, double] y = self._Bin2D().yedges()
+        return XY(util.Edges(x.first, x.second),
+                  util.Edges(y.first, y.second))
+
+    @property
+    def widths(self):
+        """The widths of this bin in the x- and y-dimensions."""
+        return XY(self._Bin2D().widthX(), self._Bin2D().widthY())
+
+    @property
+    def focus(self):
+        """The focus of the bin in the x- and y-dimensions"""
+        cdef pair[double, double] f = self._Bin2D().focus()
+        return XY(f.first, f.second)
+
+    @property
+    def midpoint(self):
+        cdef pair[double, double] f = self._Bin2D().midpoint()
+        return XY(f.first, f.second)
+
+    @property
+    def mean(self):
+        return XY(self._Bin2D().xMean(), self._Bin2D().yMean())
+
+    @property
+    def std_dev(self):
+        return XY(self._Bin2D().xStdDev(), self._Bin2D().yStdDev())
+
+    @property
+    def std_err(self):
+        return XY(self._Bin2D().xStdErr(), self._Bin2D().yStdErr())
+
+    @property
+    def rms(self):
+        return XY(self._Bin2D().xRMS(), self._Bin2D().yRMS())
+
+    # Raw statistics #
+    ##################
+    @property
+    def count(self):
+        return self._Bin2D().numEntries()
+
+    @property
+    def effective_count(self):
+        return self._Bin2D().effNumEntries()
+
+    @property
+    def sum_w(self):
+        return self._Bin2D().sumW()
+
+    @property
+    def sum_w2(self):
+        return self._Bin2D().sumW2()
+
+    @property
+    def sum_wx(self):
+        return self._Bin2D().sumWX()
+
+    @property
+    def sum_wy(self):
+        return self._Bin2D().sumWY()
+
+    @property
+    def sum_wxy(self):
+        return self._Bin2D().sumWXY()
+
+    @property
+    def sum_wx2(self):
+        return self._Bin2D().sumWX2()
+
+    @property
+    def sum_wy2(self):
+        return self._Bin2D().sumWY2()
+
+    #def merge(Bin2D_${DBN} self, Bin2D_${DBN} other):
+    #    self._Bin2D().merge(deref(other._Bin2D()))
+    #    return self
+
+    def __add__(Bin2D_${DBN} self, Bin2D_${DBN} other):
+        return util.new_owned_cls(
+            Bin2D_${DBN},
+            new c.Bin2D_${DBN}(deref(self._Bin2D()) + deref(other._Bin2D())))
+
+    def __sub__(Bin2D_${DBN} self, Bin2D_${DBN} other):
+        return util.new_owned_cls(
+            Bin2D_${DBN},
+            new c.Bin2D_${DBN}(deref(self._Bin2D()) - deref(other._Bin2D())))
+    
+    cdef inline c.Bin2D_${DBN} *_Bin2D(self) except NULL:
+        return <c.Bin2D_${DBN} *> self.ptr()

Added: trunk/pyext/yoda/include/Dbn1D.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/Dbn1D.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,116 @@
+cdef class Dbn1D(util.Base):
+    """
+    A 1D distribution 'counter', used and exposed by 1D histograms and their bins.
+
+    """
+
+    def __init__(self):
+        util.set_owned_ptr(self, new c.Dbn1D())
+
+    def copy(self):
+        return util.set_owned_ptr(self, new c.Dbn1D(deref(self._Dbn1D())))
+
+    def fill(self, x, weight=1.0):
+        """
+        (float x, float weight=1.0) -> None
+        
+        Fills the distribution with the given weight at given x.
+        
+        """
+        self._Dbn1D().fill(x, weight)
+
+    def reset(self):
+        """
+        () -> None
+
+        Reset the distribution counters to the unfilled state."""
+        self._Dbn1D().reset()
+
+
+    def scale(self, x=1.0, w=1.0):
+        """
+        (x=1.0, w=1.0) -> None
+
+        Scale the Dbn's variables by the given factors
+
+        """
+        if x != 1.0:
+            self._Dbn1D().scaleX(x)
+        if w != 1.0:
+            self._Dbn1D().scaleW(w)
+
+    @property
+    def mean(self):
+        """Weighted mean of x"""
+        return (self._Dbn1D().xMean())
+
+    @property
+    def variance(self):
+        """Weighted variance of x"""
+        return (self._Dbn1D().xVariance())
+
+    @property
+    def std_dev(self):
+        """Weighted standard deviation of x"""
+        return (self._Dbn1D().xStdDev())
+
+    @property
+    def std_err(self):
+        """Weighted standard error on <x>"""
+        return (self._Dbn1D().xStdErr())
+
+    @property
+    def rms(self):
+        """Weighted root mean squared (RMS) of x"""
+        return (self._Dbn1D().xRMS())
+
+    @property
+    def count(self):
+        """The number of entries"""
+        return self._Dbn1D().numEntries()
+
+    @property
+    def effective_count(self):
+        """Effective number of entries (for weighted events)"""
+        return self._Dbn1D().numEntries()
+
+    @property
+    def sum_w(self):
+        """sum(weights)"""
+        return self._Dbn1D().sumW()
+
+    @property
+    def sum_w2(self):
+        """sum(weights**2)"""
+        return self._Dbn1D().sumW2()
+
+    @property
+    def sum_wx(self):
+        """sum(weights * xs)"""
+        return self._Dbn1D().sumWX()
+
+    @property
+    def sum_wx2(self):
+        """sum(weights * xs**2)"""
+        return self._Dbn1D().sumWX2()
+
+    def __add__(Dbn1D self, Dbn1D other):
+        return util.new_owned_cls(Dbn1D, new c.Dbn1D(
+            deref(self._Dbn1D()) + deref(other._Dbn1D())))
+
+    def __sub__(Dbn1D self, Dbn1D other):
+        return util.new_owned_cls(Dbn1D, new c.Dbn1D(
+            deref(self._Dbn1D()) - deref(other._Dbn1D())))
+
+    def __repr__(self):
+        return 'Dbn1D(mean=%g, stdDev=%g)' % (self.mean, self.stdDev)
+
+    # Magic stuff
+    cdef c.Dbn1D *_Dbn1D(self) except NULL:
+        return <c.Dbn1D *> self.ptr()
+
+    def __dealloc__(self):
+        cdef c.Dbn1D *p = self._Dbn1D()
+        if self._deallocate:
+            del p
+

Added: trunk/pyext/yoda/include/Dbn2D.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/Dbn2D.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,155 @@
+
+cdef class Dbn2D(util.Base):
+    """
+    A 2D distribution 'counter', used and exposed by 2D histograms and
+    1D profiles and their bins.
+
+    """
+
+    def __init__(self):
+        util.set_owned_ptr(self, new c.Dbn2D())
+
+    def copy(self):
+        return util.new_owned_cls(Dbn2D, new c.Dbn2D(deref(self._Dbn2D())))
+
+    def fill(self, x, y, weight=1.0):
+        """
+        (x, y, weight=1.0) -> None
+
+        Fills the distribution with the given weight at given (x, y).
+        
+        """
+        self._Dbn2D().fill(x, y, weight)
+
+    def fill_many(self, xs, ys, ws=repeat(1)):
+        """
+        (xs, ys, ws=repeat(1.0)) -> None
+
+        Fills the distribution from iterables xs, ys and ws.
+
+        """
+        cdef c.Dbn2D *ptr = self._Dbn2D()
+        itx = iter(xs)
+        ity = iter(ys)
+        itw = iter(ws)
+        while True:
+            x = next(xs, None)
+            y = next(ys, None)
+            w = next(ws, None)
+            if x is None or y is None or w is None:
+                break
+            ptr.fill(x, y, w)
+
+    def reset(self):
+        """
+        () -> None
+
+        Reset the distribution counters to the unfilled state."""
+        self._Dbn2D().reset()
+
+    def scale(self, x=1.0, y=1.0, w=1.0):
+        """
+        (x=1.0, y=1.0, w=1.0) -> None
+
+        Scale Dbn2D's parameters
+
+        """
+        if x != 1.0 and y != 1.0:
+            self._Dbn2D().scaleXY(x, y)
+        elif x != 1.0:
+            self._Dbn2D().scaleX(x)
+        elif y != 1.0:
+            self._Dbn2D().scaleY(y)
+        if w != 1.0:
+            self._Dbn2D().scaleW(w)
+
+    @property
+    def mean(self):
+        """Weighted mean of x"""
+        return util.XY(self._Dbn2D().xMean(), self._Dbn2D().yMean())
+
+    @property
+    def variance(self):
+        """Weighted variance of x"""
+        return util.XY(self._Dbn2D().xVariance(), self._Dbn2D().yVariance())
+
+    @property
+    def std_dev(self):
+        """Weighted standard deviation of x"""
+        return util.XY(self._Dbn2D().xStdDev(), self._Dbn2D().yStdDev())
+
+    @property
+    def std_err(self):
+        """Weighted standard error on <x>"""
+        return util.XY(self._Dbn2D().xStdErr(), self._Dbn2D().yStdErr())
+
+    @property
+    def rms(self):
+        """Weighted root mean squared (RMS) of x"""
+        return util.XY(self._Dbn2D().xRMS(), self._Dbn2D().yRMS())
+
+    @property
+    def count(self):
+        """The number of entries"""
+        return self._Dbn2D().numEntries()
+
+    @property
+    def effective_count(self):
+        """Effective number of entries (for weighted events)"""
+        return self._Dbn2D().effNumEntries()
+
+    @property
+    def sum_w(self):
+        """sum(weights)"""
+        return self._Dbn2D().sumW()
+
+    @property
+    def sum_w2(self):
+        """sum(weights**2)"""
+        return self._Dbn2D().sumW2()
+
+    @property
+    def sum_wx(self):
+        """sum(weights * xs)"""
+        return self._Dbn2D().sumWX()
+
+    @property
+    def sum_wy(self):
+        """sum(weights * ys)"""
+        return self._Dbn2D().sumWY()
+
+    @property
+    def sum_wx2(self):
+        """sum(weights * xs**2)"""
+        return self._Dbn2D().sumWX2()
+
+    @property
+    def sum_wy2(self):
+        """sum(weights * ys**2)"""
+        return self._Dbn2D().sumWY2()
+
+    @property
+    def sum_wxy(self):
+        """sum(weights xs * ys)"""
+        return self._Dbn2D().sumWXY()
+
+    def __add__(Dbn2D self, Dbn2D other):
+        return util.new_owned_cls(Dbn2D, new c.Dbn2D(
+            deref(self._Dbn2D()) + deref(other._Dbn2D())))
+
+    def __sub__(Dbn2D self, Dbn2D other):
+        return util.new_owned_cls(Dbn2D, new c.Dbn2D(
+            deref(self._Dbn2D()) - deref(other._Dbn2D())))
+
+    def __repr__(self):
+        return 'Dbn2D(mean=(%g, %g), stdDev=(%g, %g))' % (self.mean + self.std_dev)
+
+    # Magic stuff
+    cdef c.Dbn2D *_Dbn2D(self) except NULL:
+        return <c.Dbn2D *> self.ptr()
+
+    def __dealloc__(self):
+        cdef c.Dbn2D *p = self._Dbn2D()
+        if self._deallocate:
+            del p
+

Added: trunk/pyext/yoda/include/Dbn3D.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/Dbn3D.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,198 @@
+from collections import namedtuple
+from itertools import izip, repeat
+
+cdef class Dbn3D(util.Base):
+    """
+    A 2D distribution 'counter', used and exposed by 2D histograms and
+    1D profiles and their bins.
+
+    """
+    def __init__(self):
+        util.set_owned_ptr(self, new c.Dbn3D())
+
+    def copy(self):
+        return util.new_owned_cls(Dbn3D, new c.Dbn3D(deref(self._Dbn3D())))
+
+    def fill(self, x, y, z, weight=1.0):
+        """
+        (x, y, z, weight=1.0) -> None
+        
+        Fills the distribution with the given weight at given (x, y).
+        
+        """
+        self._Dbn3D().fill(x, y, z, weight)
+
+    def fill_many(self, xs, ys, zs, ws=repeat(1)):
+        """
+        (xs, ys, ws=repeat(1.0)) -> None
+
+        Fills the distribution from iterables xs, ys and ws.
+
+        """
+        cdef c.Dbn3D *ptr = self._Dbn3D()
+        itx = iter(xs)
+        ity = iter(ys)
+        itz = iter(zs)
+        itw = iter(ws)
+        while True:
+            x = next(xs, None)
+            y = next(ys, None)
+            z = next(zs, None)
+            w = next(ws, None)
+            if x is None or y is None or w is None:
+                break
+            ptr.fill(x, y, z, w)
+
+
+    def reset(self):
+        """
+        () -> None
+
+        Reset the distribution counters to the unfilled state."""
+        self._Dbn3D().reset()
+
+    def scale(self, x=1.0, y=1.0, z=1.0, w=1.0):
+        """
+        (x=1.0, y=1.0, w=1.0) -> None
+
+        Scale Dbn3D's parameters
+
+        """
+        if x != 1.0 and y != 1.0 and z != 1.0:
+            self._Dbn3D().scaleXYZ(x, y, z)
+        else:
+            if x != 1.0:
+                self._Dbn3D().scaleX(x)
+            if y != 1.0:
+                self._Dbn3D().scaleY(x)
+            if z != 1.0:
+                self._Dbn3D().scaleZ(x)
+
+        if w != 1.0:
+            self._Dbn3D().scaleW(w)
+
+    @property
+    def mean(self):
+        """Weighted mean of x"""
+        return util.XYZ(self._Dbn3D().xMean(),
+                        self._Dbn3D().yMean(),
+                        self._Dbn3D().zMean())
+
+    @property
+    def variance(self):
+        """Weighted variance of x"""
+        return util.XYZ(self._Dbn3D().xVariance(),
+                        self._Dbn3D().yVariance(),
+                        self._Dbn3D().zVariance())
+
+    @property
+    def std_dev(self):
+        """Weighted standard deviation of x"""
+        return util.XYZ(self._Dbn3D().xStdDev(),
+                        self._Dbn3D().yStdDev(),
+                        self._Dbn3D().zStdDev())
+
+    @property
+    def std_err(self):
+        """Weighted standard error on <x>"""
+        return util.XYZ(self._Dbn3D().xStdErr(),
+                        self._Dbn3D().yStdErr(),
+                        self._Dbn3D().zStdErr())
+
+    @property
+    def rms(self):
+        """Weighted root mean squared (RMS) of x"""
+        return util.XYZ(self._Dbn3D().xRMS(),
+                       self._Dbn3D().yRMS(),
+                       self._Dbn3D().zRMS())
+
+    @property
+    def count(self):
+        """The number of entries"""
+        return self._Dbn3D().numEntries()
+
+    @property
+    def effective_count(self):
+        """Effective number of entries (for weighted events)"""
+        return self._Dbn3D().effNumEntries()
+
+    @property
+    def sum_w(self):
+        """sum(weights)"""
+        return self._Dbn3D().sumW()
+
+    @property
+    def sum_w2(self):
+        """sum(weights**2)"""
+        return self._Dbn3D().sumW2()
+
+    @property
+    def sum_wx(self):
+        """sum(weights * xs)"""
+        return self._Dbn3D().sumWX()
+
+    @property
+    def sum_wy(self):
+        """sum(weights * ys)"""
+        return self._Dbn3D().sumWY()
+
+    @property
+    def sum_wz(self):
+        """sum(weights * ys)"""
+        return self._Dbn3D().sumWZ()
+
+    @property
+    def sum_wx2(self):
+        """sum(weights * xs**2)"""
+        return self._Dbn3D().sumWX2()
+
+    @property
+    def sum_wy2(self):
+        """sum(weights * ys**2)"""
+        return self._Dbn3D().sumWY2()
+
+    @property
+    def sum_wz2(self):
+        """sum(weights * ys)"""
+        return self._Dbn3D().sumWZ2()
+
+    @property
+    def sum_wxy(self):
+        """sum(weights xs * ys)"""
+        return self._Dbn3D().sumWXY()
+
+    @property
+    def sum_wxz(self):
+        """sum(weights xs * ys)"""
+        return self._Dbn3D().sumWXZ()
+
+    @property
+    def sum_wyz(self):
+        """sum(weights xs * ys)"""
+        return self._Dbn3D().sumWYZ()
+
+    @property
+    def sum_wxyz(self):
+        """sum(weights xs * ys)"""
+        return self._Dbn3D().sumWXYZ()
+
+    def __add__(Dbn3D self, Dbn3D other):
+        return util.new_owned_cls(Dbn3D, new c.Dbn3D(
+            deref(self._Dbn3D()) + deref(other._Dbn3D())))
+
+    def __sub__(Dbn3D self, Dbn3D other):
+        return util.new_owned_cls(Dbn3D, new c.Dbn3D(
+            deref(self._Dbn3D()) - deref(other._Dbn3D())))
+
+    def __repr__(self):
+        return 'Dbn3D(mean=(%g, %g), stdDev=(%g, %g))' % (self.mean + self.std_dev)
+
+    # Magic stuff
+    cdef c.Dbn3D *_Dbn3D(self) except NULL:
+        return <c.Dbn3D *> self.ptr()
+
+    def __dealloc__(self):
+        cdef c.Dbn3D *p = self._Dbn3D()
+        if self._deallocate:
+            del p
+

Added: trunk/pyext/yoda/include/Errors.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/Errors.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,37 @@
+from cpython.exc cimport PyErr_NewException
+
+# Create public exceptions -- these will be available for inclusion from an
+# external C++ module using 'core.h' or similar, and also be available from
+# within Cython.
+
+# These are called from translate_yoda_error in errors.cpp
+
+cpdef public:
+    # Master exception
+    object YodaExc_Exception = PyErr_NewException(
+        "yoda.Exception", <object> NULL, <object> NULL)
+
+    # Inherited exceptions
+    object YodaExc_BinningError = PyErr_NewException(
+        "yoda.BinningError", YodaExc_Exception, <object> NULL)
+    object YodaExc_RangeError = PyErr_NewException(
+        "yoda.RangeError", YodaExc_Exception, <object> NULL)
+    object YodaExc_LockError = PyErr_NewException(
+        "yoda.LockError", YodaExc_Exception, <object> NULL)
+    object YodaExc_GridError = PyErr_NewException(
+        "yoda.GridError", YodaExc_Exception, <object> NULL)
+    object YodaExc_LogicError = PyErr_NewException(
+        "yoda.LogicError", YodaExc_Exception, <object> NULL)
+    object YodaExc_LowStatsError = PyErr_NewException(
+        "yoda.WeightError", YodaExc_Exception, <object> NULL)
+    object YodaExc_WeightError = PyErr_NewException(
+        "yoda.LowStatsError", YodaExc_Exception, <object> NULL)
+    object YodaExc_AnnotationError = PyErr_NewException(
+        "yoda.AnnotationError", YodaExc_Exception, <object> NULL)
+    object YodaExc_ReadError = PyErr_NewException(
+        "yoda.ReadError", YodaExc_Exception, <object> NULL)
+    object YodaExc_UserError = PyErr_NewException(
+        "yoda.UserError", YodaExc_Exception, <object> NULL)
+
+# Note that these don't appear in python space due to the cdef. What we will
+# have to do is use some magic to make these appear in Python space.

Added: trunk/pyext/yoda/include/Histo1D.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/Histo1D.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,164 @@
+
+
+
+cdef class Histo1D(AnalysisObject):
+    """
+    1D histogram. Complete histogramming is supported, including
+    uniform/regular binning, variable wideth bininng, unbinned gaps in
+    the covered range, and under/overflows. Rebinning by integer
+    factors, or by explicit merging of contiguous bins is also
+    supported.
+
+    Rescaling of weights and/or the x axis is permitted in-place: the
+    result is still a valid Histo1D. Binning-compatible 1D histograms
+    may be divided, resulting in a Scatter2D since further fulls would
+    not be meaningful.
+
+    Several sets of arguments are tried by the constructor in the
+    following order.
+
+    Histo1D(path="", title=""). Construct a histogram with optional path
+    and title but no bins.
+
+    Histo1D(B, path="", title=""). Construct a histogram from an
+    iterator of bins, B.
+
+    Histo1D(E, path="", title=""). Construct a histogram from an
+    iterator of edges, E.
+
+    Should this constructor fail, then 
+    Histo1D(nbins, low, high, path="", title="")
+    """
+
+    # Is there a reason why this sounds like an advert? I'M ALREADY SOLD
+    # DAMMIT!
+
+
+    cdef inline c.Histo1D *_Histo1D(self) except NULL:
+        return <c.Histo1D*> self.ptr()
+
+    # There is a pythonic constructor here, and it looks a little like...
+    # __init__(self, *args, **kwargs)
+    # ([edge], path="", title="")
+    # ([bins], **kwargs)
+
+    def __init__(self, *args, **kwargs):
+        # Multimethod constructors are blatantly unpythonic. However,
+        # doing something like the inbuilt collections do would be
+        # really neat. So we will use an iterable of bins in place of
+        # the "from other type" constructors, and have a copy() method
+        # which has exactly the same semantics.
+        util.try_loop([self.__init2, self.__init5, self.__init3], *args, **kwargs)
+
+    def __init3(self, bins, char *path="", char *title=""):
+        # Make an iterator over bins. We might as well make our code
+        # general, as that increases pythonicity.
+        self.__init2(path, title)
+        self.addBins(bins)
+
+    def __init2(self, char *path="", char *title=""):
+        util.set_owned_ptr(
+            self, new c.Histo1D(string(path), string(title)))
+
+    def __init5(self, nbins, low, high, char *path="", char *title=""):
+        util.set_owned_ptr(
+            self, new c.Histo1D(
+                nbins, low, high, string(path), string(title)))
+
+    def __len__(self):
+        return self._Histo1D().numBins()
+
+    def __getitem__(self, py_ix):
+        cdef size_t i = util.pythonic_index(
+            py_ix, self._Histo1D().numBins())
+
+        return util.new_borrowed_cls(
+            HistoBin1D, & self._Histo1D().bin(i), self)
+
+    def fill(self, x, weight=1.0):
+        """
+        (x, weight=1.0) -> None. Fill with given x and optional weight.
+
+        """
+        self._Histo1D().fill(x, weight)
+
+    def copy(self, char *path=""):
+        """(path="") -> Histo1D. Clone this Histo1D with optional new path."""
+        return util.new_owned_cls(Histo1D,
+            new c.Histo1D(deref(self._Histo1D()), string(path)))
+
+    def bins(self):
+        return list(self)
+
+    @property
+    def edges(self):
+        return util.Edges(self._Histo1D().lowEdge(),
+                          self._Histo1D().highEdge())
+
+    @property
+    def totalDbn(self):
+        """The Dbn1D representing the total distribution."""
+        # Now does this actually involve a copy in Cython? We should probably
+        # find out!
+        return util.new_borrowed_cls(
+            Dbn1D, &self._Histo1D().totalDbn(), self)
+
+    def reset(self):
+        """
+        Reset the histogram but leave the bin structure.
+        
+        """
+        self._Histo1D().reset()
+
+    def scale(self, w=1.0):
+        """
+        (double w=1.0) -> None. Scale Histogram and its statistics as if all
+        weights had been scaled by given factor.
+        
+        """
+        self._Histo1D().scaleW(w)
+
+    def normalize(self, normto=1.0, includeOverflows=True):
+        """
+        (normto=1.0, includeOverflows=False) -> None. Normalize the histogram.
+
+        """
+        self._Histo1D().normalize(normto, includeOverflows)
+
+    def mergeBins(self, a, b):
+        """mergeBins(a, b) -> None. Merge bins from indices a through b."""
+        self._Histo1D().mergeBins(a, b)
+
+    def rebin(self, n):
+        """(n) -> None. Merge every nth bin."""
+        self._Histo1D().rebin(n)
+
+    def addBin(self, low, high):
+        """(low, high) -> None. Add a bin."""
+        self._Histo1D().addBin(low, high)
+
+    def addBins(self, edges_or_bins):
+        arg = list(edges_or_bins)
+        util.try_loop([self.__addBins_edges,
+                       self.__addBins_tuples,
+                       self.__addBins_points,
+                       self.__addBins_bins], arg)
+
+    def __addBins_edges(self, edges):
+        cdef vector[double] cedges
+        for edge in edges:
+            cedges.push_back(edge)
+        
+        if len(edges):
+            self._Histo1D().addBins(cedges)
+    
+    def __addBins_bins(self, bins):
+        self.__addBins_tuples(imap(attrgetter('edges'), bins))
+
+    def __addBins_points(self, points):
+        self.__addBins_tuples(imap(attrgetter('xRange'), points))
+
+    def __addBins_tuples(self, tuples):
+        cdef double a, b
+        for a, b in tuples:
+            self._Histo1D().addBin(a, b)

Added: trunk/pyext/yoda/include/Histo2D.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/Histo2D.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,111 @@
+cdef class Histo2D(AnalysisObject):
+
+    cdef inline c.Histo2D *_Histo2D(self) except NULL:
+        return <c.Histo2D*> self.ptr()
+
+    def __init__(self, *args, **kwargs):
+        if len(args) <= 2:
+            self.__init2(*args, **kwargs)
+        else:
+            self.__init7(*args, **kwargs)
+
+    def __init2(Histo2D self, char *path="", char *title=""):
+        util.set_owned_ptr(
+            self, new c.Histo2D(string(path), string(title))) 
+
+    def __init7(Histo2D self,   nxbins, xlow, xupp,   nybins, ylow, yupp,
+                char *path="", char *title=""):
+
+        util.set_owned_ptr(
+            self, new c.Histo2D(
+                nxbins, xlow, xupp,
+                nybins, ylow, yupp,
+                string(path), string(title)))
+
+    def __len__(self):
+        return self._Histo2D().bins().size()
+
+    def __getitem__(self, py_ix):
+        cdef size_t i = util.pythonic_index(py_ix, self._Histo2D().bins().size())
+        return util.new_borrowed_cls(
+            HistoBin2D, & self._Histo2D().bins().at(i), self)
+
+    def __repr__(self):
+        return "Histo2D"
+
+    def fill(self, x, y, weight=1.0):
+        self._Histo2D().fill(x, y, weight)
+
+    def copy(self, char *path=""):
+        return util.new_owned_cls(Histo2D,
+            new c.Histo2D(deref(self._Histo2D()), string(path)))
+
+    @property
+    def total_dbn(self):
+        return util.new_owned_cls(
+            Dbn2D,
+            new c.Dbn2D(self._Histo2D().totalDbn()))
+
+    def outflow(self, ix=0, iy=0):
+        # For now we do manual error detection, due to exceptions and references
+        return util.new_owned_cls(
+            Dbn2D,
+            new c.Dbn2D(self._Histo2D().outflow(ix, iy)))
+
+    def sum_w(self, overflows=True):
+        return self._Histo2D().sumW(overflows)
+
+    def mean(self, overflows=True):
+        return XY(
+            self._Histo2D().xMean(overflows),
+            self._Histo2D().yMean(overflows))
+
+    def variance(self, overflows=True):
+        return XY(
+            self._Histo2D().xVariance(overflows),
+            self._Histo2D().yVariance(overflows))
+
+    def std_dev(self, overflows=True):
+        return XY(
+            self._Histo2D().xStdDev(overflows),
+            self._Histo2D().yStdDev(overflows))
+
+    def std_err(self, overflows=True):
+        return XY(
+            self._Histo2D().xStdErr(overflows),
+            self._Histo2D().yStdErr(overflows))
+
+    def reset(self):
+        self._Histo2D().reset()
+
+    def scale(self, w=1.0):
+        """
+        (w=1.0) -> None
+
+        Scale the given parameters
+
+        """
+        if w != 1.0:
+            self._Histo2D().scaleW(w)
+
+    def normalize(self, double normto, bint includeoverflows=True):
+        self._Histo2D().normalize(normto, includeoverflows)
+
+    #def mergeBins(self, size_t a, size_t b):
+    #    self._Histo2D().mergeBins(a, b)
+
+    #def rebin(self, int n):
+    #    self._Histo2D().rebin(n)
+
+    #def addBin(self, double low, double high):
+    #    """Add a bin to the Histo2D"""
+    #    self._Histo2D().addBin(low, high)
+    #    return self
+
+    #def addBins(self, edges):
+    #    cdef vector[double] cedges
+    #    for i in edges:
+    #        cedges.push_back(i)
+    #    self._Histo2D().addBins(cedges)
+    #    return self
+

Added: trunk/pyext/yoda/include/HistoBin1D.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/HistoBin1D.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,42 @@
+cdef class HistoBin1D(Bin1D_Dbn1D):
+
+    cdef inline c.HistoBin1D *_HistoBin1D(self) except NULL:
+        return <c.HistoBin1D *> self.ptr()
+
+    def __init__(self, double a, double b):
+        util.set_owned_ptr(
+            self, new c.HistoBin1D(a, b))
+        
+    def fill(self, double value, double weight=1.0):
+        self._HistoBin1D().fill(value, weight)
+
+    @property
+    def area(self):
+        return self._HistoBin1D().area()
+
+    @property
+    def height(self):
+        return self._HistoBin1D().height()
+
+    @property
+    def areaErr(self):
+        return self._HistoBin1D().areaErr()
+
+    @property
+    def heightErr(self):
+        return self._HistoBin1D().heightErr()
+
+    def __add__(HistoBin1D a, HistoBin1D b):
+        return util.new_owned_cls(
+            HistoBin1D,
+            new c.HistoBin1D(
+                deref(a._HistoBin1D()) + 
+                deref(b._HistoBin1D())))
+
+    def __sub__(HistoBin1D a, HistoBin1D b):
+        return util.new_owned_cls(
+            HistoBin1D,
+            new c.HistoBin1D(
+                deref(a._HistoBin1D()) -
+                deref(b._HistoBin1D())))
+

Added: trunk/pyext/yoda/include/HistoBin2D.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/HistoBin2D.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,44 @@
+cdef class HistoBin2D(Bin2D_Dbn2D):
+
+    cdef inline c.HistoBin2D *_HistoBin2D(self) except NULL:
+        return <c.HistoBin2D *> self.ptr()
+
+    def __init__(self, xlow, xhigh, ylow, yhigh):
+        util.set_owned_ptr(
+            self, new c.HistoBin2D(xlow, xhigh, ylow, yhigh))
+        
+    def fill(self, x, y, weight=1.0):
+        self._HistoBin2D().fill(x, y, weight)
+
+    @property
+    def volume(self):
+        return self._HistoBin2D().volume()
+
+    @property
+    def height(self):
+        return self._HistoBin2D().height()
+
+    @property
+    def volume_err(self):
+        return self._HistoBin2D().volumeErr()
+
+    @property
+    def height_err(self):
+        return self._HistoBin2D().heightErr()
+
+    def __add__(HistoBin2D a, HistoBin2D b):
+        return util.new_owned_cls(
+            HistoBin2D,
+            new c.HistoBin2D(
+                deref(a._HistoBin2D()) + 
+                deref(b._HistoBin2D())))
+
+    def __sub__(HistoBin2D a, HistoBin2D b):
+        return util.new_owned_cls(
+            HistoBin2D,
+            new c.HistoBin2D(
+                deref(a._HistoBin2D()) -
+                deref(b._HistoBin2D())))
+
+    def __repr__(self):
+        return 'HistoBin2D(%g, %g, %g, %g)' % (self.edges.x + self.edges.y)

Added: trunk/pyext/yoda/include/IO.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/IO.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,95 @@
+# Readers and writers
+
+cdef list aobjects_to_list(vector[c.AnalysisObject*] *aobjects):
+    cdef list out = []
+    cdef c.AnalysisObject *ao
+    cdef size_t i
+    for i in range(aobjects.size()):
+        ao = deref(aobjects)[i]
+        out.append(
+            util.new_owned_cls(
+                globals()[ao.type().c_str()], ao))
+    return out
+
+# Set a istringstream's string from a C/Python string
+cdef void make_iss(c.istringstream &iss, char *s):
+    iss.str(string(s))
+
+##
+## Readers
+##
+
+def readYODA(file_or_filename):
+    cdef c.istringstream iss
+    cdef vector[c.AnalysisObject*] aobjects
+
+    if hasattr(file_or_filename, 'read'):
+        s = file_or_filename.read()
+    else:
+        with open(file_or_filename) as f:
+            s = f.read()
+
+    make_iss(iss, s)
+     
+    c.ReaderYODA_create().read(iss, aobjects)
+
+    # Not as expensive as it looks!
+    return aobjects_to_list(&aobjects)
+
+def readAIDA(file_or_filename):
+    cdef c.istringstream iss
+    cdef vector[c.AnalysisObject*] aobjects
+
+    if hasattr(file_or_filename, 'read'):
+        s = file_or_filename.read()
+    else:
+        with open(file_or_filename) as f:
+            s = f.read()
+
+    make_iss(iss, s)
+     
+    c.ReaderAIDA_create().read(iss, aobjects)
+
+    # Not as expensive as it looks!
+    return aobjects_to_list(&aobjects)
+
+##
+## Writers
+##
+
+def writeYODA(ana_objs, file_or_filename):
+    cdef c.ostringstream oss
+    cdef vector[c.AnalysisObject*] vec
+    cdef AnalysisObject a
+
+    for a in ana_objs:
+        vec.push_back(a._AnalysisObject())
+
+    # Most of the time we just won't care about memory
+    # Perhaps speak with andy re: huge files
+    c.WriterYODA_create().write(oss, vec)
+
+    if hasattr(file_or_filename, 'write'):
+        file_or_filename.write(oss.str().c_str())
+    else:
+        with open(file_or_filename, 'w') as f:
+            f.write(oss.str().c_str())
+
+
+def writeAIDA(ana_objs, file_or_filename):
+    cdef c.ostringstream oss
+    cdef vector[c.AnalysisObject*] vec
+    cdef AnalysisObject a
+
+    for a in ana_objs:
+        vec.push_back(a._AnalysisObject())
+
+    # Most of the time we just won't care about memory
+    # Perhaps speak with andy re: huge files
+    c.WriterAIDA_create().write(oss, vec)
+
+    if hasattr(file_or_filename, 'write'):
+        file_or_filename.write(oss.str().c_str())
+    else:
+        with open(file_or_filename, 'w') as f:
+            f.write(oss.str().c_str())

Added: trunk/pyext/yoda/include/Point2D.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/Point2D.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,128 @@
+from collections import namedtuple
+from libcpp cimport bool
+
+cdef inline pair[double, double] read_symmetric(object val) except *:
+    try:
+        a, b = val
+    except TypeError:
+        a = b = val
+
+    return pair[double, double](a, b)
+
+cdef inline object read_error_pair(pair[double, double] es):
+    return util.Errors(es.first, es.second)
+
+cdef class Point2D(util.Base):
+    """
+    A 2D point with errors, used by the Scatter2D class.
+
+    """
+
+    def __init__(self, x=0, y=0, xerrs=0, yerrs=0):
+        util.set_owned_ptr(self, new c.Point2D())
+        self.coords = x, y
+        self.xErrs = xerrs
+        self.yErrs = yerrs
+
+    def copy(self):
+        return util.new_owned_cls(Point2D,
+                                  new c.Point2D(deref(self._Point2D())))
+
+    property x:
+        """x coordinate"""
+        def __get__(self):
+            return self._Point2D().x()
+
+        def __set__(self, x):
+            self._Point2D().setX(x)
+
+    property y:
+        """y coordinate"""
+        def __get__(self):
+            return self._Point2D().y()
+
+        def __set__(self, y):
+            self._Point2D().setY(y)
+
+    property coords:
+        """x and y coordinates as a tuple"""
+        def __get__(self):
+            return util.XY(self.x, self.y)
+
+        def __set__(self, val):
+            self.x, self.y = val
+
+    property xRange:
+        """The minimum and maximum points within the error"""
+        def __get__(self):
+            return util.Edges(self._Point2D().xMin(),
+                              self._Point2D().xMax())
+
+    property xErrs:
+        """The x errors"""
+        def __get__(self):
+            return read_error_pair(self._Point2D().xErrs())
+
+        def __set__(self, val):
+            self._Point2D().setXErr(read_symmetric(val))
+
+    property yErrs:
+        """The y errors"""
+        def __get__(self):
+            return read_error_pair(self._Point2D().yErrs())
+
+        def __set__(self, val):
+            self._Point2D().setYErr(read_symmetric(val))
+
+    property errs:
+        """The x and y errors as a tuple"""
+        def __get__(self):
+            return util.XY(self.xErrs, self.yErrs)
+
+        def __set__(self, val):
+            self.xErrs, self.yErrs = val
+
+
+    property xErrAvg:
+        def __get__(self):
+            return self.xErrAvg()
+
+    property yErrAvg:
+        def __get__(self):
+            return self.yErrAvg()
+
+    def scale(self, x=1.0, y=1.0):
+        """
+        (x=1.0, y=1.0) -> None
+
+        Scale the Dbn's variables by the given factors
+
+        """
+        self._Point2D().scale(x, y)
+
+    def __repr__(self):
+        return '<Point2D(%g, %g)>' % self.coords
+
+    def __richcmp__(Point2D self, Point2D other, int op):
+        if op == 0: 
+            return deref(self._Point2D()) < deref(other._Point2D())
+        elif op == 1:
+            return deref(self._Point2D()) <= deref(other._Point2D())
+        elif op == 2:
+            return deref(self._Point2D()) == deref(other._Point2D())
+        elif op == 3:
+            return deref(self._Point2D()) != deref(other._Point2D())
+        elif op == 4:
+            return deref(self._Point2D()) > deref(other._Point2D())
+        elif op == 5:
+            return deref(self._Point2D()) >= deref(other._Point2D())
+
+    # Magic stuff
+    cdef c.Point2D *_Point2D(self) except NULL:
+        return <c.Point2D *> self.ptr()
+
+    def __dealloc__(self):
+        cdef c.Point2D *p = self._Point2D()
+        if self._deallocate:
+            del p
+

Added: trunk/pyext/yoda/include/Point3D.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/Point3D.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,122 @@
+from collections import namedtuple
+from libcpp cimport bool
+
+cdef class Point3D(util.Base):
+    """
+    A 1D distribution 'counter', used and exposed by 1D histograms and their bins.
+
+    """
+
+    def __init__(self, x=0, y=0, xerrs=0, yerrs=0):
+        util.set_owned_ptr(self, new c.Point3D())
+        self.xy = x, y
+        self.xerrs = xerrs
+        self.yerrs = yerrs
+
+    def copy(self):
+        return util.new_owned_cls(Point3D,
+                                  new c.Point3D(deref(self._Point3D())))
+
+    property x:
+        """x coordinate"""
+        def __get__(self):
+            return self._Point3D().x()
+
+        def __set__(self, x):
+            self._Point3D().setX(x)
+
+    property y:
+        """y coordinate"""
+        def __get__(self):
+            return self._Point3D().y()
+
+        def __set__(self, y):
+            self._Point3D().setY(y)
+
+    property z:
+        """y coordinate"""
+        def __get__(self):
+            return self._Point3D().z()
+
+        def __set__(self, z):
+            self._Point3D().setZ(z)
+
+    property xyz:
+        def __get__(self):
+            return util.XYZ(self.x, self.y, self.z)
+
+        def __set__(self, val):
+            self.x, self.y, self.z = val
+
+    property xerrs:
+        def __get__(self):
+            return read_error_pair(self._Point3D().xErrs())
+
+        def __set__(self, val):
+            self._Point3D().setXErr(read_symmetric(val))
+
+    property yerrs:
+        def __get__(self):
+            return read_error_pair(self._Point3D().yErrs())
+
+        def __set__(self, val):
+            self._Point3D().setYErr(read_symmetric(val))
+
+    property zerrs:
+        def __get__(self):
+            return read_error_pair(self._Point3D().zErrs())
+
+        def __set__(self, val):
+            self._Point3D().setZErr(read_symmetric(val))
+
+    property xerr_avg:
+        def __get__(self):
+            return self.xErrAvg()
+
+    property yerr_avg:
+        def __get__(self):
+            return self.yErrAvg()
+
+    property zerr_avg:
+        def __get__(self):
+            return self.zErrAvg()
+
+    def scale(self, x=1.0, y=1.0, z=1.0):
+        """
+        (x=1.0, y=1.0, z=1.0) -> None
+
+        Scale the Dbn's variables by the given factors
+
+        """
+        self._Point3D().scale(x, y, z)
+
+    def __repr__(self):
+        return '<Point3D(x=%g, y=%g)>' % (
+            self.x, self.y, self.xerrs, self.yerrs)
+
+    def __str__(self):
+        return 'Point3D(%g, %g)' % self.xy
+
+    def __richcmp__(Point3D self, Point3D other, int op):
+        if op == 0: 
+            return deref(self._Point3D()) < deref(other._Point3D())
+        elif op == 1:
+            return deref(self._Point3D()) <= deref(other._Point3D())
+        elif op == 2:
+            return deref(self._Point3D()) == deref(other._Point3D())
+        elif op == 3:
+            return deref(self._Point3D()) != deref(other._Point3D())
+        elif op == 4:
+            return deref(self._Point3D()) > deref(other._Point3D())
+        elif op == 5:
+            return deref(self._Point3D()) >= deref(other._Point3D())
+
+    # Magic stuff
+    cdef c.Point3D *_Point3D(self) except NULL:
+        return <c.Point3D *> self.ptr()
+
+    def __dealloc__(self):
+        cdef c.Point3D *p = self._Point3D()
+        if self._deallocate:
+            del p
+

Added: trunk/pyext/yoda/include/Profile1D.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/Profile1D.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,113 @@
+#TODO: docstrings!!!
+
+cdef class Profile1D(AnalysisObject):
+    cdef inline c.Profile1D *_Profile1D(self) except NULL:
+        return <c.Profile1D*> self.ptr()
+
+    def __init__(self, *args, **kwargs):
+        #TODO: convert to the new-style Profile1D
+        if len(args) <= 2:
+            self.__init2(*args)
+        elif len(args) >= 3:
+            self.__init5(*args)
+
+    def __init2(self, char *path="", char *title=""):
+        util.set_owned_ptr(
+            self, new c.Profile1D(string(path), string(title))) 
+
+    def __init5(self, size_t nbins, double lower, double upper,
+                  char *path="", char *title=""):
+        util.set_owned_ptr(
+            self, new c.Profile1D(
+                nbins, lower, upper, string(path), string(title)))
+
+    def __len__(self):
+        return self._Profile1D().bins().size()
+
+    def __getitem__(self, py_ix):
+        cdef size_t i = util.pythonic_index(py_ix, self._Profile1D().bins().size())
+        return util.new_borrowed_cls(
+            ProfileBin1D, & self._Profile1D().bins().at(i), self)
+
+    def __repr__(self):
+        return "<Profile1D at %x>" % id(self)
+
+    def fill(self, double x, double y, double weight=1.0):
+        self._Profile1D().fill(x, y, weight)
+
+    def fillMany(self, xs, ys, ws=None):
+        cdef double x, y, w
+        cdef c.Profile1D *p = self._Profile1D()
+
+        itx = iter(xs)
+        ity = iter(ys)
+        if ws:
+            itw = iter(ws)
+            while True:
+                try:
+                    x = next(itx, None)
+                    y = next(ity, None)
+                    w = next(itw, None)
+                except TypeError:
+                    break
+                else:
+                    p.fill(x, y, w)
+        else:
+            while True:
+                try:
+                    x = next(itx, None)
+                    y = next(ity, None)
+                except TypeError:
+                    break
+                else:
+                    p.fill(x, y, 1.0)
+
+    def copy(self, char *path=""):
+        return util.new_owned_cls(Profile1D,
+            new c.Profile1D(deref(self._Profile1D()), string(path)))
+
+    @property
+    def totalDbn(self):
+        return util.new_borrowed_cls(
+            Dbn2D, &self._Profile1D().totalDbn(), self)
+
+    @property
+    def underflow(self):
+        return util.new_borrowed_cls(
+            Dbn2D, &self._Profile1D().underflow(), self)
+
+    @property
+    def overflow(self):
+        return util.new_borrowed_cls(
+            Dbn2D, &self._Profile1D().overflow(), self)
+
+    def reset(self):
+        self._Profile1D().reset()
+
+    def scale(self, double w=1.0):
+        """
+        (w=1.0) -> None
+
+        """
+        if w != 1.0:
+            self._Profile1D().scaleW(w)
+
+    def mergeBins(self, size_t a, size_t b):
+        self._Profile1D().mergeBins(a, b)
+
+    def rebin(self, int n):
+        self._Profile1D().rebin(n)
+
+    def addBin(self, double low, double high):
+        """Add a bin to the Profile1D"""
+        self._Profile1D().addBin(low, high)
+        return self
+
+    # TODO: look at Histo1D
+    def addBins(self, edges):
+        cdef vector[double] cedges
+        for i in edges:
+            cedges.push_back(i)
+        self._Profile1D().addBins(cedges)
+        return self
+

Added: trunk/pyext/yoda/include/Profile2D.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/Profile2D.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,148 @@
+cdef class Profile2D(AnalysisObject):
+    cdef inline c.Profile2D *_Profile2D(self) except NULL:
+        return <c.Profile2D*> self.ptr()
+
+    def __init__(self, *args, **kwargs):
+        if len(args) <= 2:
+            self.__init2(*args, **kwargs)
+        else:
+            self.__init7(*args, **kwargs)
+
+    def __init2(Profile2D self, char *path="", char *title=""):
+        util.set_owned_ptr(
+            self, new c.Profile2D(string(path), string(title))) 
+
+    def __init7(Profile2D self,   nxbins, xlow, xupp,   nybins, ylow, yupp,
+                char *path="", char *title=""):
+
+        util.set_owned_ptr(
+            self, new c.Profile2D(
+                nxbins, xlow, xupp,
+                nybins, ylow, yupp,
+                string(path), string(title)))
+
+    def __len__(self):
+        return self._Profile2D().bins().size()
+
+    def __getitem__(self, py_ix):
+        cdef size_t i = util.pythonic_index(py_ix, self._Profile2D().bins().size())
+        return util.new_borrowed_cls(
+            ProfileBin2D, & self._Profile2D().bins().at(i), self)
+
+    def __repr__(self):
+        return "<Profile2D at %x>" % id(self)
+
+    def fill(self, double x, double y, double z, double weight=1.0):
+        self._Profile2D().fill(x, y, z, weight)
+
+    def bin_by_coord(self, x, y):
+        cdef int index = self._Profile2D().findBinIndex(x, y)
+        print index
+        return self[index]
+
+    def fill_many(self, xs, ys, zs, ws=None):
+        cdef double x, y, z, w
+        cdef c.Profile2D *p = self._Profile2D()
+
+        itx = iter(xs)
+        ity = iter(ys)
+        itz = iter(zs)
+        if ws:
+            itw = iter(ws)
+            while True:
+                try:
+                    x = next(itx, None)
+                    y = next(ity, None)
+                    z = next(itz, None)
+                    w = next(itw, None)
+                except TypeError:
+                    break
+                else:
+                    p.fill(x, y, z, w)
+        else:
+            while True:
+                try:
+                    x = next(itx, None)
+                    y = next(ity, None)
+                    z = next(itz, None)
+                except TypeError:
+                    break
+                else:
+                    p.fill(x, y, z, 1.0)
+
+    def copy(self, char *path=""):
+        return util.new_owned_cls(Profile2D,
+            new c.Profile2D(deref(self._Profile2D()), string(path)))
+
+    @property
+    def total_dbn(self):
+        return util.new_borrowed_cls(
+            Dbn3D, new c.Dbn3D(self._Profile2D().totalDbn()), self)
+
+    @property
+    def outflow(self, ix, iy):
+        return util.new_borrowed_cls(
+            Dbn3D, new c.Dbn3D(self._Profile2D().outflow(ix, iy)), self)
+
+    
+    #def integral(self, overflows=True):
+    #    return self._Profile2D().integral(overflows)
+
+    def sum_w(self, overflows=True):
+        return self._Profile2D().sumW(overflows)
+
+    def sum_w2(self, overflows=True):
+        return self._Profile2D().sumW2(overflows)
+
+    #def mean(self, overflows=True):
+    #    return XY(
+    #        self._Profile2D().xMean(overflows),
+    #        self._Profile2D().yMean(overflows))
+
+    #def variance(self, overflows=True):
+    #    return XY(
+    #        self._Profile2D().xVariance(overflows),
+    #        self._Profile2D().yVariance(overflows))
+
+    #def std_dev(self, overflows=True):
+    #    return XY(
+    #        self._Profile2D().xStdDev(overflows),
+    #        self._Profile2D().yStdDev(overflows))
+
+    #def std_err(self, overflows=True):
+    #    return XY(
+    #        self._Profile2D().xStdErr(overflows),
+    #        self._Profile2D().yStdErr(overflows))
+
+    def reset(self):
+        self._Profile2D().reset()
+
+    #def scale(self, double w=1.0):
+    #    """
+    #    (w=1.0) -> None
+
+    #    """
+    #    if w != 1.0:
+    #        self._Profile2D().scaleW(w)
+
+    #def merge_bins(self, size_t a, size_t b):
+    #    self._Profile2D().mergeBins(a, b)
+
+    # NOTE: Cython 0.17 will be bringing automatic conversion between python and
+    # C++ -- will be very nice here.
+
+    #def rebin(self, int n):
+    #    self._Profile2D().rebin(n)
+
+    #def add_bin(self, double low, double high):
+    #    """Add a bin to the Profile2D"""
+    #    self._Profile2D().addBin(low, high)
+    #    return self
+
+    #def add_bins(self, edges):
+    #    cdef vector[double] cedges
+    #    for i in edges:
+    #        cedges.push_back(i)
+    #    self._Profile2D().addBins(cedges)
+    #    return self
+

Added: trunk/pyext/yoda/include/ProfileBin1D.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/ProfileBin1D.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,56 @@
+cdef class ProfileBin1D(Bin1D_Dbn2D):
+
+    cdef inline c.ProfileBin1D *_ProfileBin1D(self) except NULL:
+        return <c.ProfileBin1D *> self.ptr()
+
+    def __init__(self, double a, double b):
+        util.set_owned_ptr(
+            self, new c.ProfileBin1D(a, b))
+
+    def fill(self, double x, double y, double weight=1.0):
+        self._ProfileBin1D().fill(x, y, weight)
+
+    def fill_bin(self, double y, double weight=1.0):
+        self._ProfileBin1D().fillBin(y, weight)
+
+    @property
+    def mean(self):
+        return self._ProfileBin1D().mean()
+
+    @property
+    def stdDev(self):
+        return self._ProfileBin1D().stdDev()
+
+    @property
+    def variance(self):
+        return self._ProfileBin1D().variance()
+
+    @property
+    def stdErr(self):
+        return self._ProfileBin1D().stdErr()
+
+    @property
+    def rms(self):
+        return self._ProfileBin1D().rms()
+
+    @property
+    def sumWY(self):
+        return self._ProfileBin1D().sumWY()
+
+    @property
+    def sumWY2(self):
+        return self._ProfileBin1D().sumWY2()
+
+    def __add__(ProfileBin1D a, ProfileBin1D b):
+        return util.new_owned_cls(
+            ProfileBin1D,
+            new c.ProfileBin1D(
+                deref(a._ProfileBin1D()) +
+                deref(b._ProfileBin1D())))
+
+    def __sub__(ProfileBin1D a, ProfileBin1D b):
+        return util.new_owned_cls(
+            ProfileBin1D,
+            new c.ProfileBin1D(
+                deref(a._ProfileBin1D()) -
+                deref(b._ProfileBin1D())))

Added: trunk/pyext/yoda/include/ProfileBin2D.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/ProfileBin2D.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,59 @@
+cdef class ProfileBin2D(Bin2D_Dbn3D):
+
+    cdef inline c.ProfileBin2D *_ProfileBin2D(self) except NULL:
+        return <c.ProfileBin2D *> self.ptr()
+
+    def __init__(self, xlow, xhigh, ylow, yhigh):
+        util.set_owned_ptr(
+            self, new c.ProfileBin2D(xlow, xhigh, ylow, yhigh))
+        
+    def fill(self, x, y, z, weight=1.0):
+        self._ProfileBin2D().fill(x, y, z, weight)
+
+    def fill_bin(self, z, weight=1.0):
+        self._ProfileBin2D().fillBin(z, weight)
+
+    @property
+    def mean(self):
+        return self._ProfileBin2D().mean()
+
+    @property
+    def std_dev(self):
+        return self._ProfileBin2D().stdDev()
+    
+    @property
+    def variance(self):
+        return self._ProfileBin2D().variance()
+
+    @property
+    def std_err(self):
+        return self._ProfileBin2D().stdErr()
+
+    @property
+    def rms(self):
+        return self._ProfileBin2D().rms()
+
+    @property
+    def sum_wz(self):
+        return self._ProfileBin2D().sumWZ()
+
+    @property
+    def sum_wz2(self):
+        return self._ProfileBin2D().sumWZ2()
+
+    def __add__(ProfileBin2D a, ProfileBin2D b):
+        return util.new_owned_cls(
+            ProfileBin2D,
+            new c.ProfileBin2D(
+                deref(a._ProfileBin2D()) + 
+                deref(b._ProfileBin2D())))
+
+    def __sub__(ProfileBin2D a, ProfileBin2D b):
+        return util.new_owned_cls(
+            ProfileBin2D,
+            new c.ProfileBin2D(
+                deref(a._ProfileBin2D()) -
+                deref(b._ProfileBin2D())))
+
+    def __repr__(self):
+        return 'ProfileBin2D(%g, %g, %g, %g)' % (self.edges.x + self.edges.y)

Added: trunk/pyext/yoda/include/Scatter2D.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/include/Scatter2D.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,95 @@
+
+cdef class Scatter2D(AnalysisObject):
+    """
+    1D histogram. Complete histogramming is supported, including
+    uniform/regular binning, variable wideth bininng, unbinned gaps in
+    the covered range, and under/overflows. Rebinning by integer
+    factors, or by explicit merging of contiguous bins is also
+    supported.
+
+    Rescaling of weights and/or the x axis is permitted in-place: the
+    result is still a valid Scatter2D. Binning-compatible 1D histograms
+    may be divided, resulting in a Scatter2D since further fulls would
+    not be meaningful.
+
+    Several sets of arguments are tried by the constructor in the
+    following order.
+
+    Scatter2D(path="", title=""). Construct a histogram with optional path
+    and title but no bins.
+
+    Scatter2D(B, path="", title=""). Construct a histogram from an
+    iterator of bins, B.
+
+    Scatter2D(E, path="", title=""). Construct a histogram from an
+    iterator of edges, E.
+
+    Should this constructor fail, then 
+    Scatter2D(nbins, low, high, path="", title="")
+    """
+
+    # Is there a reason why this sounds like an advert? I'M ALREADY SOLD
+    # DAMMIT!
+
+
+    cdef inline c.Scatter2D *_Scatter2D(self) except NULL:
+        return <c.Scatter2D*> self.ptr()
+
+    # There is a pythonic constructor here, and it looks a little like...
+    # __init__(self, *args, **kwargs)
+    # ([edge], path="", title="")
+    # ([bins], **kwargs)
+
+    def __init__(self, *args, **kwargs):
+        #try_loop([self.__init2, self.__init5, self.__init3], *args, **kwargs)
+        util.try_loop([self.__init_2, self.__init_3], *args, **kwargs)
+
+    def __init_2(self, char* path="", char* title=""):
+        util.set_owned_ptr(self, new c.Scatter2D(string(path), string(title)))
+
+    def __init_3(self, points, char* path="", char* title=""):
+        self.__init_2(path, title)
+        self.addPoints(points)
+
+    def __len__(self):
+        return self._Scatter2D().numPoints()
+
+    def __getitem__(self, py_ix):
+        cdef size_t i = util.pythonic_index(
+            py_ix, self._Scatter2D().numPoints())
+
+        return util.new_borrowed_cls(
+            Point2D, & self._Scatter2D().point(i), self)
+
+    def copy(self, char *path=""):
+        """(path="") -> Scatter2D. Clone this Scatter2D with optional new path."""
+        return util.new_owned_cls(Scatter2D,
+            new c.Scatter2D(deref(self._Scatter2D()), string(path)))
+
+    def addPoints(self, iterable):
+        for row in iterable:
+            self.addPoint(*row)
+
+    def addPoint(self, *args, **kwargs):
+        try:
+            self.__addPoint_point(*args, **kwargs)
+        except TypeError:
+            self.__addPoint_explicit(*args, **kwargs)
+
+    def __addPoint_explicit(self, x, y, xerrs=0, yerrs=0):
+        self.__addPoint_point(Point2D(x, y, xerrs, yerrs))
+
+    def __addPoint_point(self, Point2D p):
+        self._Scatter2D().addPoint(p._Point2D()[0])
+
+    def combineWith(self, others):
+        cdef Scatter2D other
+        try:
+            # Can we type it as a scatter2D?
+            other = others
+        except TypeError:
+            # Could be an iterable...
+            for other in others:
+                self._Scatter2D().combineWith(deref(other._Scatter2D()))
+        else:
+            self._Scatter2D().combineWith(deref(other._Scatter2D()))

Added: trunk/pyext/yoda/util.pxd
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/util.pxd	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,56 @@
+# A base CppObject which prevents null pointer access
+cdef class Base:
+    """A base class which wraps a pointer"""
+    cdef void *_ptr
+    cdef bint _deallocate
+    cdef object _owner
+
+    cdef inline void *ptr(self) except NULL:
+        if self._ptr == NULL:
+            raise MemoryError('Null pointer referenced: '
+                              'perhaps the class is uninitialised.') 
+
+        return self._ptr
+
+# Magic for setting pointers
+
+# Use this for setting a pointer that is owned by the object obj
+# e.g. if you want deallocation to happen automatically when this object goes
+# out of scope
+cdef inline set_owned_ptr(Base obj, void *ptr):
+    obj._ptr = ptr
+    obj._deallocate = True
+
+# Use this for setting a pointer that is *not* owned by the object obj e.g. if
+# you were given this object by a class which will deallocate it once it's
+# finished
+cdef inline set_borrowed_ptr(Base obj, void *ptr, object owner):
+    obj._ptr = ptr
+    obj._deallocate = False
+    obj._owner = owner
+
+# Use this to create a new object of type cls from the pointer ptr. The class is
+# one which owns its pointer, and will deallocate when it's done. It's this one
+# that you want to use for e.g. loaders or factory methods where the user is
+# expected to manage memory. Or where you've explicitly called new.
+cdef inline object new_owned_cls(object cls, void *ptr):
+    obj = cls.__new__(cls)
+    set_owned_ptr(obj, ptr)
+    return obj
+
+# Use this to create a thin wrapper around a pointer that will *not* be
+# deallocated when it goes out of scope. Useful when you're given a reference
+# from a class which is not expecting you to delete its innards!
+cdef inline object new_borrowed_cls(object cls, void *ptr, object owner):
+    obj = cls.__new__(cls)
+    set_borrowed_ptr(obj, ptr, owner)
+    return obj
+
+cdef inline size_t pythonic_index(int i, size_t size) except? 0:
+    if i < 0:
+        i += size
+        
+    if 0 <= i < size:
+        return i
+    else:
+        raise IndexError

Added: trunk/pyext/yoda/util.pyx
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ trunk/pyext/yoda/util.pyx	Fri Aug  3 03:14:44 2012	(r514)
@@ -0,0 +1,19 @@
+from collections import namedtuple
+
+cdef class Base:
+    pass
+
+def try_loop(fs, *args, char *_msg='Invalid arguments', **kwargs):
+    for f in fs:
+        try:
+            f(*args, **kwargs)
+            return
+        except TypeError:
+            pass
+
+    raise TypeError(_msg)
+
+Edges = namedtuple('Edges', ('low', 'high'))
+Errors = namedtuple('Errors', ('minus', 'plus'))
+XY = namedtuple('XY', ('x', 'y'))
+XYZ = namedtuple('XYZ', ('x', 'y', 'z'))


More information about the yoda-svn mailing list