From 61f3ab8f23f4c924d455757bf3e65f8487521b5a Mon Sep 17 00:00:00 2001
From: Daniel Baumann <daniel.baumann@progress-linux.org>
Date: Sat, 13 Apr 2024 13:57:42 +0200
Subject: Adding upstream version 1.3.

Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
---
 include/2geom/2geom.h                              |   75 ++
 include/2geom/affine.h                             |  244 +++++
 include/2geom/angle.h                              |  408 +++++++
 include/2geom/basic-intersection.h                 |  151 +++
 include/2geom/bezier-curve.h                       |  366 +++++++
 include/2geom/bezier-to-sbasis.h                   |   94 ++
 include/2geom/bezier-utils.h                       |   99 ++
 include/2geom/bezier.h                             |  394 +++++++
 include/2geom/cairo-path-sink.h                    |   91 ++
 include/2geom/choose.h                             |  147 +++
 include/2geom/circle.h                             |  165 +++
 include/2geom/concepts.h                           |  209 ++++
 include/2geom/conic_section_clipper.h              |   58 +
 include/2geom/conic_section_clipper_cr.h           |   64 ++
 include/2geom/conic_section_clipper_impl.h         |  346 ++++++
 include/2geom/conicsec.h                           |  537 ++++++++++
 include/2geom/convex-hull.h                        |  346 ++++++
 include/2geom/coord.h                              |  208 ++++
 include/2geom/crossing.h                           |  213 ++++
 include/2geom/curve.h                              |  375 +++++++
 include/2geom/curves.h                             |   54 +
 include/2geom/d2.h                                 |  564 ++++++++++
 include/2geom/ellipse.h                            |  260 +++++
 include/2geom/elliptical-arc.h                     |  344 ++++++
 include/2geom/exception.h                          |  157 +++
 include/2geom/forward.h                            |  127 +++
 include/2geom/generic-interval.h                   |  374 +++++++
 include/2geom/generic-rect.h                       |  547 ++++++++++
 include/2geom/geom.h                               |   66 ++
 include/2geom/int-interval.h                       |   63 ++
 include/2geom/int-point.h                          |  202 ++++
 include/2geom/int-rect.h                           |   75 ++
 include/2geom/intersection-graph.h                 |  259 +++++
 include/2geom/intersection.h                       |  147 +++
 include/2geom/interval.h                           |  245 +++++
 include/2geom/intervaltree/interval_tree.h         |  126 +++
 include/2geom/line.h                               |  605 +++++++++++
 include/2geom/linear.h                             |  167 +++
 include/2geom/math-utils.h                         |  140 +++
 include/2geom/nearest-time.h                       |  141 +++
 include/2geom/numeric/fitting-model.h              |  521 +++++++++
 include/2geom/numeric/fitting-tool.h               |  562 ++++++++++
 include/2geom/numeric/linear_system.h              |  138 +++
 include/2geom/numeric/matrix.h                     |  603 +++++++++++
 .../2geom/numeric/symmetric-matrix-fs-operation.h  |  102 ++
 include/2geom/numeric/symmetric-matrix-fs-trace.h  |  427 ++++++++
 include/2geom/numeric/symmetric-matrix-fs.h        |  733 +++++++++++++
 include/2geom/numeric/vector.h                     |  594 +++++++++++
 include/2geom/ord.h                                |   80 ++
 include/2geom/orphan-code/arc-length.h             |   58 +
 include/2geom/orphan-code/chebyshev.h              |   30 +
 .../2geom/orphan-code/intersection-by-smashing.h   |   78 ++
 include/2geom/orphan-code/linear-of.h              |  269 +++++
 include/2geom/orphan-code/linearN.h                |  363 +++++++
 include/2geom/orphan-code/redblacktree.h           |  121 +++
 include/2geom/orphan-code/rtree.h                  |  241 +++++
 include/2geom/orphan-code/sbasis-of.h              |  638 +++++++++++
 include/2geom/orphan-code/sbasisN.h                | 1123 ++++++++++++++++++++
 include/2geom/parallelogram.h                      |   83 ++
 include/2geom/path-intersection.h                  |  118 ++
 include/2geom/path-sink.h                          |  253 +++++
 include/2geom/path.h                               |  917 ++++++++++++++++
 include/2geom/pathvector.h                         |  304 ++++++
 include/2geom/piecewise.h                          |  945 ++++++++++++++++
 include/2geom/point.h                              |  449 ++++++++
 include/2geom/polynomial.h                         |  264 +++++
 include/2geom/ray.h                                |  192 ++++
 include/2geom/rect.h                               |  263 +++++
 include/2geom/sbasis-2d.h                          |  371 +++++++
 include/2geom/sbasis-curve.h                       |  160 +++
 include/2geom/sbasis-geometric.h                   |  146 +++
 include/2geom/sbasis-math.h                        |   99 ++
 include/2geom/sbasis-poly.h                        |   56 +
 include/2geom/sbasis-to-bezier.h                   |   87 ++
 include/2geom/sbasis.h                             |  530 +++++++++
 include/2geom/solver.h                             |   88 ++
 include/2geom/svg-path-parser.h                    |  199 ++++
 include/2geom/svg-path-writer.h                    |  122 +++
 include/2geom/sweep-bounds.h                       |   62 ++
 include/2geom/sweeper.h                            |  189 ++++
 include/2geom/symbolic/determinant-minor.h         |  175 +++
 include/2geom/symbolic/implicit.h                  |  353 ++++++
 include/2geom/symbolic/matrix.h                    |  265 +++++
 include/2geom/symbolic/multi-index.h               |  169 +++
 include/2geom/symbolic/multipoly.h                 |  684 ++++++++++++
 include/2geom/symbolic/mvpoly-tools.h              |  690 ++++++++++++
 include/2geom/symbolic/polynomial.h                |  569 ++++++++++
 include/2geom/symbolic/unity-builder.h             |  102 ++
 include/2geom/transforms.h                         |  370 +++++++
 include/2geom/utils.h                              |  114 ++
 90 files changed, 25322 insertions(+)
 create mode 100644 include/2geom/2geom.h
 create mode 100644 include/2geom/affine.h
 create mode 100644 include/2geom/angle.h
 create mode 100644 include/2geom/basic-intersection.h
 create mode 100644 include/2geom/bezier-curve.h
 create mode 100644 include/2geom/bezier-to-sbasis.h
 create mode 100644 include/2geom/bezier-utils.h
 create mode 100644 include/2geom/bezier.h
 create mode 100644 include/2geom/cairo-path-sink.h
 create mode 100644 include/2geom/choose.h
 create mode 100644 include/2geom/circle.h
 create mode 100644 include/2geom/concepts.h
 create mode 100644 include/2geom/conic_section_clipper.h
 create mode 100644 include/2geom/conic_section_clipper_cr.h
 create mode 100644 include/2geom/conic_section_clipper_impl.h
 create mode 100644 include/2geom/conicsec.h
 create mode 100644 include/2geom/convex-hull.h
 create mode 100644 include/2geom/coord.h
 create mode 100644 include/2geom/crossing.h
 create mode 100644 include/2geom/curve.h
 create mode 100644 include/2geom/curves.h
 create mode 100644 include/2geom/d2.h
 create mode 100644 include/2geom/ellipse.h
 create mode 100644 include/2geom/elliptical-arc.h
 create mode 100644 include/2geom/exception.h
 create mode 100644 include/2geom/forward.h
 create mode 100644 include/2geom/generic-interval.h
 create mode 100644 include/2geom/generic-rect.h
 create mode 100644 include/2geom/geom.h
 create mode 100644 include/2geom/int-interval.h
 create mode 100644 include/2geom/int-point.h
 create mode 100644 include/2geom/int-rect.h
 create mode 100644 include/2geom/intersection-graph.h
 create mode 100644 include/2geom/intersection.h
 create mode 100644 include/2geom/interval.h
 create mode 100644 include/2geom/intervaltree/interval_tree.h
 create mode 100644 include/2geom/line.h
 create mode 100644 include/2geom/linear.h
 create mode 100644 include/2geom/math-utils.h
 create mode 100644 include/2geom/nearest-time.h
 create mode 100644 include/2geom/numeric/fitting-model.h
 create mode 100644 include/2geom/numeric/fitting-tool.h
 create mode 100644 include/2geom/numeric/linear_system.h
 create mode 100644 include/2geom/numeric/matrix.h
 create mode 100644 include/2geom/numeric/symmetric-matrix-fs-operation.h
 create mode 100644 include/2geom/numeric/symmetric-matrix-fs-trace.h
 create mode 100644 include/2geom/numeric/symmetric-matrix-fs.h
 create mode 100644 include/2geom/numeric/vector.h
 create mode 100644 include/2geom/ord.h
 create mode 100644 include/2geom/orphan-code/arc-length.h
 create mode 100644 include/2geom/orphan-code/chebyshev.h
 create mode 100644 include/2geom/orphan-code/intersection-by-smashing.h
 create mode 100644 include/2geom/orphan-code/linear-of.h
 create mode 100644 include/2geom/orphan-code/linearN.h
 create mode 100644 include/2geom/orphan-code/redblacktree.h
 create mode 100644 include/2geom/orphan-code/rtree.h
 create mode 100644 include/2geom/orphan-code/sbasis-of.h
 create mode 100644 include/2geom/orphan-code/sbasisN.h
 create mode 100644 include/2geom/parallelogram.h
 create mode 100644 include/2geom/path-intersection.h
 create mode 100644 include/2geom/path-sink.h
 create mode 100644 include/2geom/path.h
 create mode 100644 include/2geom/pathvector.h
 create mode 100644 include/2geom/piecewise.h
 create mode 100644 include/2geom/point.h
 create mode 100644 include/2geom/polynomial.h
 create mode 100644 include/2geom/ray.h
 create mode 100644 include/2geom/rect.h
 create mode 100644 include/2geom/sbasis-2d.h
 create mode 100644 include/2geom/sbasis-curve.h
 create mode 100644 include/2geom/sbasis-geometric.h
 create mode 100644 include/2geom/sbasis-math.h
 create mode 100644 include/2geom/sbasis-poly.h
 create mode 100644 include/2geom/sbasis-to-bezier.h
 create mode 100644 include/2geom/sbasis.h
 create mode 100644 include/2geom/solver.h
 create mode 100644 include/2geom/svg-path-parser.h
 create mode 100644 include/2geom/svg-path-writer.h
 create mode 100644 include/2geom/sweep-bounds.h
 create mode 100644 include/2geom/sweeper.h
 create mode 100644 include/2geom/symbolic/determinant-minor.h
 create mode 100644 include/2geom/symbolic/implicit.h
 create mode 100644 include/2geom/symbolic/matrix.h
 create mode 100644 include/2geom/symbolic/multi-index.h
 create mode 100644 include/2geom/symbolic/multipoly.h
 create mode 100644 include/2geom/symbolic/mvpoly-tools.h
 create mode 100644 include/2geom/symbolic/polynomial.h
 create mode 100644 include/2geom/symbolic/unity-builder.h
 create mode 100644 include/2geom/transforms.h
 create mode 100644 include/2geom/utils.h

(limited to 'include/2geom')

diff --git a/include/2geom/2geom.h b/include/2geom/2geom.h
new file mode 100644
index 0000000..7bf36ae
--- /dev/null
+++ b/include/2geom/2geom.h
@@ -0,0 +1,75 @@
+/**
+ * \file
+ * \brief Include everything
+ *//*
+ * Authors:
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * 
+ * Copyright 2011 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_2GEOM_H
+#define LIB2GEOM_SEEN_2GEOM_H
+
+#include <2geom/forward.h>
+
+// primitives
+#include <2geom/coord.h>
+#include <2geom/point.h>
+#include <2geom/interval.h>
+#include <2geom/rect.h>
+#include <2geom/angle.h>
+#include <2geom/ray.h>
+#include <2geom/line.h>
+#include <2geom/affine.h>
+#include <2geom/transforms.h>
+
+// curves and paths
+#include <2geom/curves.h>
+#include <2geom/path.h>
+#include <2geom/pathvector.h>
+
+// fragments
+#include <2geom/d2.h>
+#include <2geom/linear.h>
+#include <2geom/bezier.h>
+#include <2geom/sbasis.h>
+
+// others
+#include <2geom/math-utils.h>
+#include <2geom/utils.h>
+
+#endif // LIB2GEOM_SEEN_2GEOM_H
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/affine.h b/include/2geom/affine.h
new file mode 100644
index 0000000..470d5fc
--- /dev/null
+++ b/include/2geom/affine.h
@@ -0,0 +1,244 @@
+/** 
+ * \file
+ * \brief 3x3 affine transformation matrix.
+ *//*
+ * Authors:
+ *   Lauris Kaplinski <lauris@kaplinski.com> (Original NRAffine definition and related macros)
+ *   Nathan Hurst <njh@mail.csse.monash.edu.au> (Geom::Affine class version of the above)
+ *   Michael G. Sloan <mgsloan@gmail.com> (reorganization and additions)
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com> (removal of boilerplate, docs)
+ *
+ * This code is in public domain.
+ */
+
+#ifndef LIB2GEOM_SEEN_AFFINE_H
+#define LIB2GEOM_SEEN_AFFINE_H
+
+#include <boost/operators.hpp>
+#include <2geom/forward.h>
+#include <2geom/point.h>
+#include <2geom/utils.h>
+
+namespace Geom {
+
+/**
+ * @brief 3x3 matrix representing an affine transformation.
+ *
+ * Affine transformations on elements of a vector space are transformations which can be
+ * expressed in terms of matrix multiplication followed by addition
+ * (\f$x \mapsto A x + b\f$). They can be thought of as generalizations of linear functions
+ * (\f$y = a x + b\f$) to vector spaces. Affine transformations of points on a 2D plane preserve
+ * the following properties:
+ *
+ * - Colinearity: if three points lie on the same line, they will still be colinear after
+ *   an affine transformation.
+ * - Ratios of distances between points on the same line are preserved
+ * - Parallel lines remain parallel.
+ *
+ * All affine transformations on 2D points can be written as combinations of scaling, rotation,
+ * shearing and translation. They can be represented as a combination of a vector and a 2x2 matrix,
+ * but this form is inconvenient to work with. A better solution is to represent all affine
+ * transformations on the 2D plane as 3x3 matrices, where the last column has fixed values.
+ * \f[ A = \left[ \begin{array}{ccc}
+    c_0 & c_1 & 0 \\
+    c_2 & c_3 & 0 \\
+    c_4 & c_5 & 1 \end{array} \right]\f]
+ *
+ * We then interpret points as row vectors of the form \f$[p_X, p_Y, 1]\f$. Applying a
+ * transformation to a point can be written as right-multiplication by a 3x3 matrix
+ * (\f$p' = pA\f$). This subset of matrices is closed under multiplication - combination
+ * of any two transforms can be expressed as the multiplication of their matrices.
+ * In this representation, the \f$c_4\f$ and \f$c_5\f$ coefficients represent
+ * the translation component of the transformation.
+ *
+ * Matrices can be multiplied by other more specific transformations. When multiplying,
+ * the transformations are applied from left to right, so for example <code>m = a * b</code>
+ * means: @a m first transforms by a, then by b.
+ *
+ * @ingroup Transforms
+ */
+class Affine
+    : boost::equality_comparable< Affine // generates operator!= from operator==
+    , boost::multipliable1< Affine
+    , MultipliableNoncommutative< Affine, Translate
+    , MultipliableNoncommutative< Affine, Scale
+    , MultipliableNoncommutative< Affine, Rotate
+    , MultipliableNoncommutative< Affine, HShear
+    , MultipliableNoncommutative< Affine, VShear
+    , MultipliableNoncommutative< Affine, Zoom
+      > > > > > > > >
+{
+    Coord _c[6];
+public:
+    Affine() {
+        _c[0] = _c[3] = 1;
+        _c[1] = _c[2] = _c[4] = _c[5] = 0;
+    }
+
+    /** @brief Create a matrix from its coefficient values.
+     * It's rather inconvenient to directly create matrices in this way. Use transform classes
+     * if your transformation has a geometric interpretation.
+     * @see Translate
+     * @see Scale
+     * @see Rotate
+     * @see HShear
+     * @see VShear
+     * @see Zoom */
+    Affine(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, Coord c5) {
+        _c[0] = c0; _c[1] = c1;
+        _c[2] = c2; _c[3] = c3;
+        _c[4] = c4; _c[5] = c5;
+    }
+
+    /** @brief Access a coefficient by its index. */
+    inline Coord operator[](unsigned i) const { return _c[i]; }
+    inline Coord &operator[](unsigned i) { return _c[i]; }
+
+    /// @name Combine with other transformations
+    /// @{
+    Affine &operator*=(Affine const &m);
+    // implemented in transforms.cpp
+    Affine &operator*=(Translate const &t);
+    Affine &operator*=(Scale const &s);
+    Affine &operator*=(Rotate const &r);
+    Affine &operator*=(HShear const &h);
+    Affine &operator*=(VShear const &v);
+    Affine &operator*=(Zoom const &);
+    /// @}
+
+    bool operator==(Affine const &o) const {
+        for(unsigned i = 0; i < 6; ++i) {
+            if ( _c[i] != o._c[i] ) return false;
+        }
+        return true;
+    }
+
+    /// @name Get the parameters of the matrix's transform
+    /// @{
+    Point xAxis() const;
+    Point yAxis() const;
+    Point translation() const;
+    Coord expansionX() const;
+    Coord expansionY() const;
+    Point expansion() const { return Point(expansionX(), expansionY()); }
+    /// @}
+    
+    /// @name Modify the matrix
+    /// @{
+    void setXAxis(Point const &vec);
+    void setYAxis(Point const &vec);
+
+    void setTranslation(Point const &loc);
+
+    void setExpansionX(Coord val);
+    void setExpansionY(Coord val);
+    void setIdentity();
+    /// @}
+
+    /// @name Inspect the matrix's transform
+    /// @{
+    bool isIdentity(Coord eps = EPSILON) const;
+
+    bool isTranslation(Coord eps = EPSILON) const;
+    bool isScale(Coord eps = EPSILON) const;
+    bool isUniformScale(Coord eps = EPSILON) const;
+    bool isRotation(Coord eps = EPSILON) const;
+    bool isHShear(Coord eps = EPSILON) const;
+    bool isVShear(Coord eps = EPSILON) const;
+
+    bool isNonzeroTranslation(Coord eps = EPSILON) const;
+    bool isNonzeroScale(Coord eps = EPSILON) const;
+    bool isNonzeroUniformScale(Coord eps = EPSILON) const;
+    bool isNonzeroRotation(Coord eps = EPSILON) const;
+    bool isNonzeroNonpureRotation(Coord eps = EPSILON) const;
+    Point rotationCenter() const;
+    bool isNonzeroHShear(Coord eps = EPSILON) const;
+    bool isNonzeroVShear(Coord eps = EPSILON) const;
+
+    bool isZoom(Coord eps = EPSILON) const;
+    bool preservesArea(Coord eps = EPSILON) const;
+    bool preservesAngles(Coord eps = EPSILON) const;
+    bool preservesDistances(Coord eps = EPSILON) const;
+    bool flips() const;
+
+    bool isSingular(Coord eps = EPSILON) const;
+    /// @}
+
+    /// @name Compute other matrices
+    /// @{
+    Affine withoutTranslation() const {
+        Affine ret(*this);
+        ret.setTranslation(Point(0,0));
+        return ret;
+    }
+    Affine inverse() const;
+    /// @}
+
+    /// @name Compute scalar values
+    /// @{
+    Coord det() const;
+    Coord descrim2() const;
+    Coord descrim() const;
+    /// @}
+    inline static Affine identity();
+};
+
+/** @brief Print out the Affine (for debugging).
+ * @relates Affine */
+inline std::ostream &operator<< (std::ostream &out_file, const Geom::Affine &m) {
+    out_file << "A: " << m[0] << "  C: " << m[2] << "  E: " << m[4] << "\n";
+    out_file << "B: " << m[1] << "  D: " << m[3] << "  F: " << m[5] << "\n";
+    return out_file;
+}
+
+// Affine factories
+Affine from_basis(const Point &x_basis, const Point &y_basis, const Point &offset=Point(0,0));
+Affine elliptic_quadratic_form(Affine const &m);
+
+/** Given a matrix (ignoring the translation) this returns the eigen
+ * values and vectors. */
+class Eigen{
+public:
+    Point vectors[2];
+    double values[2];
+    Eigen(Affine const &m);
+    Eigen(double M[2][2]);
+};
+
+/** @brief Create an identity matrix.
+ * This is a convenience function identical to Affine::identity(). */
+inline Affine identity() {
+    Affine ret(Affine::identity());
+    return ret; // allow NRVO
+}
+
+/** @brief Create an identity matrix.
+ * @return The matrix
+ *         \f$\left[\begin{array}{ccc}
+           1 & 0 & 0 \\
+           0 & 1 & 0 \\
+           0 & 0 & 1 \end{array}\right]\f$.
+ * @relates Affine */
+inline Affine Affine::identity() {
+    Affine ret(1.0, 0.0,
+               0.0, 1.0,
+               0.0, 0.0);
+    return ret; // allow NRVO
+}
+
+bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON);
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_AFFINE_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/angle.h b/include/2geom/angle.h
new file mode 100644
index 0000000..f0caaba
--- /dev/null
+++ b/include/2geom/angle.h
@@ -0,0 +1,408 @@
+/**
+ *  \file
+ *  \brief Various trigoniometric helper functions
+ *//*
+ *  Authors:
+ *   Johan Engelen <goejendaagh@zonnet.nl>
+ *   Marco Cecchetti <mrcekets at gmail.com>
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright (C) 2007-2010 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_ANGLE_H
+#define LIB2GEOM_SEEN_ANGLE_H
+
+#include <cmath>
+#include <boost/operators.hpp>
+#include <2geom/exception.h>
+#include <2geom/coord.h>
+#include <2geom/point.h>
+
+namespace Geom {
+
+#ifndef M_PI
+# define M_PI 3.14159265358979323846
+#endif
+#ifndef M_1_2PI
+# define M_1_2PI 0.159154943091895335768883763373
+#endif
+
+/** @brief Wrapper for angular values.
+ *
+ * This class is a convenience wrapper that implements the behavior generally expected of angles,
+ * like addition modulo \f$2\pi\f$. The value returned from the default conversion
+ * to <tt>double</tt> is in the range \f$[-\pi, \pi)\f$ - the convention used by C's
+ * math library.
+ *
+ * This class holds only a single floating point value, so passing it by value will generally
+ * be faster than passing it by const reference.
+ *
+ * @ingroup Primitives
+ */
+class Angle
+    : boost::additive< Angle
+    , boost::additive< Angle, Coord
+    , boost::equality_comparable< Angle
+    , boost::equality_comparable< Angle, Coord
+      > > > >
+{
+public:
+    Angle() : _angle(0) {}
+    Angle(Coord v) : _angle(v) { _normalize(); } // this can be called implicitly
+    explicit Angle(Point const &p) : _angle(atan2(p)) { _normalize(); }
+    Angle(Point const &a, Point const &b) : _angle(angle_between(a, b)) { _normalize(); }
+    operator Coord() const { return radians(); }
+    Angle &operator+=(Angle o) {
+        _angle += o._angle;
+        _normalize();
+        return *this;
+    }
+    Angle &operator-=(Angle o) {
+        _angle -= o._angle;
+        _normalize();
+        return *this;
+    }
+    Angle &operator+=(Coord a) {
+        *this += Angle(a);
+        return *this;
+    }
+    Angle &operator-=(Coord a) {
+        *this -= Angle(a);
+        return *this;
+    }
+    bool operator==(Angle o) const {
+        return _angle == o._angle;
+    }
+    bool operator==(Coord c) const {
+        return _angle == Angle(c)._angle;
+    }
+
+    /** @brief Get the angle as radians.
+     * @return Number in range \f$[-\pi, \pi)\f$. */
+    Coord radians() const {
+        return _angle >= M_PI ? _angle - 2*M_PI : _angle;
+    }
+    /** @brief Get the angle as positive radians.
+     * @return Number in range \f$[0, 2\pi)\f$. */
+    Coord radians0() const {
+        return _angle;
+    }
+    /** @brief Get the angle as degrees in math convention.
+     * @return Number in range [-180, 180) obtained by scaling the result of radians()
+     *         by \f$180/\pi\f$. */
+    Coord degrees() const { return radians() * (180.0 / M_PI); }
+    /** @brief Get the angle as degrees in clock convention.
+     * This method converts the angle to the "clock convention": angles start from the +Y axis
+     * and grow clockwise. This means that 0 corresponds to \f$\pi/2\f$ radians,
+     * 90 to 0 radians, 180 to \f$-\pi/2\f$ radians, and 270 to \f$\pi\f$ radians.
+     * @return A number in the range [0, 360).
+     */
+    Coord degreesClock() const {
+        Coord ret = 90.0 - _angle * (180.0 / M_PI);
+        if (ret < 0) ret += 360;
+        return ret;
+    }
+    /** @brief Create an angle from its measure in radians. */
+    static Angle from_radians(Coord d) {
+        Angle a(d);
+        return a;
+    }
+    /** @brief Create an angle from its measure in degrees. */
+    static Angle from_degrees(Coord d) {
+        Angle a(d * (M_PI / 180.0));
+        return a;
+    }
+    /** @brief Create an angle from its measure in degrees in clock convention.
+     * @see Angle::degreesClock() */
+    static Angle from_degrees_clock(Coord d) {
+        // first make sure d is in [0, 360)
+        d = std::fmod(d, 360.0);
+        if (d < 0) d += 360.0;
+        Coord rad = M_PI/2 - d * (M_PI / 180.0);
+        if (rad < 0) rad += 2*M_PI;
+        Angle a;
+        a._angle = rad;
+        return a;
+    }
+private:
+
+    void _normalize() {
+        _angle = std::fmod(_angle, 2*M_PI);
+        if (_angle < 0) _angle += 2*M_PI;
+        //_angle -= floor(_angle * (1.0/(2*M_PI))) * 2*M_PI;
+    }
+    Coord _angle; // this is always in [0, 2pi)
+    friend class AngleInterval;
+};
+
+inline Angle distance(Angle const &a, Angle const &b) {
+    // the distance cannot be larger than M_PI.
+    Coord ac = a.radians0();
+    Coord bc = b.radians0();
+    Coord d = fabs(ac - bc);
+    return Angle(d > M_PI ? 2*M_PI - d : d);
+}
+
+/** @brief Directed angular interval.
+ *
+ * Wrapper for directed angles with defined start and end values. Useful e.g. for representing
+ * the portion of an ellipse in an elliptical arc. Both extreme angles are contained
+ * in the interval (it is a closed interval). Angular intervals can also be interptered
+ * as functions \f$f: [0, 1] \to [-\pi, \pi)\f$, which return the start angle for 0,
+ * the end angle for 1, and interpolate linearly for other values. Note that such functions
+ * are not continuous if the interval crosses the angle \f$\pi\f$.
+ *
+ * This class can represent all directed angular intervals, including empty ones.
+ * However, not all possible intervals can be created with the constructors.
+ * For full control, use the setInitial(), setFinal() and setAngles() methods.
+ *
+ * @ingroup Primitives
+ */
+class AngleInterval
+    : boost::equality_comparable< AngleInterval >
+{
+public:
+    AngleInterval() {}
+    /** @brief Create an angular interval from two angles and direction.
+     * If the initial and final angle are the same, a degenerate interval
+     * (containing only one angle) will be created.
+     * @param s Starting angle
+     * @param e Ending angle
+     * @param cw Which direction the interval goes. True means that it goes
+     *   in the direction of increasing angles, while false means in the direction
+     *   of decreasing angles. */
+    AngleInterval(Angle s, Angle e, bool cw = false)
+        : _start_angle(s), _end_angle(e), _sweep(cw), _full(false)
+    {}
+    AngleInterval(double s, double e, bool cw = false)
+        : _start_angle(s), _end_angle(e), _sweep(cw), _full(false)
+    {}
+    /** @brief Create an angular interval from three angles.
+     * If the inner angle is exactly equal to initial or final angle,
+     * the sweep flag will be set to true, i.e. the interval will go
+     * in the direction of increasing angles.
+     *
+     * If the initial and final angle are the same, but the inner angle
+     * is different, a full angle in the direction of increasing angles
+     * will be created.
+     *
+     * @param s Initial angle
+     * @param inner Angle contained in the interval
+     * @param e Final angle */
+    AngleInterval(Angle s, Angle inner, Angle e)
+        : _start_angle(s)
+        , _end_angle(e)
+        , _sweep((inner-s).radians0() <= (e-s).radians0())
+        , _full(s == e && s != inner)
+    {
+        if (_full) {
+            _sweep = true;
+        }
+    }
+
+    /// Get the start angle.
+    Angle initialAngle() const { return _start_angle; }
+    /// Get the end angle.
+    Angle finalAngle() const { return _end_angle; }
+    /// Check whether the interval goes in the direction of increasing angles.
+    bool sweep() const { return _sweep; }
+    /// Check whether the interval contains only a single angle.
+    bool isDegenerate() const {
+        return _start_angle == _end_angle && !_full;
+    }
+    /// Check whether the interval contains all angles.
+    bool isFull() const {
+        return _start_angle == _end_angle && _full;
+    }
+
+    /** @brief Set the initial angle.
+     * @param a Angle to set
+     * @param prefer_full Whether to set a full angular interval when
+     * the initial angle is set to the final angle */
+    void setInitial(Angle a, bool prefer_full = false) {
+        _start_angle = a;
+        _full = prefer_full && a == _end_angle;
+    }
+
+    /** @brief Set the final angle.
+     * @param a Angle to set
+     * @param prefer_full Whether to set a full angular interval when
+     * the initial angle is set to the final angle */
+    void setFinal(Angle a, bool prefer_full = false) {
+        _end_angle = a;
+        _full = prefer_full && a == _start_angle;
+    }
+    /** @brief Set both angles at once.
+     * The direction (sweep flag) is left unchanged.
+     * @param s Initial angle
+     * @param e Final angle
+     * @param prefer_full Whether to set a full interval when the passed
+     * initial and final angle are the same */
+    void setAngles(Angle s, Angle e, bool prefer_full = false) {
+        _start_angle = s;
+        _end_angle = e;
+        _full = prefer_full && s == e;
+    }
+    /// Set whether the interval goes in the direction of increasing angles.
+    void setSweep(bool s) { _sweep = s; }
+
+    /// Reverse the direction of the interval while keeping contained values the same.
+    void reverse() {
+        using std::swap;
+        swap(_start_angle, _end_angle);
+        _sweep = !_sweep;
+    }
+    /// Get a new interval with reversed direction.
+    AngleInterval reversed() const {
+        AngleInterval result(*this);
+        result.reverse();
+        return result;
+    }
+
+    /// Get an angle corresponding to the specified time value.
+    Angle angleAt(Coord t) const {
+        Coord span = extent();
+        Angle ret = _start_angle.radians0() + span * (_sweep ? t : -t);
+        return ret;
+    }
+    Angle operator()(Coord t) const { return angleAt(t); }
+
+    /** @brief Compute a time value that would evaluate to the given angle.
+     * If the start and end angle are exactly the same, NaN will be returned.
+     * Negative values will be returned for angles between the initial angle
+     * and the angle exactly opposite the midpoint of the interval. */
+    Coord timeAtAngle(Angle a) const {
+        if (_full) {
+            Angle ta = _sweep ? a - _start_angle : _start_angle - a;
+            return ta.radians0() / (2*M_PI);
+        }
+        Coord ex = extent();
+        Coord outex = 2*M_PI - ex;
+        if (_sweep) {
+            Angle midout = _start_angle - outex / 2;
+            Angle acmp = a - midout, scmp = _start_angle - midout;
+            if (acmp.radians0() >= scmp.radians0()) {
+                return (a - _start_angle).radians0() / ex;
+            } else {
+                return -(_start_angle - a).radians0() / ex;
+            }
+        } else {
+            Angle midout = _start_angle + outex / 2;
+            Angle acmp = a - midout, scmp = _start_angle - midout;
+            if (acmp.radians0() <= scmp.radians0()) {
+                return (_start_angle - a).radians0() / ex;
+            } else {
+                return -(a - _start_angle).radians0() / ex;
+            }
+        }
+    }
+
+    /// Check whether the interval includes the given angle.
+    bool contains(Angle a) const {
+        if (_full) return true;
+        Coord s = _start_angle.radians0();
+        Coord e = _end_angle.radians0();
+        Coord x = a.radians0();
+        if (_sweep) {
+            if (s < e) return x >= s && x <= e;
+            return x >= s || x <= e;
+        } else {
+            if (s > e) return x <= s && x >= e;
+            return x <= s || x >= e;
+        }
+    }
+    /** @brief Extent of the angle interval.
+     * Equivalent to the absolute value of the sweep angle.
+     * @return Extent in range \f$[0, 2\pi)\f$. */
+    Coord extent() const {
+        if (_full) return 2*M_PI;
+        return _sweep
+            ? (_end_angle - _start_angle).radians0()
+            : (_start_angle - _end_angle).radians0();
+    }
+    /** @brief Get the sweep angle of the interval.
+     * This is the value you need to add to the initial angle to get the final angle.
+     * It is positive when sweep is true. Denoted as \f$\Delta\theta\f$ in the SVG
+     * elliptical arc implementation notes. */
+    Coord sweepAngle() const {
+        if (_full) return _sweep ? 2*M_PI : -2*M_PI;
+        Coord sa = _end_angle.radians0() - _start_angle.radians0();
+        if (_sweep && sa < 0) sa += 2*M_PI;
+        if (!_sweep && sa > 0) sa -= 2*M_PI;
+        return sa;
+    }
+
+    /// Check another interval for equality.
+    bool operator==(AngleInterval const &other) const {
+        if (_start_angle != other._start_angle) return false;
+        if (_end_angle != other._end_angle) return false;
+        if (_sweep != other._sweep) return false;
+        if (_full != other._full) return false;
+        return true;
+    }
+
+    static AngleInterval create_full(Angle start, bool sweep = true) {
+        AngleInterval result;
+        result._start_angle = result._end_angle = start;
+        result._sweep = sweep;
+        result._full = true;
+        return result;
+    }
+
+private:
+    Angle _start_angle;
+    Angle _end_angle;
+    bool _sweep;
+    bool _full;
+};
+
+/** @brief Given an angle in degrees, return radians
+ * @relates Angle */
+inline Coord rad_from_deg(Coord deg) { return deg*M_PI/180.0;}
+/** @brief Given an angle in radians, return degrees
+ * @relates Angle */
+inline Coord deg_from_rad(Coord rad) { return rad*180.0/M_PI;}
+
+} // end namespace Geom
+
+namespace std {
+template <> class iterator_traits<Geom::Angle> {};
+}
+
+#endif // LIB2GEOM_SEEN_ANGLE_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/basic-intersection.h b/include/2geom/basic-intersection.h
new file mode 100644
index 0000000..2d0c00d
--- /dev/null
+++ b/include/2geom/basic-intersection.h
@@ -0,0 +1,151 @@
+/** @file
+ * @brief Basic intersection routines
+ *//*
+ * Authors:
+ *   Nathan Hurst <njh@njhurst.com>
+ *   Marco Cecchetti <mrcekets at gmail.com>
+ *   Jean-François Barraud <jf.barraud@gmail.com>
+ * 
+ * Copyright 2008-2009 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_BASIC_INTERSECTION_H
+#define LIB2GEOM_SEEN_BASIC_INTERSECTION_H
+
+#include <2geom/point.h>
+#include <2geom/bezier.h>
+#include <2geom/sbasis.h>
+#include <2geom/d2.h>
+
+#include <vector>
+#include <utility>
+
+#define USE_RECURSIVE_INTERSECTOR 0
+
+
+namespace Geom {
+
+void find_intersections(std::vector<std::pair<double, double> > &xs,
+                        D2<Bezier> const &A,
+                        D2<Bezier> const &B,
+                        double precision = EPSILON);
+
+void find_intersections(std::vector<std::pair<double, double> > &xs,
+                        D2<SBasis> const &A,
+                        D2<SBasis> const &B,
+                        double precision = EPSILON);
+
+void find_intersections(std::vector< std::pair<double, double> > &xs,
+                        std::vector<Point> const &A,
+                        std::vector<Point> const &B,
+                        double precision = EPSILON);
+
+void find_self_intersections(std::vector<std::pair<double, double> > &xs,
+                             D2<SBasis> const &A,
+                             double precision = EPSILON);
+
+void find_self_intersections(std::vector<std::pair<double, double> > &xs,
+                             D2<Bezier> const &A,
+                             double precision = EPSILON);
+
+/*
+ * find_intersection
+ *
+ *  input: A, B       - set of control points of two Bezier curve
+ *  input: precision  - required precision of computation
+ *  output: xs        - set of pairs of parameter values
+ *                      at which crossing happens
+ *
+ *  This routine is based on the Bezier Clipping Algorithm,
+ *  see: Sederberg, Nishita, 1990 - Curve intersection using Bezier clipping
+ */
+void find_intersections_bezier_clipping (std::vector< std::pair<double, double> > & xs,
+                         std::vector<Point> const& A,
+                         std::vector<Point> const& B,
+                         double precision = EPSILON);
+//#endif
+
+void subdivide(D2<Bezier> const &a,
+               D2<Bezier> const &b,
+               std::vector< std::pair<double, double> > const &xs,
+               std::vector< D2<Bezier> > &av,
+               std::vector< D2<Bezier> > &bv);
+
+/*
+ * find_collinear_normal
+ *
+ *  input: A, B       - set of control points of two Bezier curve
+ *  input: precision  - required precision of computation
+ *  output: xs        - set of pairs of parameter values
+ *                      at which there are collinear normals
+ *
+ *  This routine is based on the Bezier Clipping Algorithm,
+ *  see: Sederberg, Nishita, 1990 - Curve intersection using Bezier clipping
+ */
+void find_collinear_normal (std::vector< std::pair<double, double> >& xs,
+                            std::vector<Point> const& A,
+                            std::vector<Point> const& B,
+                            double precision = EPSILON);
+
+void polish_intersections(std::vector<std::pair<double, double> > &xs, 
+                          D2<SBasis> const &A,
+                          D2<SBasis> const &B);
+
+
+/**
+ * Compute the Hausdorf distance from A to B only.
+ */
+double hausdorfl(D2<SBasis>& A, D2<SBasis> const &B,
+                 double m_precision,
+                 double *a_t=NULL, double *b_t=NULL);
+
+/** 
+ * Compute the symmetric Hausdorf distance.
+ */
+double hausdorf(D2<SBasis> &A, D2<SBasis> const &B,
+                double m_precision,
+                double *a_t=NULL, double *b_t=NULL);
+
+/**
+ * Check if two line segments intersect. If they are collinear, the result is undefined.
+ * @return True if line segments AB and CD intersect
+ */
+bool non_collinear_segments_intersect(const Point &A, const Point &B, const Point &C, const Point &D);
+}
+
+#endif // !LIB2GEOM_SEEN_BASIC_INTERSECTION_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/bezier-curve.h b/include/2geom/bezier-curve.h
new file mode 100644
index 0000000..754c9cc
--- /dev/null
+++ b/include/2geom/bezier-curve.h
@@ -0,0 +1,366 @@
+/**
+ * \file
+ * \brief Bezier curve
+ *//*
+ * Authors:
+ *   MenTaLguY <mental@rydia.net>
+ *   Marco Cecchetti <mrcekets at gmail.com>
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * 
+ * Copyright 2007-2011 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_BEZIER_CURVE_H
+#define LIB2GEOM_SEEN_BEZIER_CURVE_H
+
+#include <2geom/curve.h>
+#include <2geom/sbasis-curve.h> // for non-native winding method
+#include <2geom/bezier.h>
+#include <2geom/transforms.h>
+
+namespace Geom 
+{
+
+class BezierCurve : public Curve {
+protected:
+    D2<Bezier> inner;
+    BezierCurve() {}
+    BezierCurve(Bezier const &x, Bezier const &y) : inner(x, y) {}
+    BezierCurve(std::vector<Point> const &pts);
+
+public:
+    explicit BezierCurve(D2<Bezier> const &b) : inner(b) {}
+
+    /// @name Access and modify control points
+    /// @{
+    /** @brief Get the order of the Bezier curve.
+     * A Bezier curve has order() + 1 control points. */
+    unsigned order() const { return inner[X].order(); }
+    /** @brief Get the number of control points. */
+    unsigned size() const { return inner[X].order() + 1; }
+    /** @brief Access control points of the curve.
+     * @param ix The (zero-based) index of the control point. Note that the caller is responsible for checking that this value is <= order().
+     * @return The control point. No-reference return, use setPoint() to modify control points. */
+    Point controlPoint(unsigned ix) const { return Point(inner[X][ix], inner[Y][ix]); }
+    Point operator[](unsigned ix) const { return Point(inner[X][ix], inner[Y][ix]); }
+    /** @brief Get the control points.
+     * @return Vector with order() + 1 control points. */
+    std::vector<Point> controlPoints() const { return bezier_points(inner); }
+    D2<Bezier> const &fragment() const { return inner; }
+
+    /** @brief Modify a control point.
+     * @param ix The zero-based index of the point to modify. Note that the caller is responsible for checking that this value is <= order().
+     * @param v The new value of the point */
+    void setPoint(unsigned ix, Point const &v) {
+        inner[X][ix] = v[X];
+        inner[Y][ix] = v[Y];
+    }
+    /** @brief Set new control points.
+     * @param ps Vector which must contain order() + 1 points.
+     *           Note that the caller is responsible for checking the size of this vector.
+     * @throws LogicalError Thrown when the size of the vector does not match the order. */
+    virtual void setPoints(std::vector<Point> const &ps) {
+        // must be virtual, because HLineSegment will need to redefine it
+        if (ps.size() != order() + 1)
+            THROW_LOGICALERROR("BezierCurve::setPoints: incorrect number of points in vector");
+        for(unsigned i = 0; i <= order(); i++) {
+            setPoint(i, ps[i]);
+        }
+    }
+    /// @}
+
+    /// @name Construct a Bezier curve with runtime-determined order.
+    /// @{
+    /** @brief Construct a curve from a vector of control points.
+     * This will construct the appropriate specialization of BezierCurve (i.e. LineSegment,
+     * QuadraticBezier or Cubic Bezier) if the number of control points in the passed vector
+     * does not exceed 4. */
+    static BezierCurve *create(std::vector<Point> const &pts);
+    /// @}
+
+    // implementation of virtual methods goes here
+    Point initialPoint() const override { return inner.at0(); }
+    Point finalPoint() const override { return inner.at1(); }
+    bool isDegenerate() const override;
+    bool isLineSegment() const override;
+    void setInitial(Point const &v) override { setPoint(0, v); }
+    void setFinal(Point const &v) override { setPoint(order(), v); }
+    Rect boundsFast() const override { return *bounds_fast(inner); }
+    Rect boundsExact() const override { return *bounds_exact(inner); }
+    void expandToTransformed(Rect &bbox, Affine const &transform) const override;
+    OptRect boundsLocal(OptInterval const &i, unsigned deg) const override {
+        if (!i) return OptRect();
+        if(i->min() == 0 && i->max() == 1) return boundsFast();
+        if(deg == 0) return bounds_local(inner, i);
+        // TODO: UUUUUUGGGLLY
+        if(deg == 1 && order() > 1) return OptRect(bounds_local(Geom::derivative(inner[X]), i),
+                                                   bounds_local(Geom::derivative(inner[Y]), i));
+        return OptRect();
+    }
+    Curve *duplicate() const override {
+        return new BezierCurve(*this);
+    }
+
+    Curve *portion(Coord f, Coord t) const override;
+
+    Curve *reverse() const override {
+        return new BezierCurve(Geom::reverse(inner));
+    }
+
+    using Curve::operator*=;
+    void operator*=(Translate const &tr) override {
+        for (unsigned i = 0; i < size(); ++i) {
+            inner[X][i] += tr[X];
+            inner[Y][i] += tr[Y];
+        }
+    }
+    void operator*=(Scale const &s) override {
+        for (unsigned i = 0; i < size(); ++i) {
+            inner[X][i] *= s[X];
+            inner[Y][i] *= s[Y];
+        }
+    }
+    void operator*=(Affine const &m) override {
+        for (unsigned i = 0; i < size(); ++i) {
+            setPoint(i, controlPoint(i) * m);
+        }
+    }
+
+    Curve *derivative() const override {
+        return new BezierCurve(Geom::derivative(inner[X]), Geom::derivative(inner[Y]));
+    }
+    int degreesOfFreedom() const override {
+        return 2 * (order() + 1);
+    }
+    std::vector<Coord> roots(Coord v, Dim2 d) const override {
+        return (inner[d] - v).roots();
+    }
+    Coord nearestTime(Point const &p, Coord from = 0, Coord to = 1) const override;
+    Coord length(Coord tolerance) const override;
+    std::vector<CurveIntersection> intersect(Curve const &other, Coord eps = EPSILON) const override;
+    Point pointAt(Coord t) const override { return inner.pointAt(t); }
+    std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const override {
+        return inner.valueAndDerivatives(t, n);
+    }
+    Coord valueAt(Coord t, Dim2 d) const override { return inner[d].valueAt(t); }
+    D2<SBasis> toSBasis() const override {return inner.toSBasis(); }
+    bool isNear(Curve const &c, Coord precision) const override;
+    bool operator==(Curve const &c) const override;
+    void feed(PathSink &sink, bool) const override;
+};
+
+template <unsigned degree>
+class BezierCurveN
+    : public BezierCurve
+{
+    template <unsigned required_degree>
+    static void assert_degree(BezierCurveN<required_degree> const *) {}
+
+public:
+    /// @name Construct Bezier curves
+    /// @{
+    /** @brief Construct a Bezier curve of the specified order with all points zero. */
+    BezierCurveN() {
+        inner = D2<Bezier>(Bezier(Bezier::Order(degree)), Bezier(Bezier::Order(degree)));
+    }
+
+    /** @brief Construct from 2D Bezier polynomial. */
+    explicit BezierCurveN(D2<Bezier > const &x) {
+        inner = x;
+    }
+
+    /** @brief Construct from two 1D Bezier polynomials of the same order. */
+    BezierCurveN(Bezier x, Bezier y) {
+        inner = D2<Bezier > (x,y);
+    }
+
+    /** @brief Construct a Bezier curve from a vector of its control points. */
+    BezierCurveN(std::vector<Point> const &points) {
+        unsigned ord = points.size() - 1;
+        if (ord != degree) THROW_LOGICALERROR("BezierCurve<degree> does not match number of points");
+        for (unsigned d = 0; d < 2; ++d) {
+            inner[d] = Bezier(Bezier::Order(ord));
+            for(unsigned i = 0; i <= ord; i++)
+                inner[d][i] = points[i][d];
+        }
+    }
+
+    /** @brief Construct a linear segment from its endpoints. */
+    BezierCurveN(Point c0, Point c1) {
+      assert_degree<1>(this);
+      for(unsigned d = 0; d < 2; d++)
+          inner[d] = Bezier(c0[d], c1[d]);
+    }
+
+    /** @brief Construct a quadratic Bezier curve from its control points. */
+    BezierCurveN(Point c0, Point c1, Point c2) {
+      assert_degree<2>(this);
+      for(unsigned d = 0; d < 2; d++)
+          inner[d] = Bezier(c0[d], c1[d], c2[d]);
+    }
+
+    /** @brief Construct a cubic Bezier curve from its control points. */
+    BezierCurveN(Point c0, Point c1, Point c2, Point c3) {
+      assert_degree<3>(this);
+      for(unsigned d = 0; d < 2; d++)
+          inner[d] = Bezier(c0[d], c1[d], c2[d], c3[d]);
+    }
+
+    // default copy
+    // default assign
+
+    /// @}
+
+    /** @brief Divide a Bezier curve into two curves
+     * @param t Time value
+     * @return Pair of Bezier curves \f$(\mathbf{D}, \mathbf{E})\f$ such that
+     *         \f$\mathbf{D}[ [0,1] ] = \mathbf{C}[ [0,t] ]\f$ and
+     *         \f$\mathbf{E}[ [0,1] ] = \mathbf{C}[ [t,1] ]\f$ */
+    std::pair<BezierCurveN, BezierCurveN> subdivide(Coord t) const {
+        std::pair<Bezier, Bezier> sx = inner[X].subdivide(t), sy = inner[Y].subdivide(t);
+        return std::make_pair(
+                   BezierCurveN(sx.first, sy.first),
+                   BezierCurveN(sx.second, sy.second));
+    }
+
+    bool isDegenerate() const override {
+        return BezierCurve::isDegenerate();
+    }
+
+    bool isLineSegment() const override {
+        if constexpr (degree == 1) {
+            return true;
+        } else {
+            return BezierCurve::isLineSegment();
+        }
+    }
+
+    Curve *duplicate() const override {
+        return new BezierCurveN(*this);
+    }
+    Curve *portion(Coord f, Coord t) const override {
+        if (degree == 1) {
+            return new BezierCurveN<1>(pointAt(f), pointAt(t));
+        } else {
+            return new BezierCurveN(Geom::portion(inner, f, t));
+        }
+    }
+    Curve *reverse() const override {
+        if (degree == 1) {
+            return new BezierCurveN<1>(finalPoint(), initialPoint()); 
+        } else {
+            return new BezierCurveN(Geom::reverse(inner));
+        }
+    }
+    Curve *derivative() const override;
+
+    Coord nearestTime(Point const &p, Coord from = 0, Coord to = 1) const override {
+        return BezierCurve::nearestTime(p, from, to);
+    }
+    std::vector<CurveIntersection> intersect(Curve const &other, Coord eps = EPSILON) const override {
+        // call super. this is implemented only to allow specializations
+        return BezierCurve::intersect(other, eps);
+    }
+    int winding(Point const &p) const override {
+        return Curve::winding(p);
+    }
+    void feed(PathSink &sink, bool moveto_initial) const override {
+        // call super. this is implemented only to allow specializations
+        BezierCurve::feed(sink, moveto_initial);
+    }
+    void expandToTransformed(Rect &bbox, Affine const &transform) const override {
+        // call super. this is implemented only to allow specializations
+        BezierCurve::expandToTransformed(bbox, transform);
+    }
+};
+
+// BezierCurveN<0> is meaningless; specialize it out
+template<> class BezierCurveN<0> : public BezierCurveN<1> { private: BezierCurveN();};
+
+/** @brief Line segment.
+ * Line segments are Bezier curves of order 1. They have only two control points,
+ * the starting point and the ending point.
+ * @ingroup Curves */
+typedef BezierCurveN<1> LineSegment;
+
+/** @brief Quadratic (order 2) Bezier curve.
+ * @ingroup Curves */
+typedef BezierCurveN<2> QuadraticBezier;
+
+/** @brief Cubic (order 3) Bezier curve.
+ * @ingroup Curves */
+typedef BezierCurveN<3> CubicBezier;
+
+template <unsigned degree>
+inline
+Curve *BezierCurveN<degree>::derivative() const {
+    return new BezierCurveN<degree-1>(Geom::derivative(inner[X]), Geom::derivative(inner[Y]));
+}
+
+// optimized specializations
+template <> inline bool BezierCurveN<1>::isDegenerate() const {
+    return inner[X][0] == inner[X][1] && inner[Y][0] == inner[Y][1];
+}
+template <> inline bool BezierCurveN<1>::isLineSegment() const { return true; }
+template <> Curve *BezierCurveN<1>::derivative() const;
+template <> Coord BezierCurveN<1>::nearestTime(Point const &, Coord, Coord) const;
+template <> std::vector<CurveIntersection> BezierCurveN<1>::intersect(Curve const &, Coord) const;
+template <> std::vector<CurveIntersection> BezierCurveN<2>::intersect(Curve const &, Coord) const;
+template <> std::vector<CurveIntersection> BezierCurveN<3>::intersect(Curve const &, Coord) const;
+template <> int BezierCurveN<1>::winding(Point const &) const;
+template <> void BezierCurveN<1>::feed(PathSink &sink, bool moveto_initial) const;
+template <> void BezierCurveN<2>::feed(PathSink &sink, bool moveto_initial) const;
+template <> void BezierCurveN<3>::feed(PathSink &sink, bool moveto_initial) const;
+template <> void BezierCurveN<1>::expandToTransformed(Rect &bbox, Affine const &transform) const;
+template <> void BezierCurveN<2>::expandToTransformed(Rect &bbox, Affine const &transform) const;
+template <> void BezierCurveN<3>::expandToTransformed(Rect &bbox, Affine const &transform) const;
+
+inline Point middle_point(LineSegment const& _segment) {
+    return ( _segment.initialPoint() + _segment.finalPoint() ) / 2;
+}
+
+inline Coord length(LineSegment const& seg) {
+    return distance(seg.initialPoint(), seg.finalPoint());
+}
+
+Coord bezier_length(std::vector<Point> const &points, Coord tolerance = 0.01);
+Coord bezier_length(Point p0, Point p1, Point p2, Coord tolerance = 0.01);
+Coord bezier_length(Point p0, Point p1, Point p2, Point p3, Coord tolerance = 0.01);
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_BEZIER_CURVE_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/bezier-to-sbasis.h b/include/2geom/bezier-to-sbasis.h
new file mode 100644
index 0000000..73c55d9
--- /dev/null
+++ b/include/2geom/bezier-to-sbasis.h
@@ -0,0 +1,94 @@
+/**
+ * \file
+ * \brief Conversion between Bezier control points and SBasis curves
+ *//*
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_BEZIER_TO_SBASIS_H
+#define LIB2GEOM_SEEN_BEZIER_TO_SBASIS_H
+
+#include <2geom/coord.h>
+#include <2geom/point.h>
+#include <2geom/d2.h>
+#include <2geom/sbasis-to-bezier.h>
+
+namespace Geom
+{
+
+#if 0
+inline SBasis bezier_to_sbasis(Coord const *handles, unsigned order) {
+    if(order == 0)
+        return Linear(handles[0]);
+    else if(order == 1)
+        return Linear(handles[0], handles[1]);
+    else
+        return multiply(Linear(1, 0), bezier_to_sbasis(handles, order-1)) +
+            multiply(Linear(0, 1), bezier_to_sbasis(handles+1, order-1));
+}
+
+
+template <typename T>
+inline D2<SBasis> handles_to_sbasis(T const &handles, unsigned order)
+{
+    double v[2][order+1];
+    for(unsigned i = 0; i <= order; i++)
+        for(unsigned j = 0; j < 2; j++)
+             v[j][i] = handles[i][j];
+    return D2<SBasis>(bezier_to_sbasis(v[0], order),
+                      bezier_to_sbasis(v[1], order));
+}
+#endif
+
+
+template <typename T>
+inline
+D2<SBasis> handles_to_sbasis(T const& handles, unsigned order)
+{
+    D2<SBasis> sbc;
+    size_t sz = order + 1;
+    std::vector<Point> v;
+    v.reserve(sz);
+    for (size_t i = 0; i < sz; ++i)
+        v.push_back(handles[i]);
+    bezier_to_sbasis(sbc, v);
+    return sbc;
+}
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_BEZIER_TO_SBASIS_H
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/bezier-utils.h b/include/2geom/bezier-utils.h
new file mode 100644
index 0000000..3e56e6e
--- /dev/null
+++ b/include/2geom/bezier-utils.h
@@ -0,0 +1,99 @@
+/**
+ * \file
+ * \brief Bezier fitting algorithms
+ *//*
+ * An Algorithm for Automatically Fitting Digitized Curves
+ * by Philip J. Schneider
+ * from "Graphics Gems", Academic Press, 1990
+ *
+ * Authors:
+ *   Philip J. Schneider
+ *   Lauris Kaplinski <lauris@ximian.com>
+ *
+ * Copyright (C) 1990 Philip J. Schneider
+ * Copyright (C) 2001 Lauris Kaplinski and Ximian, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_BEZIER_UTILS_H
+#define LIB2GEOM_SEEN_BEZIER_UTILS_H
+
+#include <2geom/point.h>
+
+namespace Geom {
+
+Point bezier_pt(unsigned degree, Point const V[], double t);
+
+int bezier_fit_cubic(Point bezier[], Point const data[], int len, double error);
+
+int bezier_fit_cubic_r(Point bezier[], Point const data[], int len, double error,
+                           unsigned max_beziers);
+
+int bezier_fit_cubic_full(Point bezier[], int split_points[], Point const data[], int len,
+                              Point const &tHat1, Point const &tHat2,
+                              double error, unsigned max_beziers);
+
+Point darray_left_tangent(Point const d[], unsigned const len);
+Point darray_left_tangent(Point const d[], unsigned const len, double const tolerance_sq);
+Point darray_right_tangent(Point const d[], unsigned const length, double const tolerance_sq);
+
+template <typename iterator>
+static void
+cubic_bezier_poly_coeff(iterator b, Point *pc) {
+	double c[10] = {1, 
+			-3, 3, 
+			3, -6, 3,
+			-1, 3, -3, 1};
+
+	int cp = 0;
+
+	for(int i = 0; i < 4; i++) {
+		pc[i] = Point(0,0);
+		++b;
+	}
+	for(int i = 0; i < 4; i++) {
+		--b;
+		for(int j = 0; j <= i; j++) {
+			pc[3 - j] += c[cp]*(*b);
+			cp++;
+		}
+	}
+}
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_BEZIER_UTILS_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/bezier.h b/include/2geom/bezier.h
new file mode 100644
index 0000000..d65b3bd
--- /dev/null
+++ b/include/2geom/bezier.h
@@ -0,0 +1,394 @@
+/**
+ * @file
+ * @brief Bernstein-Bezier polynomial
+ *//*
+ * Authors:
+ *   MenTaLguY <mental@rydia.net>
+ *   Michael Sloan <mgsloan@gmail.com>
+ *   Nathan Hurst <njh@njhurst.com>
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright 2007-2015 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_BEZIER_H
+#define LIB2GEOM_SEEN_BEZIER_H
+
+#include <algorithm>
+#include <valarray>
+#include <2geom/coord.h>
+#include <2geom/d2.h>
+#include <2geom/math-utils.h>
+
+namespace Geom {
+
+/** @brief Compute the value of a Bernstein-Bezier polynomial.
+ * This method uses a Horner-like fast evaluation scheme.
+ * @param t Time value
+ * @param c_ Pointer to coefficients
+ * @param n Degree of the polynomial (number of coefficients minus one) */
+template <typename T>
+inline T bernstein_value_at(double t, T const *c_, unsigned n) {
+    double u = 1.0 - t;
+    double bc = 1;
+    double tn = 1;
+    T tmp = c_[0]*u;
+    for(unsigned i=1; i<n; i++){
+        tn = tn*t;
+        bc = bc*(n-i+1)/i;
+        tmp = (tmp + tn*bc*c_[i])*u;
+    }
+    return (tmp + tn*t*c_[n]);
+}
+
+/** @brief Perform Casteljau subdivision of a Bezier polynomial.
+ * Given an array of coefficients and a time value, computes two new Bernstein-Bezier basis
+ * polynomials corresponding to the \f$[0, t]\f$ and \f$[t, 1]\f$ intervals of the original one.
+ * @param t Time value
+ * @param v Array of input coordinates
+ * @param left Output polynomial corresponding to \f$[0, t]\f$
+ * @param right Output polynomial corresponding to \f$[t, 1]\f$
+ * @param order Order of the input polynomial, equal to one less the number of coefficients
+ * @return Value of the polynomial at @a t */
+template <typename T>
+inline T casteljau_subdivision(double t, T const *v, T *left, T *right, unsigned order) {
+    // The Horner-like scheme gives very slightly different results, but we need
+    // the result of subdivision to match exactly with Bezier's valueAt function.
+    T val = bernstein_value_at(t, v, order);
+
+    if (!left && !right) {
+        return val;
+    }
+
+    if (!right) {
+        if (left != v) {
+            std::copy(v, v + order + 1, left);
+        }
+        for (std::size_t i = order; i > 0; --i) {
+            for (std::size_t j = i; j <= order; ++j) {
+                left[j] = lerp(t, left[j-1], left[j]);
+            }
+        }
+        left[order] = val;
+        return left[order];
+    }
+
+    if (right != v) {
+        std::copy(v, v + order + 1, right);
+    }
+    for (std::size_t i = 1; i <= order; ++i) {
+        if (left) {
+            left[i-1] = right[0];
+        }
+        for (std::size_t j = i; j > 0; --j) {
+            right[j-1] = lerp(t, right[j-1], right[j]);
+        }
+    }
+    right[0] = val;
+    if (left) {
+        left[order] = right[0];
+    }
+    return right[0];
+}
+
+/**
+ * @brief Polynomial in Bernstein-Bezier basis
+ * @ingroup Fragments
+ */
+class Bezier
+    : boost::arithmetic< Bezier, double
+    , boost::additive< Bezier
+      > >
+{
+private:
+    std::valarray<Coord> c_;
+
+    friend Bezier portion(const Bezier & a, Coord from, Coord to);
+    friend OptInterval bounds_fast(Bezier const & b);
+    friend Bezier derivative(const Bezier & a);
+    friend class Bernstein;
+
+    void
+    find_bezier_roots(std::vector<double> & solutions,
+                      double l, double r) const;
+
+protected:
+    Bezier(Coord const c[], unsigned ord)
+        : c_(c, ord+1)
+    {}
+
+public:
+    unsigned order() const { return c_.size()-1;}
+    unsigned degree() const { return order(); }
+    unsigned size() const { return c_.size();}
+
+    Bezier() {}
+    Bezier(const Bezier& b) :c_(b.c_) {}
+    Bezier &operator=(Bezier const &other) {
+        if ( c_.size() != other.c_.size() ) {
+            c_.resize(other.c_.size());
+        }
+        c_ = other.c_;
+        return *this;
+    }
+
+    bool operator==(Bezier const &other) const
+    {
+        if (degree() != other.degree()) {
+            return false;
+        }
+
+        for (size_t i = 0; i < c_.size(); i++) {
+            if (c_[i] != other.c_[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    bool operator!=(Bezier const &other) const
+    {
+        return !(*this == other);
+    }
+
+    struct Order {
+        unsigned order;
+        explicit Order(Bezier const &b) : order(b.order()) {}
+        explicit Order(unsigned o) : order(o) {}
+        operator unsigned() const { return order; }
+    };
+
+    //Construct an arbitrary order bezier
+    Bezier(Order ord) : c_(0., ord.order+1) {
+        assert(ord.order ==  order());
+    }
+
+    /// @name Construct Bezier polynomials from their control points
+    /// @{
+    explicit Bezier(Coord c0) : c_(0., 1) {
+        c_[0] = c0;
+    }
+    Bezier(Coord c0, Coord c1) : c_(0., 2) {
+        c_[0] = c0; c_[1] = c1;
+    }
+    Bezier(Coord c0, Coord c1, Coord c2) : c_(0., 3) {
+        c_[0] = c0; c_[1] = c1; c_[2] = c2;
+    }
+    Bezier(Coord c0, Coord c1, Coord c2, Coord c3) : c_(0., 4) {
+        c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3;
+    }
+    Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4) : c_(0., 5) {
+        c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4;
+    }
+    Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4,
+           Coord c5) : c_(0., 6) {
+        c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4;
+        c_[5] = c5;
+    }
+    Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4,
+           Coord c5, Coord c6) : c_(0., 7) {
+        c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4;
+        c_[5] = c5; c_[6] = c6;
+    }
+    Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4,
+           Coord c5, Coord c6, Coord c7) : c_(0., 8) {
+        c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4;
+        c_[5] = c5; c_[6] = c6; c_[7] = c7;
+    }
+    Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4,
+           Coord c5, Coord c6, Coord c7, Coord c8) : c_(0., 9) {
+        c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4;
+        c_[5] = c5; c_[6] = c6; c_[7] = c7; c_[8] = c8;
+    }
+    Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4,
+           Coord c5, Coord c6, Coord c7, Coord c8, Coord c9) : c_(0., 10) {
+        c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4;
+        c_[5] = c5; c_[6] = c6; c_[7] = c7; c_[8] = c8; c_[9] = c9;
+    }
+
+    template <typename Iter>
+    Bezier(Iter first, Iter last) {
+        c_.resize(std::distance(first, last));
+        for (std::size_t i = 0; first != last; ++first, ++i) {
+            c_[i] = *first;
+        }
+    }
+    Bezier(std::vector<Coord> const &vec)
+        : c_(&vec[0], vec.size())
+    {}
+    /// @}
+
+    void resize (unsigned int n, Coord v = 0) {
+        c_.resize (n, v);
+    }
+    void clear() {
+        c_.resize(0);
+    }
+
+    //IMPL: FragmentConcept
+    typedef Coord output_type;
+    bool isZero(double eps=EPSILON) const {
+        for(unsigned i = 0; i <= order(); i++) {
+            if( ! are_near(c_[i], 0., eps) ) return false;
+        }
+        return true;
+    }
+    bool isConstant(double eps=EPSILON) const {
+        for(unsigned i = 1; i <= order(); i++) {
+            if( ! are_near(c_[i], c_[0], eps) ) return false;
+        }
+        return true;
+    }
+    bool isFinite() const {
+        for(unsigned i = 0; i <= order(); i++) {
+            if(!std::isfinite(c_[i])) return false;
+        }
+        return true;
+    }
+    Coord at0() const { return c_[0]; }
+    Coord &at0() { return c_[0]; }
+    Coord at1() const { return c_[order()]; }
+    Coord &at1() { return c_[order()]; }
+
+    Coord valueAt(double t) const {
+        return bernstein_value_at(t, &c_[0], order());
+    }
+    Coord operator()(double t) const { return valueAt(t); }
+
+    SBasis toSBasis() const;
+
+    Coord &operator[](unsigned ix) { return c_[ix]; }
+    Coord const &operator[](unsigned ix) const { return const_cast<std::valarray<Coord>&>(c_)[ix]; }
+
+    void setCoeff(unsigned ix, double val) { c_[ix] = val; }
+
+    // The size of the returned vector equals n_derivs+1.
+    std::vector<Coord> valueAndDerivatives(Coord t, unsigned n_derivs) const;
+
+    void subdivide(Coord t, Bezier *left, Bezier *right) const;
+    std::pair<Bezier, Bezier> subdivide(Coord t) const;
+
+    std::vector<Coord> roots() const;
+    std::vector<Coord> roots(Interval const &ivl) const;
+
+    Bezier forward_difference(unsigned k) const;
+    Bezier elevate_degree() const;
+    Bezier reduce_degree() const;
+    Bezier elevate_to_degree(unsigned newDegree) const;
+    Bezier deflate() const;
+
+    // basic arithmetic operators
+    Bezier &operator+=(double v) {
+        c_ += v;
+        return *this;
+    }
+    Bezier &operator-=(double v) {
+        c_ -= v;
+        return *this;
+    }
+    Bezier &operator*=(double v) {
+        c_ *= v;
+        return *this;
+    }
+    Bezier &operator/=(double v) {
+        c_ /= v;
+        return *this;
+    }
+    Bezier &operator+=(Bezier const &other);
+    Bezier &operator-=(Bezier const &other);
+
+    /// Unary minus
+    Bezier operator-() const
+    {
+        Bezier result;
+        result.c_ = -c_;
+        return result;
+    }
+};
+
+
+void bezier_to_sbasis (SBasis &sb, Bezier const &bz);
+
+Bezier operator*(Bezier const &f, Bezier const &g);
+inline Bezier multiply(Bezier const &f, Bezier const &g) {
+    Bezier result = f * g;
+    return result;
+}
+
+inline Bezier reverse(const Bezier & a) {
+    Bezier result = Bezier(Bezier::Order(a));
+    for(unsigned i = 0; i <= a.order(); i++)
+        result[i] = a[a.order() - i];
+    return result;
+}
+
+Bezier portion(const Bezier & a, double from, double to);
+
+// XXX Todo: how to handle differing orders
+inline std::vector<Point> bezier_points(const D2<Bezier > & a) {
+    std::vector<Point> result;
+    for(unsigned i = 0; i <= a[0].order(); i++) {
+        Point p;
+        for(unsigned d = 0; d < 2; d++) p[d] = a[d][i];
+        result.push_back(p);
+    }
+    return result;
+}
+
+Bezier derivative(Bezier const &a);
+Bezier integral(Bezier const &a);
+OptInterval bounds_fast(Bezier const &b);
+OptInterval bounds_exact(Bezier const &b);
+OptInterval bounds_local(Bezier const &b, OptInterval const &i);
+
+/// Expand an interval to the image of a Bézier-Bernstein polynomial, assuming it already contains the initial point x0.
+void bezier_expand_to_image(Interval &range, Coord x0, Coord x1, Coord x2);
+void bezier_expand_to_image(Interval &range, Coord x0, Coord x1, Coord x2, Coord x3);
+
+inline std::ostream &operator<< (std::ostream &os, const Bezier & b) {
+    os << "Bezier(";
+    for(unsigned i = 0; i < b.order(); i++) {
+        os << format_coord_nice(b[i]) << ", ";
+    }
+    os << format_coord_nice(b[b.order()]) << ")";
+    return os;
+}
+
+} // namespace Geom
+
+#endif // LIB2GEOM_SEEN_BEZIER_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/cairo-path-sink.h b/include/2geom/cairo-path-sink.h
new file mode 100644
index 0000000..3f7a044
--- /dev/null
+++ b/include/2geom/cairo-path-sink.h
@@ -0,0 +1,91 @@
+/**
+ * @file
+ * @brief  Path sink for Cairo contexts
+ *//*
+ * Copyright 2014 Krzysztof Kosiński
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_CAIRO_PATH_SINK_H
+#define LIB2GEOM_SEEN_CAIRO_PATH_SINK_H
+
+#ifdef HAVE_CAIRO
+
+#include <2geom/path-sink.h>
+#include <cairo.h>
+
+namespace Geom {
+
+
+/** @brief Output paths to a Cairo drawing context
+ *
+ * This class converts from 2Geom path representation to the Cairo representation.
+ * Use it to simplify visualizing the results of 2Geom operations with the Cairo library,
+ * for example:
+ * @code
+ *   CairoPathSink sink(cr);
+ *   sink.feed(pv);
+ *   cairo_stroke(cr);
+ * @endcode
+ *
+ * Currently the flush method is a no-op, but this is not guaranteed
+ * to hold forever.
+ */
+class CairoPathSink
+    : public PathSink
+{
+public:
+    CairoPathSink(cairo_t *cr);
+
+    void moveTo(Point const &p) override;
+    void lineTo(Point const &p) override;
+    void curveTo(Point const &c0, Point const &c1, Point const &p) override;
+    void quadTo(Point const &c, Point const &p) override;
+    void arcTo(Coord rx, Coord ry, Coord angle,
+               bool large_arc, bool sweep, Point const &p) override;
+    void closePath() override;
+    void flush() override;
+
+private:
+    cairo_t *_cr;
+    Point _current_point;
+};
+
+}
+
+#endif
+
+#endif // !LIB2GEOM_SEEN_CAIRO_PATH_SINK_H
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/choose.h b/include/2geom/choose.h
new file mode 100644
index 0000000..106d04f
--- /dev/null
+++ b/include/2geom/choose.h
@@ -0,0 +1,147 @@
+/**
+ * \file
+ * \brief Calculation of binomial cefficients
+ *//*
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_CHOOSE_H
+#define LIB2GEOM_SEEN_CHOOSE_H
+
+#include <vector>
+
+namespace Geom {
+
+/**
+ * @brief Given a multiple of binomial(n, k), modify it to the same multiple of binomial(n + 1, k).
+ */
+template <typename T>
+constexpr void binomial_increment_n(T &b, int n, int k)
+{
+    b = b * (n + 1) / (n + 1 - k);
+}
+
+/**
+ * @brief Given a multiple of binomial(n, k), modify it to the same multiple of binomial(n - 1, k).
+ */
+template <typename T>
+constexpr void binomial_decrement_n(T &b, int n, int k)
+{
+    b = b * (n - k) / n;
+}
+
+/**
+ * @brief Given a multiple of binomial(n, k), modify it to the same multiple of binomial(n, k + 1).
+ */
+template <typename T>
+constexpr void binomial_increment_k(T &b, int n, int k)
+{
+    b = b * (n - k) / (k + 1);
+}
+
+/**
+ * @brief Given a multiple of binomial(n, k), modify it to the same multiple of binomial(n, k - 1).
+ */
+template <typename T>
+constexpr void binomial_decrement_k(T &b, int n, int k)
+{
+    b = b * k / (n + 1 - k);
+}
+
+/**
+ * @brief Calculate the (n, k)th binomial coefficient.
+ */
+template <typename T>
+constexpr T choose(unsigned n, unsigned k)
+{
+    if (k > n) {
+        return 0;
+    }
+    T b = 1;
+    int max = std::min(k, n - k);
+    for (int i = 0; i < max; i++) {
+        binomial_increment_k(b, n, i);
+    }
+    return b;
+}
+
+/**
+ * @brief Class for calculating and accessing a row of Pascal's triangle.
+ */
+template <typename ValueType>
+class BinomialCoefficient
+{
+public:
+    using value_type = ValueType;
+    using container_type = std::vector<value_type>;
+
+    BinomialCoefficient(unsigned int _n)
+        : n(_n)
+    {
+        coefficients.reserve(n / 2 + 1);
+        coefficients.emplace_back(1);
+        value_type b = 1;
+        for (int i = 0; i < n / 2; i++) {
+            binomial_increment_k(b, n, i);
+            coefficients.emplace_back(b);
+        }
+    }
+
+    unsigned int size() const
+    {
+        return degree() + 1;
+    }
+
+    unsigned int degree() const
+    {
+        return n;
+    }
+
+    value_type operator[](unsigned int k) const
+    {
+        return coefficients[std::min(k, n - k)];
+    }
+
+private:
+    int const n;
+    container_type coefficients;
+};
+
+} // namespace Geom
+
+#endif // LIB2GEOM_SEEN_CHOOSE_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/circle.h b/include/2geom/circle.h
new file mode 100644
index 0000000..a4d5f20
--- /dev/null
+++ b/include/2geom/circle.h
@@ -0,0 +1,165 @@
+/** @file
+ * @brief Circle shape
+ *//*
+ * Authors:
+ *   Marco Cecchetti <mrcekets at gmail.com>
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright 2008-2014 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_CIRCLE_H
+#define LIB2GEOM_SEEN_CIRCLE_H
+
+#include <2geom/forward.h>
+#include <2geom/intersection.h>
+#include <2geom/point.h>
+#include <2geom/rect.h>
+#include <2geom/transforms.h>
+
+namespace Geom {
+
+class EllipticalArc;
+
+/** @brief Set of all points at a fixed distance from the center
+ * @ingroup Shapes */
+class Circle
+    : boost::equality_comparable1< Circle
+    , MultipliableNoncommutative< Circle, Translate
+    , MultipliableNoncommutative< Circle, Rotate
+    , MultipliableNoncommutative< Circle, Zoom
+      > > > >
+{
+    Point _center;
+    Coord _radius;
+
+public:
+    Circle() {}
+    Circle(Coord cx, Coord cy, Coord r)
+        : _center(cx, cy), _radius(r)
+    {}
+    Circle(Point const &center, Coord r)
+        : _center(center), _radius(r)
+    {}
+
+    Circle(Coord A, Coord B, Coord C, Coord D) {
+        setCoefficients(A, B, C, D);
+    }
+
+    // Construct the unique circle passing through three points.
+    //Circle(Point const &a, Point const &b, Point const &c);
+
+    Point center() const { return _center; }
+    Coord center(Dim2 d) const { return _center[d]; }
+    Coord radius() const { return _radius; }
+    Coord area() const { return M_PI * _radius * _radius; }
+    bool isDegenerate() const { return _radius == 0; }
+
+    void setCenter(Point const &p) { _center = p; }
+    void setRadius(Coord c) { _radius = c; }
+
+    Rect boundsFast() const;
+    Rect boundsExact() const { return boundsFast(); }
+
+    Point initialPoint() const;
+    Point finalPoint() const { return initialPoint(); }
+    Point pointAt(Coord t) const;
+    Coord valueAt(Coord t, Dim2 d) const;
+    Coord timeAt(Point const &p) const;
+    Coord nearestTime(Point const &p) const;
+
+    bool contains(Point const &p) const { return distance(p, _center) <= _radius; }
+    bool contains(Rect const &other) const;
+    bool contains(Circle const &other) const;
+
+    bool intersects(Line const &l) const;
+    bool intersects(LineSegment const &l) const;
+    bool intersects(Circle const &other) const;
+
+    std::vector<ShapeIntersection> intersect(Line const &other) const;
+    std::vector<ShapeIntersection> intersect(LineSegment const &other) const;
+    std::vector<ShapeIntersection> intersect(Circle const &other) const;
+
+    // build a circle by its implicit equation:
+    // Ax^2 + Ay^2 + Bx + Cy + D = 0
+    void setCoefficients(Coord A, Coord B, Coord C, Coord D);
+    void coefficients(Coord &A, Coord &B, Coord &C, Coord &D) const;
+    std::vector<Coord> coefficients() const;
+
+    Zoom unitCircleTransform() const;
+    Zoom inverseUnitCircleTransform() const;
+
+    EllipticalArc *
+    arc(Point const& initial, Point const& inner, Point const& final) const;
+
+    D2<SBasis> toSBasis() const;
+
+    Circle &operator*=(Translate const &t) {
+        _center *= t;
+        return *this;
+    }
+    Circle &operator*=(Rotate const &) {
+        return *this;
+    }
+    Circle &operator*=(Zoom const &z) {
+        _center *= z;
+        _radius *= z.scale();
+        return *this;
+    }
+
+    bool operator==(Circle const &other) const;
+
+    /** @brief Fit the circle to the passed points using the least squares method.
+     * @param points Samples at the perimeter of the circle */
+    void fit(std::vector<Point> const &points);
+};
+
+bool are_near(Circle const &a, Circle const &b, Coord eps=EPSILON);
+
+std::ostream &operator<<(std::ostream &out, Circle const &c);
+
+template <>
+struct ShapeTraits<Circle> {
+    typedef Coord TimeType;
+    typedef Interval IntervalType;
+    typedef Ellipse AffineClosureType;
+    typedef Intersection<> IntersectionType;
+};
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_CIRCLE_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/concepts.h b/include/2geom/concepts.h
new file mode 100644
index 0000000..de76d0f
--- /dev/null
+++ b/include/2geom/concepts.h
@@ -0,0 +1,209 @@
+/**
+ * \file
+ * \brief Template concepts used by 2Geom
+ *//*
+ * Copyright 2007 Michael Sloan <mgsloan@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_CONCEPTS_H
+#define LIB2GEOM_SEEN_CONCEPTS_H
+
+#include <2geom/sbasis.h>
+#include <2geom/interval.h>
+#include <2geom/point.h>
+#include <2geom/rect.h>
+#include <2geom/intersection.h>
+#include <vector>
+#include <boost/concept/assert.hpp>
+#include <2geom/forward.h>
+#include <2geom/transforms.h>
+
+namespace Geom {
+
+//forward decls
+template <typename T> struct ResultTraits;
+
+template <> struct ResultTraits<double> {
+  typedef OptInterval bounds_type;
+  typedef SBasis sb_type;
+};
+
+template <> struct ResultTraits<Point> {
+  typedef OptRect bounds_type;
+  typedef D2<SBasis> sb_type;
+};
+
+//A concept for one-dimensional functions defined on [0,1]
+template <typename T>
+struct FragmentConcept {
+    typedef typename T::output_type                        OutputType;
+    typedef typename ResultTraits<OutputType>::bounds_type BoundsType;
+    typedef typename ResultTraits<OutputType>::sb_type     SbType;
+    T t;
+    double d;
+    OutputType o;
+    bool b;
+    BoundsType i;
+    Interval dom;
+    std::vector<OutputType> v;
+    unsigned u;
+    SbType sb;
+    void constraints() {
+        t = T(o);
+        b = t.isZero(d);
+        b = t.isConstant(d);
+        b = t.isFinite();
+        o = t.at0();
+        o = t.at1();
+        t.at0() = o;
+        t.at1() = o;
+        o = t.valueAt(d);
+        o = t(d);
+        v = t.valueAndDerivatives(d, u-1);
+        //Is a pure derivative (ignoring others) accessor ever much faster?
+        //u = number of values returned. first val is value.
+        sb = t.toSBasis();
+        t = reverse(t);
+        i = bounds_fast(t);
+        i = bounds_exact(t);
+        i = bounds_local(t, dom);
+        /*With portion, Interval makes some sense, but instead I'm opting for
+          doubles, for the following reasons:
+          A) This way a reversed portion may be specified
+          B) Performance might be a bit better for piecewise and such
+          C) Interval version provided below
+         */
+        t = portion(t, d, d);
+    }
+};
+
+template <typename T>
+struct ShapeConcept {
+    typedef typename ShapeTraits<T>::TimeType Time;
+    typedef typename ShapeTraits<T>::IntervalType Interval;
+    typedef typename ShapeTraits<T>::AffineClosureType AffineClosure;
+    typedef typename ShapeTraits<T>::IntersectionType Isect;
+
+    T shape, other;
+    Time t;
+    Point p;
+    AffineClosure ac;
+    Affine m;
+    Translate tr;
+    Coord c;
+    bool bool_;
+    std::vector<Isect> ivec;
+
+    void constraints() {
+        p = shape.pointAt(t);
+        c = shape.valueAt(t, X);
+        ivec = shape.intersect(other);
+        t = shape.nearestTime(p);
+        shape *= tr;
+        ac = shape;
+        ac *= m;
+        bool_ = (shape == shape);
+        bool_ = (shape != other);
+        bool_ = shape.isDegenerate();
+        //bool_ = are_near(shape, other, c);
+    }
+};
+
+template <typename T>
+inline T portion(const T& t, const Interval& i) { return portion(t, i.min(), i.max()); }
+
+template <typename T>
+struct EqualityComparableConcept {
+    T a, b;
+    bool bool_;
+    void constraints() {
+        bool_ = (a == b);
+        bool_ = (a != b);
+    }
+};
+
+template <typename T>
+struct NearConcept {
+    T a, b;
+    double tol;
+    bool res;
+    void constraints() {
+        res = are_near(a, b, tol);
+    }
+};
+
+template <typename T>
+struct OffsetableConcept {
+    T t;
+    typename T::output_type d;
+    void constraints() {
+        t = t + d; t += d;
+        t = t - d; t -= d;
+    }
+};
+
+template <typename T>
+struct ScalableConcept {
+    T t;
+    typename T::output_type d;
+    void constraints() {
+        t = -t;
+        t = t * d; t *= d;
+        t = t / d; t /= d;
+    }
+};
+
+template <typename T>
+struct AddableConcept {
+    T i, j;
+    void constraints() {
+        i += j; i = i + j;
+        i -= j; i = i - j;
+    }
+};
+
+template <typename T>
+struct MultiplicableConcept {
+    T i, j;
+    void constraints() {
+        i *= j; i = i * j;
+    }
+};
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_CONCEPTS_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/conic_section_clipper.h b/include/2geom/conic_section_clipper.h
new file mode 100644
index 0000000..38bba33
--- /dev/null
+++ b/include/2geom/conic_section_clipper.h
@@ -0,0 +1,58 @@
+/** @file
+ * @brief Conic section clipping with respect to a rectangle
+ *//*
+ * Authors:
+ *      Marco Cecchetti <mrcekets at gmail>
+ *
+ * Copyright 2009  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+
+
+
+#ifndef LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_H
+#define LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_H
+
+
+#undef  CLIP_WITH_CAIRO_SUPPORT
+#include <2geom/conic_section_clipper_impl.h>
+
+
+#endif // _2GEOM_CONIC_SECTION_CLIPPER_H_
+
+
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/conic_section_clipper_cr.h b/include/2geom/conic_section_clipper_cr.h
new file mode 100644
index 0000000..6c62494
--- /dev/null
+++ b/include/2geom/conic_section_clipper_cr.h
@@ -0,0 +1,64 @@
+/** @file
+ * @brief Conic section clipping with respect to a rectangle
+ *//*
+ * Authors:
+ *      Marco Cecchetti <mrcekets at gmail>
+ *
+ * Copyright 2009  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+//  This header should be used for graphical debugging purpuse only.          //
+////////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_CR_H
+#define LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_CR_H
+
+
+#define CLIP_WITH_CAIRO_SUPPORT
+#include "conic_section_clipper_impl.h"
+#include "conic_section_clipper_impl.cpp"
+
+
+#endif // _2GEOM_CONIC_SECTION_CLIPPER_CR_H_
+
+
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/conic_section_clipper_impl.h b/include/2geom/conic_section_clipper_impl.h
new file mode 100644
index 0000000..ee67df1
--- /dev/null
+++ b/include/2geom/conic_section_clipper_impl.h
@@ -0,0 +1,346 @@
+/** @file
+ * @brief Conic section clipping with respect to a rectangle
+ *//*
+ * Authors:
+ *      Marco Cecchetti <mrcekets at gmail>
+ *
+ * Copyright 2009  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_IMPL_H
+#define LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_IMPL_H
+
+
+#include <2geom/conicsec.h>
+#include <2geom/line.h>
+
+#include <list>
+#include <map>
+
+
+
+#ifdef CLIP_WITH_CAIRO_SUPPORT
+    #include <2geom/toys/path-cairo.h>
+    #define CLIPPER_CLASS clipper_cr
+#else
+    #define CLIPPER_CLASS clipper
+#endif
+
+//#define CLIPDBG
+
+#ifdef CLIPDBG
+#include <2geom/toys/path-cairo.h>
+#define DBGINFO(msg) \
+    std::cerr << msg << std::endl;
+#define DBGPRINT(msg, var) \
+    std::cerr << msg << var << std::endl;
+#define DBGPRINTIF(cond, msg, var) \
+    if (cond)                                                                  \
+        std::cerr << msg << var << std::endl;
+
+#define DBGPRINT2(msg1, var1, msg2, var2) \
+    std::cerr << msg1 << var1 << msg2 << var2 << std::endl;
+
+#define DBGPRINTCOLL(msg, coll) \
+    if (coll.size() != 0)                                                      \
+        std::cerr << msg << ":\n";                                             \
+    for (size_t i = 0; i < coll.size(); ++i)                                   \
+    {                                                                          \
+        std::cerr << i << ": " << coll[i] << "\n";                             \
+    }
+
+#else
+#define DBGINFO(msg)
+#define DBGPRINT(msg, var)
+#define DBGPRINTIF(cond, msg, var)
+#define DBGPRINT2(msg1, var1, msg2, var2)
+#define DBGPRINTCOLL(msg, coll)
+#endif
+
+
+
+
+namespace Geom
+{
+
+class CLIPPER_CLASS
+{
+
+  public:
+
+#ifdef CLIP_WITH_CAIRO_SUPPORT
+    clipper_cr (cairo_t* _cr, const xAx & _cs, const Rect & _R)
+        : cr(_cr), cs(_cs), R(_R)
+    {
+        DBGPRINT ("CLIP: right side: ", R.right())
+        DBGPRINT ("CLIP: top side: ", R.top())
+        DBGPRINT ("CLIP: left side: ", R.left())
+        DBGPRINT ("CLIP: bottom side: ", R.bottom())
+    }
+#else
+    clipper (const xAx & _cs, const Rect & _R)
+        : cs(_cs), R(_R)
+    {
+    }
+#endif
+
+    bool clip (std::vector<RatQuad> & arcs);
+
+    bool found_any_isolated_point() const
+    {
+        return ( !single_points.empty() );
+    }
+
+    const std::vector<Point> & isolated_points() const
+    {
+        return single_points;
+    }
+
+
+  private:
+    bool intersect (std::vector<Point> & crossing_points) const;
+
+    bool are_paired (Point & M, const Point & P1, const Point & P2) const;
+    void pairing (std::vector<Point> & paired_points,
+                  std::vector<Point> & inner_points,
+                  const std::vector<Point> & crossing_points);
+
+    Point find_inner_point_by_bisector_line (const Point & P,
+                                             const Point & Q) const;
+    Point find_inner_point (const Point & P, const Point & Q) const;
+
+    std::list<Point>::iterator split (std::list<Point> & points,
+                                      std::list<Point>::iterator sp,
+                                      std::list<Point>::iterator fp) const;
+    void rsplit (std::list<Point> & points,
+                 std::list<Point>::iterator sp,
+                 std::list<Point>::iterator fp,
+                 size_t k) const;
+
+    void rsplit (std::list<Point> & points,
+                 std::list<Point>::iterator sp,
+                 std::list<Point>::iterator fp,
+                 double length) const;
+
+  private:
+#ifdef CLIP_WITH_CAIRO_SUPPORT
+    cairo_t* cr;
+#endif
+    const xAx & cs;
+    const Rect & R;
+    std::vector<Point> single_points;
+};
+
+
+
+
+/*
+ *  Given two point "P", "Q" on the conic section the method computes
+ *  a third point inner to the arc with end-point "P", "Q".
+ *  The new point is found by intersecting the conic with the bisector line
+ *  of the PQ line segment.
+ */
+inline
+Point CLIPPER_CLASS::find_inner_point_by_bisector_line (const Point & P,
+                                                  const Point & Q) const
+{
+    DBGPRINT ("CLIP: find_inner_point_by_bisector_line: P = ", P)
+    DBGPRINT ("CLIP: find_inner_point_by_bisector_line: Q = ", Q)
+    Line bl = make_bisector_line (LineSegment (P, Q));
+    std::vector<double> rts = cs.roots (bl);
+    //DBGPRINT ("CLIP: find_inner_point: rts.size = ", rts.size())
+    double t;
+    if (rts.size() == 0)
+    {
+        THROW_LOGICALERROR ("clipper::find_inner_point_by_bisector_line: "
+                            "no conic-bisector line intersection point");
+    }
+    if (rts.size() == 2)
+    {
+        // we suppose that the searched point is the nearest
+        // to the line segment PQ
+        t = (std::fabs(rts[0]) < std::fabs(rts[1])) ? rts[0] : rts[1];
+    }
+    else
+    {
+        t = rts[0];
+    }
+    return bl.pointAt (t);
+}
+
+
+/*
+ *  Given two point "P", "Q" on the conic section the method computes
+ *  a third point inner to the arc with end-point "P", "Q".
+ *  The new point is found by intersecting the conic with the line
+ *  passing through the middle point of the PQ line segment and
+ *  the intersection point of the tangent lines at points P and Q.
+ */
+inline
+Point CLIPPER_CLASS::find_inner_point (const Point & P, const Point & Q) const
+{
+
+    Line l1 = cs.tangent (P);
+    Line l2 = cs.tangent (Q);
+    Line l;
+    // in case we fail to find a crossing point we fall back to the bisector
+    // method
+    try
+    {
+        OptCrossing oc = intersection(l1, l2);
+        if (!oc)
+        {
+            return find_inner_point_by_bisector_line (P, Q);
+        }
+        l.setPoints (l1.pointAt (oc->ta), middle_point (P, Q));
+    }
+    catch (Geom::InfiniteSolutions const &e)
+    {
+        return find_inner_point_by_bisector_line (P, Q);
+    }
+
+    std::vector<double> rts = cs.roots (l);
+    double t;
+    if (rts.size() == 0)
+    {
+        return find_inner_point_by_bisector_line (P, Q);
+    }
+    // the line "l" origin is set to the tangent crossing point so in case
+    // we find two intersection points only the nearest belongs to the given arc
+    // pay attention: in case we are dealing with an hyperbola (remember that
+    // end points are on the same branch, because they are paired) the tangent
+    // crossing point belongs to the angle delimited by hyperbola asymptotes
+    // and containing the given hyperbola branch, so the previous statement is
+    // still true
+    if (rts.size() == 2)
+    {
+        t = (std::fabs(rts[0]) < std::fabs(rts[1])) ? rts[0] : rts[1];
+    }
+    else
+    {
+        t = rts[0];
+    }
+    return l.pointAt (t);
+}
+
+
+/*
+ *  Given a list of points on the conic section, and given two consecutive
+ *  points belonging to the list and passed by two list iterators, the method
+ *  finds a new point that is inner to the conic arc which has the two passed
+ *  points as initial and final point. This new point is inserted into the list
+ *  between the two passed points and an iterator pointing to the new point
+ *  is returned.
+ */
+inline
+std::list<Point>::iterator CLIPPER_CLASS::split (std::list<Point> & points,
+                                           std::list<Point>::iterator sp,
+                                           std::list<Point>::iterator fp) const
+{
+    Point new_point = find_inner_point (*sp, *fp);
+    std::list<Point>::iterator ip = points.insert (fp, new_point);
+    //std::cerr << "CLIP: split: [" << *sp << ", " << *ip << ", "
+    //          << *fp << "]" << std::endl;
+    return ip;
+}
+
+
+/*
+ *  Given a list of points on the conic section, and given two consecutive
+ *  points belonging to the list and passed by two list iterators, the method
+ *  recursively finds new points that are inner to the conic arc which has
+ *  the two passed points as initial and final point. The recursion stop after
+ *  "k" recursive calls. These new points are inserted into the list between
+ *  the two passed points, and in the order we cross them going from
+ *  the initial to the final arc point.
+ */
+inline
+void CLIPPER_CLASS::rsplit (std::list<Point> & points,
+                      std::list<Point>::iterator sp,
+                      std::list<Point>::iterator fp,
+                      size_t k) const
+{
+    if (k == 0)
+    {
+        //DBGINFO("CLIP: split: no further split")
+        return;
+    }
+
+    std::list<Point>::iterator ip = split (points, sp, fp);
+    --k;
+    rsplit (points, sp, ip, k);
+    rsplit (points, ip, fp, k);
+}
+
+
+/*
+ *  Given a list of points on the conic section, and given two consecutive
+ *  points belonging to the list and passed by two list iterators, the method
+ *  recursively finds new points that are inner to the conic arc which has
+ *  the two passed points as initial and final point. The recursion stop when
+ *  the max distance between the new computed inner point and the two passed
+ *  arc end-points is less then the value specified by the "length" parameter.
+ *  These new points are inserted into the list between the two passed points,
+ *  and in the order we cross them going from the initial to the final arc point.
+ */
+inline
+void CLIPPER_CLASS::rsplit (std::list<Point> & points,
+                      std::list<Point>::iterator sp,
+                      std::list<Point>::iterator fp,
+                      double length) const
+{
+    std::list<Point>::iterator ip = split (points, sp, fp);
+    double d1 = distance (*sp, *ip);
+    double d2 = distance (*ip, *fp);
+    double mdist = std::max (d1, d2);
+
+    if (mdist < length)
+    {
+        //DBGINFO("CLIP: split: no further split")
+        return;
+    }
+
+    // they have to be called both to keep the number of points in the list
+    // in the form 2k+1 where k are the sub-arcs the initial arc is split in.
+    rsplit (points, sp, ip, length);
+    rsplit (points, ip, fp, length);
+}
+
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_IMPL_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/conicsec.h b/include/2geom/conicsec.h
new file mode 100644
index 0000000..bfd5f36
--- /dev/null
+++ b/include/2geom/conicsec.h
@@ -0,0 +1,537 @@
+/** @file
+ * @brief Conic Section
+ *//*
+ * Authors:
+ *      Nathan Hurst <njh@njhurst.com>
+ *
+ * Copyright 2009  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+
+#ifndef LIB2GEOM_SEEN_CONICSEC_H
+#define LIB2GEOM_SEEN_CONICSEC_H
+
+#include <2geom/exception.h>
+#include <2geom/angle.h>
+#include <2geom/rect.h>
+#include <2geom/affine.h>
+#include <2geom/point.h>
+#include <2geom/line.h>
+#include <2geom/bezier-curve.h>
+#include <2geom/numeric/linear_system.h>
+#include <2geom/numeric/symmetric-matrix-fs.h>
+#include <2geom/numeric/symmetric-matrix-fs-operation.h>
+
+#include <optional>
+
+#include <string>
+#include <vector>
+#include <ostream>
+
+
+
+
+namespace Geom
+{
+
+class RatQuad{
+    /**
+     * A curve of the form B02*A + B12*B*w + B22*C/(B02 + B12*w + B22)
+     * These curves can exactly represent a piece conic section less than a certain angle (find out)
+     *
+     **/
+
+public:
+    Point P[3];
+    double w;
+    RatQuad() {}
+    RatQuad(Point a, Point b, Point c, double w) : w(w) {
+        P[0] = a;
+        P[1] = b;
+        P[2] = c;
+    }
+    double lambda() const;
+
+    static RatQuad fromPointsTangents(Point P0, Point dP0,
+                                      Point P,
+                                      Point P2, Point dP2);
+    static RatQuad circularArc(Point P0, Point P1, Point P2);
+
+    CubicBezier toCubic() const;
+    CubicBezier toCubic(double lam) const;
+
+    Point pointAt(double t) const;
+    Point at0() const {return P[0];}
+    Point at1() const {return P[2];}
+
+    void split(RatQuad &a, RatQuad &b) const;
+
+    D2<SBasis> hermite() const;
+    std::vector<SBasis> homogeneous() const;
+};
+
+
+
+
+class xAx{
+public:
+    double c[6];
+
+    enum kind_t
+    {
+        PARABOLA,
+        CIRCLE,
+        REAL_ELLIPSE,
+        IMAGINARY_ELLIPSE,
+        RECTANGULAR_HYPERBOLA,
+        HYPERBOLA,
+        DOUBLE_LINE,
+        TWO_REAL_PARALLEL_LINES,
+        TWO_IMAGINARY_PARALLEL_LINES,
+        TWO_REAL_CROSSING_LINES,
+        TWO_IMAGINARY_CROSSING_LINES, // crossing at a real point
+        SINGLE_POINT = TWO_IMAGINARY_CROSSING_LINES,
+        UNKNOWN
+    };
+
+
+    xAx() {}
+
+    /*
+     *  Define the conic section by its algebraic equation coefficients
+     *
+     *  c0, .., c5: equation coefficients
+     */
+    xAx (double c0, double c1, double c2, double c3, double c4, double c5)
+    {
+        set (c0, c1, c2, c3, c4, c5);
+    }
+
+    /*
+     *  Define a conic section by its related symmetric matrix
+     */
+    xAx (const NL::ConstSymmetricMatrixView<3> & C)
+    {
+        set(C);
+    }
+
+    /*
+     *  Define a conic section by computing the one that fits better with
+     *  N points.
+     *
+     *  points: points to fit
+     *
+     *  precondition: there must be at least 5 non-overlapping points
+     */
+    xAx (std::vector<Point> const& points)
+    {
+        set (points);
+    }
+
+    /*
+     *  Define a section conic by providing the coordinates of one of its
+     *  vertex,the major axis inclination angle and the coordinates of its foci
+     *  with respect to the unidimensional system defined by the major axis with
+     *  origin set at the provided vertex.
+     *
+     *  _vertex :   section conic vertex V
+     *  _angle :    section conic major axis angle
+     *  _dist1:     +/-distance btw V and nearest focus
+     *  _dist2:     +/-distance btw V and farest focus
+     *
+     *  prerequisite: _dist1 <= _dist2
+     */
+    xAx (const Point& _vertex, double _angle, double _dist1, double _dist2)
+    {
+        set (_vertex, _angle, _dist1, _dist2);
+    }
+
+    /*
+     *  Define a conic section by providing one of its vertex and its foci.
+     *
+     *  _vertex: section conic vertex
+     *  _focus1: section conic focus
+     *  _focus2: section conic focus
+     */
+    xAx (const Point& _vertex, const Point& _focus1, const Point& _focus2)
+    {
+        set(_vertex, _focus1, _focus2);
+    }
+
+    /*
+     *  Define a conic section by passing a focus, the related directrix,
+     *  and the eccentricity (e)
+     *  (e < 1 -> ellipse; e = 1 -> parabola; e > 1 -> hyperbola)
+     *
+     *  _focus:         a focus of the conic section
+     *  _directrix:     the directrix related to the given focus
+     *  _eccentricity:  the eccentricity parameter of the conic section
+     */
+    xAx (const Point & _focus, const Line & _directrix, double _eccentricity)
+    {
+        set (_focus, _directrix, _eccentricity);
+    }
+
+    /*
+     *  Made up a degenerate conic section as a pair of lines
+     *
+     *  l1, l2: lines that made up the conic section
+     */
+    xAx (const Line& l1, const Line& l2)
+    {
+        set (l1, l2);
+    }
+
+    /*
+     *  Define the conic section by its algebraic equation coefficients
+     *  c0, ..., c5: equation coefficients
+     */
+    void set (double c0, double c1, double c2, double c3, double c4, double c5)
+    {
+        c[0] = c0;   c[1] = c1;   c[2] = c2; // xx, xy, yy
+        c[3] = c3;   c[4] = c4; // x, y
+        c[5] = c5; // 1
+    }
+
+    /*
+     *  Define a conic section by its related symmetric matrix
+     */
+    void set (const NL::ConstSymmetricMatrixView<3> & C)
+    {
+        set(C(0,0), 2*C(1,0), C(1,1), 2*C(2,0), 2*C(2,1), C(2,2));
+    }
+
+    void set (std::vector<Point> const& points);
+
+    void set (const Point& _vertex, double _angle, double _dist1, double _dist2);
+
+    void set (const Point& _vertex, const Point& _focus1, const Point& _focus2);
+
+    void set (const Point & _focus, const Line & _directrix, double _eccentricity);
+
+    void set (const Line& l1, const Line& l2);
+
+
+    static xAx fromPoint(Point p);
+    static xAx fromDistPoint(Point p, double d);
+    static xAx fromLine(Point n, double d);
+    static xAx fromLine(Line l);
+    static xAx fromPoints(std::vector<Point> const &pts);
+
+
+    template<typename T>
+    T evaluate_at(T x, T y) const {
+        return c[0]*x*x + c[1]*x*y + c[2]*y*y + c[3]*x + c[4]*y + c[5];
+    }
+
+    double valueAt(Point P) const;
+
+    std::vector<double> implicit_form_coefficients() const {
+        return std::vector<double>(c, c+6);
+    }
+
+    template<typename T>
+    T evaluate_at(T x, T y, T w) const {
+        return c[0]*x*x + c[1]*x*y + c[2]*y*y + c[3]*x*w + c[4]*y*w + c[5]*w*w;
+    }
+
+    xAx scale(double sx, double sy) const;
+
+    Point gradient(Point p) const;
+
+    xAx operator-(xAx const &b) const;
+    xAx operator+(xAx const &b) const;
+    xAx operator+(double const &b) const;
+    xAx operator*(double const &b) const;
+
+    std::vector<Point> crossings(Rect r) const;
+    std::optional<RatQuad> toCurve(Rect const & bnd) const;
+    std::vector<double> roots(Point d, Point o) const;
+
+    std::vector<double> roots(Line const &l) const;
+
+    static Interval quad_ex(double a, double b, double c, Interval ivl);
+
+    Geom::Affine hessian() const;
+
+    std::optional<Point> bottom() const;
+
+    Interval extrema(Rect r) const;
+
+
+    /*
+     *  Return the symmetric matrix related to the conic section.
+     *  Modifying the matrix does not modify the conic section
+     */
+    NL::SymmetricMatrix<3> get_matrix() const
+    {
+        NL::SymmetricMatrix<3> C(c);
+        C(1,0) *= 0.5; C(2,0) *= 0.5; C(2,1) *= 0.5;
+        return C;
+    }
+
+    /*
+     *  Return the i-th coefficient of the conic section algebraic equation
+     *  Modifying the returned value does not modify the conic section coefficient
+     */
+    double coeff (size_t i) const
+    {
+        return c[i];
+    }
+
+    /*
+     *  Return the i-th coefficient of the conic section algebraic equation
+     *  Modifying the returned value modifies the conic section coefficient
+     */
+    double& coeff (size_t i)
+    {
+        return c[i];
+    }
+
+    kind_t kind () const;
+
+    std::string categorise() const;
+
+    /*
+     *  Return true if the equation:
+     *  c0*x^2 + c1*xy + c2*y^2 + c3*x + c4*y +c5 == 0
+     *  really defines a conic, false otherwise
+     */
+    bool is_quadratic() const
+    {
+        return (coeff(0) != 0 || coeff(1) != 0 || coeff(2) != 0);
+    }
+
+    /*
+     *  Return true if the conic is degenerate, i.e. if the related matrix
+     *  determinant is null, false otherwise
+     */
+    bool isDegenerate() const
+    {
+        return (det_sgn (get_matrix()) == 0);
+    }
+
+    /*
+     *  Compute the centre of symmetry of the conic section when it exists,
+     *  else it return an uninitialized std::optional<Point> instance.
+     */
+    std::optional<Point> centre() const
+    {
+        typedef std::optional<Point> opt_point_t;
+
+        double d = coeff(1) * coeff(1) - 4 * coeff(0) * coeff(2);
+        if (are_near (d, 0))  return opt_point_t();
+        NL::Matrix Q(2, 2);
+        Q(0,0) = coeff(0);
+        Q(1,1) = coeff(2);
+        Q(0,1) = Q(1,0) = coeff(1) * 0.5;
+        NL::Vector T(2);
+        T[0] = - coeff(3) * 0.5;
+        T[1] = - coeff(4) * 0.5;
+
+        NL::LinearSystem ls (Q, T);
+        NL::Vector sol = ls.SV_solve();
+        Point C;
+        C[0] = sol[0];
+        C[1] = sol[1];
+
+        return opt_point_t(C);
+    }
+
+    double axis_angle() const;
+
+    void roots (std::vector<double>& sol, Coord v, Dim2 d) const;
+
+    xAx translate (const Point & _offset) const;
+
+    xAx rotate (double angle) const;
+
+    /*
+     *  Rotate the conic section by the given angle wrt the provided point.
+     *
+     *  _rot_centre:   the rotation centre
+     *  _angle:        the rotation angle
+     */
+    xAx rotate (const Point & _rot_centre, double _angle) const
+    {
+        xAx result
+            = translate (-_rot_centre).rotate (_angle).translate (_rot_centre);
+        return result;
+    }
+
+    /*
+     *  Compute the tangent line of the conic section at the provided point
+     *
+     *  _point: the conic section point the tangent line pass through
+     */
+    Line tangent (const Point & _point) const
+    {
+        NL::Vector pp(3);
+        pp[0] = _point[0]; pp[1] = _point[1]; pp[2] = 1;
+        NL::SymmetricMatrix<3> C = get_matrix();
+        NL::Vector line = C * pp;
+        return Line(line[0], line[1], line[2]);
+    }
+
+    /*
+     *  For a non degenerate conic compute the dual conic.
+     *  TODO: investigate degenerate case
+     */
+    xAx dual () const
+    {
+        //assert (! isDegenerate());
+        NL::SymmetricMatrix<3> C = get_matrix();
+        NL::SymmetricMatrix<3> D = adj(C);
+        xAx dc(D);
+        return dc;
+    }
+
+    bool decompose (Line& l1, Line& l2) const;
+
+    /**
+     * @brief Division-free decomposition of a degenerate conic section, without
+     *        degeneration test.
+     *
+     * When the conic is degenerate, it consists of 0, 1 or 2 lines in the xy-plane.
+     * This function returns these lines. But it does not check whether the conic
+     * is really degenerate, so calling it on a non-degenerate conic produces a
+     * meaningless result.
+     *
+     * If the number of lines is less than two, the trailing lines in the returned
+     * array will be degenerate. Use Line::isDegenerate() to test for that.
+     *
+     * This version of the decomposition is division-free, which improves numerical
+     * stability compared to decompose().
+     *
+     * @param epsilon The numerical threshold for floating point comparison of discriminants.
+     */
+    std::array<Line, 2> decompose_df(Coord epsilon = EPSILON) const;
+
+    /*
+     *  Generate a RatQuad object from a conic arc.
+     *
+     *  p0: the initial point of the arc
+     *  p1: the inner point of the arc
+     *  p2: the final point of the arc
+     */
+    RatQuad toRatQuad (const Point & p0,
+                       const Point & p1,
+                       const Point & p2) const
+    {
+        Point dp0 = gradient (p0);
+        Point dp2 = gradient (p2);
+        return
+            RatQuad::fromPointsTangents (p0, rot90 (dp0), p1, p2, rot90 (dp2));
+    }
+
+    /*
+     *  Return the angle related to the normal gradient computed at the passed
+     *  point.
+     *
+     *  _point: the point at which computes the angle
+     *
+     *  prerequisite: the passed point must lie on the conic
+     */
+    double angle_at (const Point & _point) const
+    {
+        double angle = atan2 (gradient (_point));
+        if (angle < 0)  angle += (2*M_PI);
+        return angle;
+    }
+
+    /*
+     *  Return true if the given point is contained in the conic arc determined
+     *  by the passed points.
+     *
+     *  _point:     the point to be tested
+     *  _initial:   the initial point of the arc
+     *  _inner:     an inner point of the arc
+     *  _final:     the final point of the arc
+     *
+     *  prerequisite: the passed points must lie on the conic, the inner point
+     *                has to be strictly contained in the arc, except when the
+     *                initial and final points are equal: in such a case if the
+     *                inner point is also equal to them, then they define an arc
+     *                made up by a single point.
+     *
+     */
+    bool arc_contains (const Point & _point, const Point & _initial,
+                       const Point & _inner, const Point & _final) const
+    {
+        AngleInterval ai(angle_at(_initial), angle_at(_inner), angle_at(_final));
+        return ai.contains(angle_at(_point));
+    }
+
+    Rect arc_bound (const Point & P1, const Point & Q, const Point & P2) const;
+
+    std::vector<Point> allNearestTimes (const Point &P) const;
+
+    /*
+     *  Return the point on the conic section nearest to the passed point "P".
+     *
+     *  P: the point to compute the nearest one
+     */
+    Point nearestTime (const Point &P) const
+    {
+        std::vector<Point> points = allNearestTimes (P);
+        if ( !points.empty() )
+        {
+            return points.front();
+        }
+        // else
+        THROW_LOGICALERROR ("nearestTime: no nearest point found");
+        return Point();
+    }
+
+};
+
+std::vector<Point> intersect(const xAx & C1, const xAx & C2);
+
+bool clip (std::vector<RatQuad> & rq, const xAx & cs, const Rect & R);
+
+inline std::ostream &operator<< (std::ostream &out_file, const xAx &x) {
+    for(double i : x.c) {
+        out_file << i << ", ";
+    }
+    return out_file;
+}
+
+};
+
+
+#endif // LIB2GEOM_SEEN_CONICSEC_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
+
diff --git a/include/2geom/convex-hull.h b/include/2geom/convex-hull.h
new file mode 100644
index 0000000..4dff9c2
--- /dev/null
+++ b/include/2geom/convex-hull.h
@@ -0,0 +1,346 @@
+/** @file
+ * @brief Convex hull data structures
+ *//*
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_CONVEX_HULL_H
+#define LIB2GEOM_SEEN_CONVEX_HULL_H
+
+#include <2geom/point.h>
+#include <2geom/rect.h>
+#include <vector>
+#include <algorithm>
+#include <boost/operators.hpp>
+#include <optional>
+#include <boost/range/iterator_range.hpp>
+
+namespace Geom {
+
+namespace {
+
+/** @brief Iterator for the lower convex hull.
+ * This iterator allows us to avoid duplicating any points in the hull
+ * boundary and still express most algorithms in a concise way. */
+class ConvexHullLowerIterator
+    : public boost::random_access_iterator_helper
+        < ConvexHullLowerIterator
+        , Point
+        , std::ptrdiff_t
+        , Point const *
+        , Point const &
+        >
+{
+public:
+    typedef ConvexHullLowerIterator Self;
+    ConvexHullLowerIterator()
+        : _data(NULL)
+        , _size(0)
+        , _x(0)
+    {}
+    ConvexHullLowerIterator(std::vector<Point> const &pts, std::size_t x)
+        : _data(&pts[0])
+        , _size(pts.size())
+        , _x(x)
+    {}
+
+    Self &operator++() {
+        *this += 1;
+        return *this;
+    }
+    Self &operator--() {
+        *this -= 1;
+        return *this;
+    }
+    Self &operator+=(std::ptrdiff_t d) {
+        _x += d;
+        return *this;
+    }
+    Self &operator-=(std::ptrdiff_t d) {
+        _x -= d;
+        return *this;
+    }
+    std::ptrdiff_t operator-(Self const &other) const {
+        return _x - other._x;
+    }
+    Point const &operator*() const {
+        if (_x < _size) {
+            return _data[_x];
+        } else {
+            return *_data;
+        }
+    }
+    bool operator==(Self const &other) const {
+        return _data == other._data && _x == other._x;
+    }
+    bool operator<(Self const &other) const {
+        return _data == other._data && _x < other._x;
+    }
+
+private:
+    Point const *_data;
+    std::size_t _size;
+    std::size_t _x;
+};
+
+} // end anonymous namespace
+
+/**
+ * @brief Convex hull based on the Andrew's monotone chain algorithm.
+ * @ingroup Shapes
+ */
+class ConvexHull {
+public:
+    typedef std::vector<Point>::const_iterator iterator;
+    typedef std::vector<Point>::const_iterator const_iterator;
+    typedef std::vector<Point>::const_iterator UpperIterator;
+    typedef ConvexHullLowerIterator LowerIterator;
+
+    /// @name Construct a convex hull.
+    /// @{
+
+    /// Create an empty convex hull.
+    ConvexHull() {}
+    /// Construct a singular convex hull.
+    explicit ConvexHull(Point const &a)
+        : _boundary(1, a)
+        , _lower(1)
+    {}
+    /// Construct a convex hull of two points.
+    ConvexHull(Point const &a, Point const &b);
+    /// Construct a convex hull of three points.
+    ConvexHull(Point const &a, Point const &b, Point const &c);
+    /// Construct a convex hull of four points.
+    ConvexHull(Point const &a, Point const &b, Point const &c, Point const &d);
+    /// Create a convex hull of a vector of points.
+    ConvexHull(std::vector<Point> const &pts);
+
+    /// Create a convex hull of a range of points.
+    template <typename Iter>
+    ConvexHull(Iter first, Iter last)
+        : _lower(0)
+    {
+        _prune(first, last, _boundary);
+        _construct();
+    }
+    /// @}
+
+    /// @name Inspect basic properties.
+    /// @{
+
+    /// Check for emptiness.
+    bool empty() const { return _boundary.empty(); }
+    /// Get the number of points in the hull.
+    size_t size() const { return _boundary.size(); }
+    /// Check whether the hull contains only one point.
+    bool isSingular() const { return _boundary.size() == 1; }
+    /// Check whether the hull is a line.
+    bool isLinear() const { return _boundary.size() == 2; }
+    /// Check whether the hull has zero area.
+    bool isDegenerate() const { return _boundary.size() < 3; }
+    /// Calculate the area of the convex hull.
+    double area() const;
+    //Point centroid() const;
+    //double areaAndCentroid(Point &c);
+    //FatLine maxDiameter() const;
+    //FatLine minDiameter() const;
+    /// @}
+
+    /// @name Inspect bounds and extreme points.
+    /// @{
+
+    /// Compute the bounding rectangle of the convex hull.
+    OptRect bounds() const;
+
+    /// Get the leftmost (minimum X) coordinate of the hull.
+    Coord left() const { return _boundary[0][X]; }
+    /// Get the rightmost (maximum X) coordinate of the hull.
+    Coord right() const { return _boundary[_lower-1][X]; }
+    /// Get the topmost (minimum Y) coordinate of the hull.
+    Coord top() const { return topPoint()[Y]; }
+    /// Get the bottommost (maximum Y) coordinate of the hull.
+    Coord bottom() const { return bottomPoint()[Y]; }
+
+    /// Get the leftmost (minimum X) point of the hull.
+    /// If the leftmost edge is vertical, the top point of the edge is returned.
+    Point leftPoint() const { return _boundary[0]; }
+    /// Get the rightmost (maximum X) point of the hull.
+    /// If the rightmost edge is vertical, the bottom point edge is returned.
+    Point rightPoint() const { return _boundary[_lower-1]; }
+    /// Get the topmost (minimum Y) point of the hull.
+    /// If the topmost edge is horizontal, the right point of the edge is returned.
+    Point topPoint() const;
+    /// Get the bottommost (maximum Y) point of the hull.
+    /// If the bottommost edge is horizontal, the left point of the edge is returned.
+    Point bottomPoint() const;
+    ///@}
+
+    /// @name Iterate over points.
+    /// @{
+    /** @brief Get the begin iterator to the points that form the hull.
+     * Points are returned beginning with the leftmost one, going along
+     * the upper (minimum Y) side, and then along the bottom.
+     * Thus the points are always ordered clockwise. No point is
+     * repeated. */
+    iterator begin() const { return _boundary.begin(); }
+    /// Get the end iterator to the points that form the hull.
+    iterator end() const { return _boundary.end(); }
+    /// Get the first, leftmost point in the hull.
+    Point const &front() const { return _boundary.front(); }
+    /// Get the penultimate point of the lower hull.
+    Point const &back() const { return _boundary.back(); }
+    Point const &operator[](std::size_t i) const {
+        return _boundary[i];
+    }
+
+    /** @brief Get an iterator range to the upper part of the hull.
+     * This returns a range that includes the leftmost point,
+     * all points of the upper hull, and the rightmost point. */
+    boost::iterator_range<UpperIterator> upperHull() const {
+        boost::iterator_range<UpperIterator> r(_boundary.begin(), _boundary.begin() + _lower);
+        return r;
+    }
+
+    /** @brief Get an iterator range to the lower part of the hull.
+     * This returns a range that includes the leftmost point,
+     * all points of the lower hull, and the rightmost point. */
+    boost::iterator_range<LowerIterator> lowerHull() const {
+        if (_boundary.empty()) {
+            boost::iterator_range<LowerIterator> r(LowerIterator(_boundary, 0),
+                                                   LowerIterator(_boundary, 0));
+            return r;
+        }
+        if (_boundary.size() == 1) {
+            boost::iterator_range<LowerIterator> r(LowerIterator(_boundary, 0),
+                                                   LowerIterator(_boundary, 1));
+            return r;
+        }
+        boost::iterator_range<LowerIterator> r(LowerIterator(_boundary, _lower - 1),
+                                               LowerIterator(_boundary, _boundary.size() + 1));
+        return r;
+    }
+    /// @}
+
+    /// @name Check for containment and intersection.
+    /// @{
+    /** @brief Check whether the given point is inside the hull.
+     * This takes logarithmic time. */
+    bool contains(Point const &p) const;
+    /** @brief Check whether the given axis-aligned rectangle is inside the hull.
+     * A rectangle is inside the hull if all of its corners are inside. */
+    bool contains(Rect const &r) const;
+    /// Check whether the given convex hull is completely contained in this one.
+    bool contains(ConvexHull const &other) const;
+    //bool interiorContains(Point const &p) const;
+    //bool interiorContains(Rect const &r) const;
+    //bool interiorContains(ConvexHull const &other) const;
+    //bool intersects(Rect const &r) const;
+    //bool intersects(ConvexHull const &other) const;
+
+    //ConvexHull &operator|=(ConvexHull const &other);
+    //ConvexHull &operator&=(ConvexHull const &other);
+    //ConvexHull &operator*=(Affine const &m);
+
+    //ConvexHull &expand(Point const &p);
+    //void unifyWith(ConvexHull const &other);
+    //void intersectWith(ConvexHull const &other);
+    /// @}
+
+    void swap(ConvexHull &other);
+    void swap(std::vector<Point> &pts);
+
+private:
+    void _construct();
+    static bool _is_clockwise_turn(Point const &a, Point const &b, Point const &c);
+
+    /// Take a vector of points and produce a pruned sorted vector.
+    template <typename Iter>
+    static void _prune(Iter first, Iter last, std::vector<Point> &out) {
+        std::optional<Point> ymin, ymax, xmin, xmax;
+        for (Iter i = first; i != last; ++i) {
+            Point p = *i;
+            if (!ymin || Point::LexLess<Y>()(p, *ymin)) {
+                ymin = p;
+            }
+            if (!xmin || Point::LexLess<X>()(p, *xmin)) {
+                xmin = p;
+            }
+            if (!ymax || Point::LexGreater<Y>()(p, *ymax)) {
+                ymax = p;
+            }
+            if (!xmax || Point::LexGreater<X>()(p, *xmax)) {
+                xmax = p;
+            }
+        }
+        if (!ymin) return;
+
+        ConvexHull qhull(*xmin, *xmax, *ymin, *ymax);
+        for (Iter i = first; i != last; ++i) {
+            if (qhull.contains(*i)) continue;
+            out.push_back(*i);
+        }
+
+        out.push_back(*xmin);
+        out.push_back(*xmax);
+        out.push_back(*ymin);
+        out.push_back(*ymax);
+        std::sort(out.begin(), out.end(), Point::LexLess<X>());
+        out.erase(std::unique(out.begin(), out.end()), out.end());
+    }
+
+    /// Sequence of points forming the convex hull polygon.
+    std::vector<Point> _boundary;
+    /// Index one past the rightmost point, where the lower part of the boundary starts.
+    std::size_t _lower;
+};
+
+/** @brief Output operator for convex hulls.
+ * Prints out all the coordinates. */
+inline std::ostream &operator<< (std::ostream &out_file, const Geom::ConvexHull &in_cvx) {
+    out_file << "ConvexHull(";
+    for(auto i : in_cvx) {
+        out_file << i << ", ";
+    }
+    out_file << ")";
+    return out_file;
+}
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_CONVEX_HULL_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/coord.h b/include/2geom/coord.h
new file mode 100644
index 0000000..40db84e
--- /dev/null
+++ b/include/2geom/coord.h
@@ -0,0 +1,208 @@
+/** @file
+ * @brief Integral and real coordinate types and some basic utilities
+ *//*
+ * Authors:
+ *   Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Copyright 2006-2015 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_COORD_H
+#define LIB2GEOM_SEEN_COORD_H
+
+#include <cmath>
+#include <limits>
+#include <string>
+#include <functional>
+#include <boost/operators.hpp>
+#include <2geom/forward.h>
+
+namespace Geom {
+
+/** @brief 2D axis enumeration (X or Y).
+ * @ingroup Primitives */
+enum Dim2 { X=0, Y=1 };
+
+/** @brief Get the other (perpendicular) dimension.
+ * @ingroup Primitives */
+inline constexpr Dim2 other_dimension(Dim2 d) { return Dim2(int(d) ^ 1); }
+
+// TODO: make a smarter implementation with C++11
+template <typename T>
+struct D2Traits {
+    using D1Value = typename T::D1Value;
+    using D1Reference = typename T::D1Reference;
+    using D1ConstReference = typename T::D1ConstReference;
+};
+
+/** @brief Axis extraction functor.
+ * For use with things such as Boost's transform_iterator.
+ * @ingroup Utilities */
+template <Dim2 D, typename T>
+struct GetAxis {
+    using result_type = typename D2Traits<T>::D1Value;
+    using argument_type = T;
+    typename D2Traits<T>::D1Value operator()(T const &a) const {
+        return a[D];
+    }
+};
+
+/** @brief Floating point type used to store coordinates.
+ * @ingroup Primitives */
+using Coord = double;
+
+/** @brief Type used for integral coordinates.
+ * @ingroup Primitives */
+using IntCoord = int;
+
+/** @brief Default "acceptably small" value.
+ * @ingroup Primitives */
+constexpr Coord EPSILON = 1e-6;
+
+/** @brief Get a value representing infinity.
+ * @ingroup Primitives */
+inline constexpr Coord infinity() { return std::numeric_limits<Coord>::infinity(); }
+
+/** @brief Nearness predicate for values.
+ * @ingroup Primitives */
+inline constexpr bool are_near(Coord a, Coord b, double eps=EPSILON) { return std::abs(a-b) <= eps; }
+inline constexpr bool rel_error_bound(Coord a, Coord b, double eps=EPSILON) { return std::abs(a) <= eps*b; }
+
+/** @brief Numerically stable linear interpolation.
+ * @ingroup Primitives */
+inline constexpr Coord lerp(Coord t, Coord a, Coord b) {
+    return (1 - t) * a + t * b;
+}
+
+/** @brief Traits class used with coordinate types.
+ * Defines point, interval and rectangle types for the given coordinate type.
+ * @ingroup Utilities */
+template <typename C>
+struct CoordTraits {
+    using PointType = D2<C>;
+    using IntervalType = GenericInterval<C>;
+    using OptIntervalType = GenericOptInterval<C>;
+    using RectType = GenericRect<C>;
+    using OptRectType = GenericOptRect<C>;
+
+    using IntervalOps =
+      boost::equality_comparable< IntervalType
+    , boost::orable< IntervalType
+      >>;
+
+    using RectOps =
+      boost::equality_comparable< RectType
+    , boost::orable< RectType
+    , boost::orable< RectType, OptRectType
+      >>>;
+};
+
+// NOTE: operator helpers for Rect and Interval are defined here.
+// This is to avoid increasing their size through multiple inheritance.
+
+template<>
+struct CoordTraits<IntCoord> {
+    using PointType = IntPoint;
+    using IntervalType = IntInterval;
+    using OptIntervalType = OptIntInterval;
+    using RectType = IntRect;
+    using OptRectType = OptIntRect;
+
+    using IntervalOps =
+      boost::equality_comparable< IntInterval
+    , boost::additive< IntInterval
+    , boost::additive< IntInterval, IntCoord
+    , boost::orable< IntInterval
+      >>>>;
+
+    using RectOps =
+      boost::equality_comparable< IntRect
+    , boost::orable< IntRect
+    , boost::orable< IntRect, OptIntRect
+    , boost::additive< IntRect, IntPoint
+      >>>>;
+};
+
+template<>
+struct CoordTraits<Coord> {
+    using PointType = Point;
+    using IntervalType = Interval;
+    using OptIntervalType = OptInterval;
+    using RectType = Rect;
+    using OptRectType = OptRect;
+
+    using IntervalOps =
+      boost::equality_comparable< Interval
+    , boost::equality_comparable< Interval, IntInterval
+    , boost::additive< Interval
+    , boost::multipliable< Interval
+    , boost::orable< Interval
+    , boost::arithmetic< Interval, Coord
+      >>>>>>;
+
+    using RectOps =
+      boost::equality_comparable< Rect
+    , boost::equality_comparable< Rect, IntRect
+    , boost::orable< Rect
+    , boost::orable< Rect, OptRect
+    , boost::additive< Rect, Point
+    , boost::multipliable< Rect, Affine
+      >>>>>>;
+};
+
+/** @brief Convert coordinate to shortest possible string.
+ * @return The shortest string that parses back to the original value.
+ * @relates Coord */
+std::string format_coord_shortest(Coord x);
+
+/** @brief Convert coordinate to human-readable string.
+ * Unlike format_coord_shortest, this function will not omit a leading zero
+ * before a decimal point or use small negative exponents. The output format
+ * is similar to Javascript functions.
+ * @relates Coord */
+std::string format_coord_nice(Coord x);
+
+/** @brief Parse coordinate string.
+ * When using this function in conjunction with format_coord_shortest()
+ * or format_coord_nice(), the value is guaranteed to be preserved exactly.
+ * @relates Coord */
+Coord parse_coord(std::string const &s);
+
+} // namespace Geom
+
+#endif // LIB2GEOM_SEEN_COORD_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/crossing.h b/include/2geom/crossing.h
new file mode 100644
index 0000000..7ca273b
--- /dev/null
+++ b/include/2geom/crossing.h
@@ -0,0 +1,213 @@
+/**
+ * @file
+ * @brief Structure representing the intersection of two curves
+ *//*
+ * Authors:
+ *   Michael Sloan <mgsloan@gmail.com>
+ *   Marco Cecchetti <mrcekets at gmail.com>
+ * 
+ * Copyright 2006-2008  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_CROSSING_H
+#define LIB2GEOM_SEEN_CROSSING_H
+
+#include <vector>
+#include <2geom/rect.h>
+#include <2geom/sweep-bounds.h>
+#include <optional>
+#include <2geom/pathvector.h>
+
+namespace Geom {
+
+//Crossing between one or two paths
+struct Crossing {
+    bool dir; //True: along a, a becomes outside.
+    double ta, tb;  //time on a and b of crossing
+    unsigned a, b;  //storage of indices
+    Crossing() : dir(false), ta(0), tb(1), a(0), b(1) {}
+    Crossing(double t_a, double t_b, bool direction) : dir(direction), ta(t_a), tb(t_b), a(0), b(1) {}
+    Crossing(double t_a, double t_b, unsigned ai, unsigned bi, bool direction) : dir(direction), ta(t_a), tb(t_b), a(ai), b(bi) {}
+    bool operator==(const Crossing & other) const { return a == other.a && b == other.b && dir == other.dir && ta == other.ta && tb == other.tb; }
+    bool operator!=(const Crossing & other) const { return !(*this == other); }
+
+    unsigned getOther(unsigned cur) const { return a == cur ? b : a; }
+    double getTime(unsigned cur) const { return a == cur ? ta : tb; }
+    double getOtherTime(unsigned cur) const { return a == cur ? tb : ta; }
+    bool onIx(unsigned ix) const { return a == ix || b == ix; }
+};
+
+typedef std::optional<Crossing> OptCrossing;
+
+
+/*
+struct Edge {
+    unsigned node, path;
+    double time;
+    bool reverse;
+    Edge(unsigned p, double t, bool r) : path(p), time(t), reverse(r) {}
+    bool operator==(Edge const &other) const { return other.path == path && other.time == time && other.reverse == reverse; }
+};
+
+struct CrossingNode {
+    std::vector<Edge> edges;
+    CrossingNode() : edges(std::vector<Edge>()) {}
+    explicit CrossingNode(std::vector<Edge> es) : edges(es) {}
+    void add_edge(Edge const &e) {
+    	if(std::find(edges.begin(), edges.end(), e) == edges.end())
+    	    edges.push_back(e);
+    }
+    double time_on(unsigned p) {
+    	for(unsigned i = 0; i < edges.size(); i++)
+    	    if(edges[i].path == p) return edges[i].time;
+    	std::cout << "CrossingNode time_on failed\n";
+    	return 0;
+    }
+};
+
+
+typedef std::vector<CrossingNode> CrossingGraph;
+
+struct TimeOrder {
+    bool operator()(Edge a, Edge b) {
+        return a.time < b.time;
+    }
+};
+
+class Path;
+CrossingGraph create_crossing_graph(PathVector const &p, Crossings const &crs);
+*/
+
+/*inline bool are_near(Crossing a, Crossing b) {
+    return are_near(a.ta, b.ta) && are_near(a.tb, b.tb);
+}
+
+struct NearF { bool operator()(Crossing a, Crossing b) { return are_near(a, b); } };
+*/
+
+struct CrossingOrder {
+    unsigned ix;
+    bool rev;
+    CrossingOrder(unsigned i, bool r = false) : ix(i), rev(r) {}
+    bool operator()(Crossing a, Crossing b) {
+        if(rev) 
+            return (ix == a.a ? a.ta : a.tb) <
+                   (ix == b.a ? b.ta : b.tb);
+        else
+            return (ix == a.a ? a.ta : a.tb) >
+                   (ix == b.a ? b.ta : b.tb);
+    }
+};
+
+typedef std::vector<Crossing> Crossings;
+
+typedef std::vector<Crossings> CrossingSet;
+
+template<typename C>
+std::vector<Rect> bounds(C const &a) {
+    std::vector<Rect> rs;
+    for (unsigned i = 0; i < a.size(); i++) {
+        OptRect bb = a[i].boundsFast();
+        if (bb) {
+            rs.push_back(*bb);
+        }
+    }
+    return rs;
+}
+// provide specific method for Paths because paths can be closed or open. Path::size() is named somewhat wrong...
+std::vector<Rect> bounds(Path const &a);
+
+inline void sort_crossings(Crossings &cr, unsigned ix) { std::sort(cr.begin(), cr.end(), CrossingOrder(ix)); }
+
+template <typename T>
+struct CrossingTraits {
+    typedef std::vector<T> VectorT;
+    static inline VectorT init(T const &x) { return VectorT(1, x); }
+};
+template <>
+struct CrossingTraits<Path> {
+    typedef PathVector VectorT;
+    static inline VectorT vector_one(Path const &x) { return VectorT(x); }
+};
+
+template<typename T>
+struct Crosser {
+    typedef typename CrossingTraits<T>::VectorT VectorT;
+    virtual ~Crosser() {}
+    virtual Crossings crossings(T const &a, T const &b) {
+        return crossings(CrossingTraits<T>::vector_one(a), CrossingTraits<T>::vector_one(b))[0]; }
+    virtual CrossingSet crossings(VectorT const &a, VectorT const &b) {
+        CrossingSet results(a.size() + b.size(), Crossings());
+    
+        std::vector<std::vector<unsigned> > cull = sweep_bounds(bounds(a), bounds(b));
+        for(unsigned i = 0; i < cull.size(); i++) {
+            for(unsigned jx = 0; jx < cull[i].size(); jx++) {
+                unsigned j = cull[i][jx];
+                unsigned jc = j + a.size();
+                Crossings cr = crossings(a[i], b[j]);
+                for(auto & k : cr) { k.a = i; k.b = jc; }
+                
+                //Sort & add A-sorted crossings
+                sort_crossings(cr, i);
+                Crossings n(results[i].size() + cr.size());
+                std::merge(results[i].begin(), results[i].end(), cr.begin(), cr.end(), n.begin(), CrossingOrder(i));
+                results[i] = n;
+                
+                //Sort & add B-sorted crossings
+                sort_crossings(cr, jc);
+                n.resize(results[jc].size() + cr.size());
+                std::merge(results[jc].begin(), results[jc].end(), cr.begin(), cr.end(), n.begin(), CrossingOrder(jc));
+                results[jc] = n;
+            }
+        }
+        return results;
+    }
+};
+void merge_crossings(Crossings &a, Crossings &b, unsigned i);
+void offset_crossings(Crossings &cr, double a, double b);
+
+Crossings reverse_ta(Crossings const &cr, std::vector<double> max);
+Crossings reverse_tb(Crossings const &cr, unsigned split, std::vector<double> max);
+CrossingSet reverse_ta(CrossingSet const &cr, unsigned split, std::vector<double> max);
+CrossingSet reverse_tb(CrossingSet const &cr, unsigned split, std::vector<double> max);
+
+void clean(Crossings &cr_a, Crossings &cr_b);
+void delete_duplicates(Crossings &crs);
+
+} // end namespace Geom
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/curve.h b/include/2geom/curve.h
new file mode 100644
index 0000000..9519144
--- /dev/null
+++ b/include/2geom/curve.h
@@ -0,0 +1,375 @@
+/**
+ * \file
+ * \brief Abstract curve type
+ *
+ *//*
+ * Authors:
+ *   MenTaLguY <mental@rydia.net>
+ *   Marco Cecchetti <mrcekets at gmail.com>
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * 
+ * Copyright 2007-2009 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+
+#ifndef LIB2GEOM_SEEN_CURVE_H
+#define LIB2GEOM_SEEN_CURVE_H
+
+#include <vector>
+#include <boost/operators.hpp>
+#include <2geom/coord.h>
+#include <2geom/point.h>
+#include <2geom/interval.h>
+#include <2geom/sbasis.h>
+#include <2geom/d2.h>
+#include <2geom/affine.h>
+#include <2geom/intersection.h>
+
+namespace Geom {
+
+class PathSink;
+typedef Intersection<> CurveIntersection;
+
+/**
+ * @brief Abstract continuous curve on a plane defined on [0,1].
+ *
+ * Formally, a curve in 2Geom is defined as a function
+ * \f$\mathbf{C}: [0, 1] \to \mathbb{R}^2\f$
+ * (a function that maps the unit interval to points on a 2D plane). Its image (the set of points
+ * the curve passes through) will be denoted \f$\mathcal{C} = \mathbf{C}[ [0, 1] ]\f$.
+ * All curve types available in 2Geom are continuous and differentiable on their
+ * interior, e.g. \f$(0, 1)\f$. Sometimes the curve's image (value set) is referred to as the curve
+ * itself for simplicity, but keep in mind that it's not strictly correct.
+ * 
+ * It is common to think of the parameter as time. The curve can then be interpreted as
+ * describing the position of some moving object from time \f$t=0\f$ to \f$t=1\f$.
+ * Because of this, the parameter is frequently called the time value.
+ *
+ * Some methods return pointers to newly allocated curves. They are expected to be freed
+ * by the caller when no longer used. Default implementations are provided for some methods.
+ *
+ * @ingroup Curves
+ */
+class Curve
+     : boost::equality_comparable<Curve>
+{
+public:
+    virtual ~Curve() {}
+
+    /// @name Evaluate the curve
+    /// @{
+    /** @brief Retrieve the start of the curve.
+    * @return The point corresponding to \f$\mathbf{C}(0)\f$. */
+    virtual Point initialPoint() const = 0;
+
+    /** Retrieve the end of the curve.
+     * @return The point corresponding to \f$\mathbf{C}(1)\f$. */
+    virtual Point finalPoint() const = 0;
+
+    /** @brief Check whether the curve has exactly zero length.
+     * @return True if the curve's initial point is exactly the same as its final point, and it contains
+     *         no other points (its value set contains only one element). */
+    virtual bool isDegenerate() const = 0;
+
+    /// Check whether the curve is a line segment.
+    virtual bool isLineSegment() const { return false; }
+
+    /** @brief Get the interval of allowed time values.
+     * @return \f$[0, 1]\f$ */
+    virtual Interval timeRange() const {
+        Interval tr(0, 1);
+        return tr;
+    }
+
+    /** @brief Evaluate the curve at a specified time value.
+     * @param t Time value
+     * @return \f$\mathbf{C}(t)\f$ */
+    virtual Point pointAt(Coord t) const { return pointAndDerivatives(t, 0).front(); }
+
+    /** @brief Evaluate one of the coordinates at the specified time value.
+     * @param t Time value
+     * @param d The dimension to evaluate
+     * @return The specified coordinate of \f$\mathbf{C}(t)\f$ */
+    virtual Coord valueAt(Coord t, Dim2 d) const { return pointAt(t)[d]; }
+
+    /** @brief Evaluate the function at the specified time value. Allows curves to be used
+     * as functors. */
+    virtual Point operator() (Coord t)  const { return pointAt(t); }
+
+    /** @brief Evaluate the curve and its derivatives.
+     * This will return a vector that contains the value of the curve and the specified number
+     * of derivatives. However, the returned vector might contain less elements than specified
+     * if some derivatives do not exist.
+     * @param t Time value
+     * @param n The number of derivatives to compute
+     * @return Vector of at most \f$n+1\f$ elements of the form \f$[\mathbf{C}(t),
+        \mathbf{C}'(t), \mathbf{C}''(t), \ldots]\f$ */
+    virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const = 0;
+    /// @}
+
+    /// @name Change the curve's endpoints
+    /// @{
+    /** @brief Change the starting point of the curve.
+     * After calling this method, it is guaranteed that \f$\mathbf{C}(0) = \mathbf{p}\f$,
+     * and the curve is still continuous. The precise new shape of the curve varies with curve
+     * type.
+     * @param p New starting point of the curve */
+    virtual void setInitial(Point const &v) = 0;
+
+    /** @brief Change the ending point of the curve.
+     * After calling this method, it is guaranteed that \f$\mathbf{C}(0) = \mathbf{p}\f$,
+     * and the curve is still continuous. The precise new shape of the curve varies
+     * with curve type.
+     * @param p New ending point of the curve */
+    virtual void setFinal(Point const &v) = 0;
+    /// @}
+
+    /// @name Compute the bounding box
+    /// @{
+    /** @brief Quickly compute the curve's approximate bounding box.
+     * The resulting rectangle is guaranteed to contain all points belonging to the curve,
+     * but it might not be the smallest such rectangle. This method is usually fast.
+     * @return A rectangle that contains all points belonging to the curve. */
+    virtual Rect boundsFast() const = 0;
+
+    /** @brief Compute the curve's exact bounding box.
+     * This method can be dramatically slower than boundsFast() depending on the curve type.
+     * @return The smallest possible rectangle containing all of the curve's points. */
+    virtual Rect boundsExact() const = 0;
+
+    /** @brief Expand the given rectangle to include the transformed curve,
+     * assuming it already contains its initial point.
+     * @param bbox[in,out] bbox The rectangle to expand; it is assumed to already contain (initialPoint() * transform).
+     * @param transform The transform to apply to the curve before taking its bounding box. */
+    virtual void expandToTransformed(Rect &bbox, Affine const &transform) const = 0;
+
+    // I have no idea what the 'deg' parameter is for, so this is undocumented for now.
+    virtual OptRect boundsLocal(OptInterval const &i, unsigned deg) const = 0;
+
+    /** @brief Compute the bounding box of a part of the curve.
+     * Since this method returns the smallest possible bounding rectangle of the specified portion,
+     * it can also be rather slow.
+     * @param a An interval specifying a part of the curve, or nothing.
+     *          If \f$[0, 1] \subseteq a\f$, then the bounding box for the entire curve
+     *          is calculated.
+     * @return The smallest possible rectangle containing all points in \f$\mathbf{C}[a]\f$,
+     *         or nothing if the supplied interval is empty. */
+    OptRect boundsLocal(OptInterval const &a) const { return boundsLocal(a, 0); }
+    /// @}
+
+    /// @name Create new curves based on this one
+    /// @{
+    /** @brief Create an exact copy of this curve.
+     * @return Pointer to a newly allocated curve, identical to the original */
+    virtual Curve *duplicate() const = 0;
+
+    /** @brief Transform this curve by an affine transformation.
+     * Because of this method, all curve types must be closed under affine
+     * transformations.
+     * @param m Affine describing the affine transformation */
+    void transform(Affine const &m) {
+         *this *= m;
+    }
+
+    virtual void operator*=(Translate const &tr) { *this *= Affine(tr); }
+    virtual void operator*=(Scale const &s) { *this *= Affine(s); }
+    virtual void operator*=(Rotate const &r) { *this *= Affine(r); }
+    virtual void operator*=(HShear const &hs) { *this *= Affine(hs); }
+    virtual void operator*=(VShear const &vs) { *this *= Affine(vs); }
+    virtual void operator*=(Zoom const &z) { *this *= Affine(z); }
+    virtual void operator*=(Affine const &m) = 0;
+
+    /** @brief Create a curve transformed by an affine transformation.
+     * This method returns a new curve instead modifying the existing one.
+     * @param m Affine describing the affine transformation
+     * @return Pointer to a new, transformed curve */
+    virtual Curve *transformed(Affine const &m) const {
+         Curve *ret = duplicate();
+         ret->transform(m);
+         return ret;
+    }
+
+    /** @brief Create a curve that corresponds to a part of this curve.
+     * For \f$a > b\f$, the returned portion will be reversed with respect to the original.
+     * The returned curve will always be of the same type.
+     * @param a Beginning of the interval specifying the portion of the curve
+     * @param b End of the interval
+     * @return New curve \f$\mathbf{D}\f$ such that:
+     * - \f$\mathbf{D}(0) = \mathbf{C}(a)\f$
+     * - \f$\mathbf{D}(1) = \mathbf{C}(b)\f$
+     * - \f$\mathbf{D}[ [0, 1] ] = \mathbf{C}[ [a?b] ]\f$,
+     *   where \f$[a?b] = [\min(a, b), \max(a, b)]\f$ */
+    virtual Curve *portion(Coord a, Coord b) const = 0;
+
+    /** @brief A version of that accepts an Interval. */
+    Curve *portion(Interval const &i) const { return portion(i.min(), i.max()); }
+
+    /** @brief Create a reversed version of this curve.
+     * The result corresponds to <code>portion(1, 0)</code>, but this method might be faster.
+     * @return Pointer to a new curve \f$\mathbf{D}\f$ such that
+     *         \f$\forall_{x \in [0, 1]} \mathbf{D}(x) = \mathbf{C}(1-x)\f$ */
+    virtual Curve *reverse() const { return portion(1, 0); }
+
+    /** @brief Create a derivative of this curve.
+     * It's best to think of the derivative in physical terms: if the curve describes
+     * the position of some object on the plane from time \f$t=0\f$ to \f$t=1\f$ as said in the
+     * introduction, then the curve's derivative describes that object's speed at the same times.
+     * The second derivative refers to its acceleration, the third to jerk, etc.
+     * @return New curve \f$\mathbf{D} = \mathbf{C}'\f$. */
+    virtual Curve *derivative() const = 0;
+    /// @}
+
+    /// @name Advanced operations
+    /// @{
+    /** @brief Compute a time value at which the curve comes closest to a specified point.
+     * The first value with the smallest distance is returned if there are multiple such points.
+     * @param p Query point
+     * @param a Minimum time value to consider
+     * @param b Maximum time value to consider; \f$a < b\f$
+     * @return \f$q \in [a, b]: ||\mathbf{C}(q) - \mathbf{p}|| = 
+               \inf(\{r \in \mathbb{R} : ||\mathbf{C}(r) - \mathbf{p}||\})\f$ */
+    virtual Coord nearestTime( Point const& p, Coord a = 0, Coord b = 1 ) const;
+
+    /** @brief A version that takes an Interval. */
+    Coord nearestTime(Point const &p, Interval const &i) const {
+        return nearestTime(p, i.min(), i.max());
+    }
+
+    /** @brief Compute time values at which the curve comes closest to a specified point.
+     * @param p Query point
+     * @param a Minimum time value to consider
+     * @param b Maximum time value to consider; \f$a < b\f$
+     * @return Vector of points closest and equally far away from the query point */
+    virtual std::vector<Coord> allNearestTimes( Point const& p, Coord from = 0,
+        Coord to = 1 ) const;
+
+    /** @brief A version that takes an Interval. */
+    std::vector<Coord> allNearestTimes(Point const &p, Interval const &i) {
+        return allNearestTimes(p, i.min(), i.max());
+    }
+
+    /** @brief Compute the arc length of this curve.
+     * For a curve \f$\mathbf{C}(t) = (C_x(t), C_y(t))\f$, arc length is defined for 2D curves as
+     * \f[ \ell = \int_{0}^{1} \sqrt { [C_x'(t)]^2 + [C_y'(t)]^2 }\, \text{d}t \f]
+     * In other words, we divide the curve into infinitely small linear segments
+     * and add together their lengths. Of course we can't subdivide the curve into
+     * infinitely many segments on a computer, so this method returns an approximation.
+     * Not that there is usually no closed form solution to such integrals, so this
+     * method might be slow.
+     * @param tolerance Maximum allowed error
+     * @return Total distance the curve's value travels on the plane when going from 0 to 1 */
+    virtual Coord length(Coord tolerance=0.01) const;
+
+    /** @brief Computes time values at which the curve intersects an axis-aligned line.
+     * @param v The coordinate of the line
+     * @param d Which axis the coordinate is on. X means a vertical line, Y a horizontal line. */
+    virtual std::vector<Coord> roots(Coord v, Dim2 d) const = 0;
+
+    /** @brief Compute the partial winding number of this curve.
+     * The partial winding number is equal to the difference between the number
+     * of roots at which the curve goes in the +Y direction and the number of roots
+     * at which the curve goes in the -Y direction. This method is mainly useful
+     * for implementing path winding calculation. It will ignore roots which
+     * are local maxima on the Y axis.
+     * @param p Point where the winding number should be determined
+     * @return Winding number contribution at p */
+    virtual int winding(Point const &p) const;
+
+    /// Compute intersections with another curve.
+    virtual std::vector<CurveIntersection> intersect(Curve const &other, Coord eps = EPSILON) const;
+
+    /// Compute intersections of this curve with itself.
+    virtual std::vector<CurveIntersection> intersectSelf(Coord eps = EPSILON) const;
+
+    /** @brief Compute a vector tangent to the curve.
+     * This will return an unit vector (a Point with length() equal to 1) that denotes a vector
+     * tangent to the curve. This vector is defined as
+     * \f$ \mathbf{v}(t) = \frac{\mathbf{C}'(t)}{||\mathbf{C}'(t)||} \f$. It is pointed
+     * in the direction of increasing \f$t\f$, at the specified time value. The method uses
+     * l'Hopital's rule when the derivative is zero. A zero vector is returned if no non-zero
+     * derivative could be found.
+     * @param t Time value
+     * @param n The maximum order of derivative to consider
+     * @return Unit tangent vector \f$\mathbf{v}(t)\f$ */
+    virtual Point unitTangentAt(Coord t, unsigned n = 3) const;
+
+    /** @brief Convert the curve to a symmetric power basis polynomial.
+     * Symmetric power basis polynomials (S-basis for short) are numerical representations
+     * of curves with excellent numerical properties. Most high level operations provided by 2Geom
+     * are implemented in terms of S-basis operations, so every curve has to provide a method
+     * to convert it to an S-basis polynomial on two variables. See SBasis class reference
+     * for more information. */
+    virtual D2<SBasis> toSBasis() const = 0;
+    /// @}
+    
+    /// @name Miscellaneous
+    /// @{
+    /** Return the number of independent parameters required to represent all variations
+     * of this curve. For example, for Bezier curves it returns the curve's order
+     * multiplied by 2. */
+    virtual int degreesOfFreedom() const { return 0;}
+
+    /** @brief Test equality of two curves.
+     * Equality means that for any time value, the evaluation of either curve will yield
+     * the same value. This means non-degenerate curves are not equal to their reverses.
+     * Note that this tests for exact equality.
+     * @return True if the curves are identical, false otherwise */
+    virtual bool operator==(Curve const &c) const = 0;
+
+    /** @brief Test whether two curves are approximately the same. */
+    virtual bool isNear(Curve const &c, Coord precision) const = 0;
+
+    /** @brief Feed the curve to a PathSink */
+    virtual void feed(PathSink &sink, bool moveto_initial) const;
+    /// @}
+};
+
+inline
+Coord nearest_time(Point const& p, Curve const& c) {
+    return c.nearestTime(p);
+}
+
+// for make benefit glorious library of Boost Pointer Container
+inline
+Curve *new_clone(Curve const &c) {
+     return c.duplicate();
+}
+
+} // end namespace Geom
+
+
+#endif // _2GEOM_CURVE_H_
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/curves.h b/include/2geom/curves.h
new file mode 100644
index 0000000..46fb6d9
--- /dev/null
+++ b/include/2geom/curves.h
@@ -0,0 +1,54 @@
+/** @file
+ * @brief Include all curve types
+ *//*
+ * Authors:
+ *   MenTaLguY <mental@rydia.net>
+ *   Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 2007-2008  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_CURVES_H
+#define LIB2GEOM_SEEN_CURVES_H
+
+#include <2geom/curve.h>
+#include <2geom/sbasis-curve.h>
+#include <2geom/bezier-curve.h>
+#include <2geom/elliptical-arc.h>
+
+#endif // LIB2GEOM_SEEN_CURVES_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
+
diff --git a/include/2geom/d2.h b/include/2geom/d2.h
new file mode 100644
index 0000000..45f036b
--- /dev/null
+++ b/include/2geom/d2.h
@@ -0,0 +1,564 @@
+/**
+ * \file
+ * \brief Lifts one dimensional objects into 2D
+ *//*
+ * Authors:
+ *   Michael Sloan <mgsloan@gmail.com>
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright 2007-2015 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_D2_H
+#define LIB2GEOM_SEEN_D2_H
+
+#include <iterator>
+#include <boost/concept/assert.hpp>
+#include <boost/iterator/transform_iterator.hpp>
+#include <2geom/point.h>
+#include <2geom/interval.h>
+#include <2geom/affine.h>
+#include <2geom/rect.h>
+#include <2geom/concepts.h>
+
+namespace Geom {
+/**
+ * @brief Adaptor that creates 2D functions from 1D ones.
+ * @ingroup Fragments
+ */
+template <typename T>
+class D2
+{
+private:
+    T f[2];
+
+public:
+    typedef T D1Value;
+    typedef T &D1Reference;
+    typedef T const &D1ConstReference;
+
+    D2() {f[X] = f[Y] = T();}
+    explicit D2(Point const &a) {
+        f[X] = T(a[X]); f[Y] = T(a[Y]);
+    }
+
+    D2(T const &a, T const &b) {
+        f[X] = a;
+        f[Y] = b;
+    }
+
+    template <typename Iter>
+    D2(Iter first, Iter last) {
+        typedef typename std::iterator_traits<Iter>::value_type V;
+        typedef typename boost::transform_iterator<GetAxis<X,V>, Iter> XIter;
+        typedef typename boost::transform_iterator<GetAxis<Y,V>, Iter> YIter;
+
+        XIter xfirst(first, GetAxis<X,V>()), xlast(last, GetAxis<X,V>());
+        f[X] = T(xfirst, xlast);
+        YIter yfirst(first, GetAxis<Y,V>()), ylast(last, GetAxis<Y,V>());
+        f[Y] = T(yfirst, ylast);
+    }
+
+    D2(std::vector<Point> const &vec) {
+        typedef Point V;
+        typedef std::vector<Point>::const_iterator Iter;
+        typedef boost::transform_iterator<GetAxis<X,V>, Iter> XIter;
+        typedef boost::transform_iterator<GetAxis<Y,V>, Iter> YIter;
+
+        XIter xfirst(vec.begin(), GetAxis<X,V>()), xlast(vec.end(), GetAxis<X,V>());
+        f[X] = T(xfirst, xlast);
+        YIter yfirst(vec.begin(), GetAxis<Y,V>()), ylast(vec.end(), GetAxis<Y,V>());
+        f[Y] = T(yfirst, ylast);
+    }
+
+    //TODO: ask MenTaLguY about operator= as seen in Point
+
+    T& operator[](unsigned i)              { return f[i]; }
+    T const & operator[](unsigned i) const { return f[i]; }
+    Point point(unsigned i) const {
+        Point ret(f[X][i], f[Y][i]);
+        return ret;
+    }
+
+    //IMPL: FragmentConcept
+    typedef Point output_type;
+    bool isZero(double eps=EPSILON) const {
+        BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+        return f[X].isZero(eps) && f[Y].isZero(eps);
+    }
+    bool isConstant(double eps=EPSILON) const {
+        BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+        return f[X].isConstant(eps) && f[Y].isConstant(eps);
+    }
+    bool isFinite() const {
+        BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+        return f[X].isFinite() && f[Y].isFinite();
+    }
+    Point at0() const {
+        BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+        return Point(f[X].at0(), f[Y].at0());
+    }
+    Point at1() const {
+        BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+        return Point(f[X].at1(), f[Y].at1());
+    }
+    Point pointAt(double t) const {
+        BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+        return (*this)(t);
+    }
+    Point valueAt(double t) const {
+        // TODO: remove this alias
+        BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+        return (*this)(t);
+    }
+    std::vector<Point > valueAndDerivatives(double t, unsigned n) const {
+        BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+        std::vector<Coord> x = f[X].valueAndDerivatives(t, n),
+                           y = f[Y].valueAndDerivatives(t, n); // always returns a vector of size n+1
+        std::vector<Point> res(n+1);
+        for(unsigned i = 0; i <= n; i++) {
+            res[i] = Point(x[i], y[i]);
+        }
+        return res;
+    }
+    D2<SBasis> toSBasis() const {
+        BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+        return D2<SBasis>(f[X].toSBasis(), f[Y].toSBasis());
+    }
+
+    Point operator()(double t) const;
+    Point operator()(double x, double y) const;
+};
+template <typename T>
+inline D2<T> reverse(const D2<T> &a) {
+    BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+    return D2<T>(reverse(a[X]), reverse(a[Y]));
+}
+
+template <typename T>
+inline D2<T> portion(const D2<T> &a, Coord f, Coord t) {
+    BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+    return D2<T>(portion(a[X], f, t), portion(a[Y], f, t));
+}
+
+template <typename T>
+inline D2<T> portion(const D2<T> &a, Interval i) {
+    BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+    return D2<T>(portion(a[X], i), portion(a[Y], i));
+}
+
+//IMPL: EqualityComparableConcept
+template <typename T>
+inline bool
+operator==(D2<T> const &a, D2<T> const &b) {
+    BOOST_CONCEPT_ASSERT((EqualityComparableConcept<T>));
+    return a[0]==b[0] && a[1]==b[1];
+}
+template <typename T>
+inline bool
+operator!=(D2<T> const &a, D2<T> const &b) {
+    BOOST_CONCEPT_ASSERT((EqualityComparableConcept<T>));
+    return a[0]!=b[0] || a[1]!=b[1];
+}
+
+//IMPL: NearConcept
+template <typename T>
+inline bool
+are_near(D2<T> const &a, D2<T> const &b, double tol) {
+    BOOST_CONCEPT_ASSERT((NearConcept<T>));
+    return are_near(a[0], b[0], tol) && are_near(a[1], b[1], tol);
+}
+
+//IMPL: AddableConcept
+template <typename T>
+inline D2<T>
+operator+(D2<T> const &a, D2<T> const &b) {
+    BOOST_CONCEPT_ASSERT((AddableConcept<T>));
+
+    D2<T> r;
+    for(unsigned i = 0; i < 2; i++)
+        r[i] = a[i] + b[i];
+    return r;
+}
+template <typename T>
+inline D2<T>
+operator-(D2<T> const &a, D2<T> const &b) {
+    BOOST_CONCEPT_ASSERT((AddableConcept<T>));
+
+    D2<T> r;
+    for(unsigned i = 0; i < 2; i++)
+        r[i] = a[i] - b[i];
+    return r;
+}
+template <typename T>
+inline D2<T>
+operator+=(D2<T> &a, D2<T> const &b) {
+    BOOST_CONCEPT_ASSERT((AddableConcept<T>));
+
+    for(unsigned i = 0; i < 2; i++)
+        a[i] += b[i];
+    return a;
+}
+template <typename T>
+inline D2<T>
+operator-=(D2<T> &a, D2<T> const & b) {
+    BOOST_CONCEPT_ASSERT((AddableConcept<T>));
+
+    for(unsigned i = 0; i < 2; i++)
+        a[i] -= b[i];
+    return a;
+}
+
+//IMPL: ScalableConcept
+template <typename T>
+inline D2<T>
+operator-(D2<T> const & a) {
+    BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
+    D2<T> r;
+    for(unsigned i = 0; i < 2; i++)
+        r[i] = -a[i];
+    return r;
+}
+template <typename T>
+inline D2<T>
+operator*(D2<T> const & a, Point const & b) {
+    BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
+
+    D2<T> r;
+    for(unsigned i = 0; i < 2; i++)
+        r[i] = a[i] * b[i];
+    return r;
+}
+template <typename T>
+inline D2<T>
+operator/(D2<T> const & a, Point const & b) {
+    BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
+    //TODO: b==0?
+    D2<T> r;
+    for(unsigned i = 0; i < 2; i++)
+        r[i] = a[i] / b[i];
+    return r;
+}
+template <typename T>
+inline D2<T>
+operator*=(D2<T> &a, Point const & b) {
+    BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
+
+    for(unsigned i = 0; i < 2; i++)
+        a[i] *= b[i];
+    return a;
+}
+template <typename T>
+inline D2<T>
+operator/=(D2<T> &a, Point const & b) {
+    BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
+    //TODO: b==0?
+    for(unsigned i = 0; i < 2; i++)
+        a[i] /= b[i];
+    return a;
+}
+
+template <typename T>
+inline D2<T> operator*(D2<T> const & a, double b) { return D2<T>(a[0]*b, a[1]*b); }
+template <typename T>
+inline D2<T> operator*=(D2<T> & a, double b) { a[0] *= b; a[1] *= b; return a; }
+template <typename T>
+inline D2<T> operator/(D2<T> const & a, double b) { return D2<T>(a[0]/b, a[1]/b); }
+template <typename T>
+inline D2<T> operator/=(D2<T> & a, double b) { a[0] /= b; a[1] /= b; return a; }
+
+template<typename T>
+D2<T> operator*(D2<T> const &v, Affine const &m) {
+    BOOST_CONCEPT_ASSERT((AddableConcept<T>));
+    BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
+    D2<T> ret;
+    for(unsigned i = 0; i < 2; i++)
+        ret[i] = v[X] * m[i] + v[Y] * m[i + 2] + m[i + 4];
+    return ret;
+}
+
+//IMPL: MultiplicableConcept
+template <typename T>
+inline D2<T>
+operator*(D2<T> const & a, T const & b) {
+    BOOST_CONCEPT_ASSERT((MultiplicableConcept<T>));
+    D2<T> ret;
+    for(unsigned i = 0; i < 2; i++)
+        ret[i] = a[i] * b;
+    return ret;
+}
+
+//IMPL:
+
+//IMPL: OffsetableConcept
+template <typename T>
+inline D2<T>
+operator+(D2<T> const & a, Point b) {
+    BOOST_CONCEPT_ASSERT((OffsetableConcept<T>));
+    D2<T> r;
+    for(unsigned i = 0; i < 2; i++)
+        r[i] = a[i] + b[i];
+    return r;
+}
+template <typename T>
+inline D2<T>
+operator-(D2<T> const & a, Point b) {
+    BOOST_CONCEPT_ASSERT((OffsetableConcept<T>));
+    D2<T> r;
+    for(unsigned i = 0; i < 2; i++)
+        r[i] = a[i] - b[i];
+    return r;
+}
+template <typename T>
+inline D2<T>
+operator+=(D2<T> & a, Point b) {
+    BOOST_CONCEPT_ASSERT((OffsetableConcept<T>));
+    for(unsigned i = 0; i < 2; i++)
+        a[i] += b[i];
+    return a;
+}
+template <typename T>
+inline D2<T>
+operator-=(D2<T> & a, Point b) {
+    BOOST_CONCEPT_ASSERT((OffsetableConcept<T>));
+    for(unsigned i = 0; i < 2; i++)
+        a[i] -= b[i];
+    return a;
+}
+
+template <typename T>
+inline T
+dot(D2<T> const & a, D2<T> const & b) {
+    BOOST_CONCEPT_ASSERT((AddableConcept<T>));
+    BOOST_CONCEPT_ASSERT((MultiplicableConcept<T>));
+
+    T r;
+    for(unsigned i = 0; i < 2; i++)
+        r += a[i] * b[i];
+    return r;
+}
+
+/** @brief Calculates the 'dot product' or 'inner product' of \c a and \c b
+ * @return \f$a \bullet b = a_X b_X + a_Y b_Y\f$.
+ * @relates D2 */
+template <typename T>
+inline T
+dot(D2<T> const & a, Point const & b) {
+    BOOST_CONCEPT_ASSERT((AddableConcept<T>));
+    BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
+
+    T r;
+    for(unsigned i = 0; i < 2; i++) {
+        r += a[i] * b[i];
+    }
+    return r;
+}
+
+/** @brief Calculates the 'cross product' or 'outer product' of \c a and \c b
+ * @return \f$a \times b = a_Y b_X - a_X b_Y\f$.
+ * @relates D2 */
+template <typename T>
+inline T
+cross(D2<T> const & a, D2<T> const & b) {
+    BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
+    BOOST_CONCEPT_ASSERT((MultiplicableConcept<T>));
+
+    return a[1] * b[0] - a[0] * b[1];
+}
+
+
+//equivalent to cw/ccw, for use in situations where rotation direction doesn't matter.
+template <typename T>
+inline D2<T>
+rot90(D2<T> const & a) {
+    BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
+    return D2<T>(-a[Y], a[X]);
+}
+
+//TODO: concepterize the following functions
+template <typename T>
+inline D2<T>
+compose(D2<T> const & a, T const & b) {
+    D2<T> r;
+    for(unsigned i = 0; i < 2; i++)
+        r[i] = compose(a[i],b);
+    return r;
+}
+
+template <typename T>
+inline D2<T>
+compose_each(D2<T> const & a, D2<T> const & b) {
+    D2<T> r;
+    for(unsigned i = 0; i < 2; i++)
+        r[i] = compose(a[i],b[i]);
+    return r;
+}
+
+template <typename T>
+inline D2<T>
+compose_each(T const & a, D2<T> const & b) {
+    D2<T> r;
+    for(unsigned i = 0; i < 2; i++)
+        r[i] = compose(a,b[i]);
+    return r;
+}
+
+
+template<typename T>
+inline Point
+D2<T>::operator()(double t) const {
+    Point p;
+    for(unsigned i = 0; i < 2; i++)
+       p[i] = (*this)[i](t);
+    return p;
+}
+
+//TODO: we might want to have this take a Point as the parameter.
+template<typename T>
+inline Point
+D2<T>::operator()(double x, double y) const {
+    Point p;
+    for(unsigned i = 0; i < 2; i++)
+       p[i] = (*this)[i](x, y);
+    return p;
+}
+
+
+template<typename T>
+D2<T> derivative(D2<T> const & a) {
+    return D2<T>(derivative(a[X]), derivative(a[Y]));
+}
+template<typename T>
+D2<T> integral(D2<T> const & a) {
+    return D2<T>(integral(a[X]), integral(a[Y]));
+}
+
+/** A function to print out the Point.  It just prints out the coords
+    on the given output stream */
+template <typename T>
+inline std::ostream &operator<< (std::ostream &out_file, const Geom::D2<T> &in_d2) {
+    out_file << "X: " << in_d2[X] << "  Y: " << in_d2[Y];
+    return out_file;
+}
+
+//Some D2 Fragment implementation which requires rect:
+template <typename T>
+OptRect bounds_fast(const D2<T> &a) {
+    BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+    return OptRect(bounds_fast(a[X]), bounds_fast(a[Y]));
+}
+template <typename T>
+OptRect bounds_exact(const D2<T> &a) {
+    BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+    return OptRect(bounds_exact(a[X]), bounds_exact(a[Y]));
+}
+template <typename T>
+OptRect bounds_local(const D2<T> &a, const OptInterval &t) {
+    BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+    return OptRect(bounds_local(a[X], t), bounds_local(a[Y], t));
+}
+
+
+
+// SBasis-specific declarations
+
+inline D2<SBasis> compose(D2<SBasis> const & a, SBasis const & b) {
+    return D2<SBasis>(compose(a[X], b), compose(a[Y], b));
+}
+
+SBasis L2(D2<SBasis> const & a, unsigned k);
+double L2(D2<double> const & a);
+
+D2<SBasis> multiply(Linear const & a, D2<SBasis> const & b);
+inline D2<SBasis> operator*(Linear const & a, D2<SBasis> const & b) { return multiply(a, b); }
+D2<SBasis> multiply(SBasis const & a, D2<SBasis> const & b);
+inline D2<SBasis> operator*(SBasis const & a, D2<SBasis> const & b) { return multiply(a, b); }
+D2<SBasis> truncate(D2<SBasis> const & a, unsigned terms);
+
+unsigned sbasis_size(D2<SBasis> const & a);
+double tail_error(D2<SBasis> const & a, unsigned tail);
+
+//Piecewise<D2<SBasis> > specific declarations
+
+Piecewise<D2<SBasis> > sectionize(D2<Piecewise<SBasis> > const &a);
+D2<Piecewise<SBasis> > make_cuts_independent(Piecewise<D2<SBasis> > const &a);
+Piecewise<D2<SBasis> > rot90(Piecewise<D2<SBasis> > const &a);
+Piecewise<SBasis> dot(Piecewise<D2<SBasis> > const &a, Piecewise<D2<SBasis> > const &b);
+Piecewise<SBasis> dot(Piecewise<D2<SBasis> > const &a, Point const &b);
+Piecewise<SBasis> cross(Piecewise<D2<SBasis> > const &a, Piecewise<D2<SBasis> > const &b);
+
+Piecewise<D2<SBasis> > operator*(Piecewise<D2<SBasis> > const &a, Affine const &m);
+
+Piecewise<D2<SBasis> > force_continuity(Piecewise<D2<SBasis> > const &f, double tol=0, bool closed=false);
+
+std::vector<Piecewise<D2<SBasis> > > fuse_nearby_ends(std::vector<Piecewise<D2<SBasis> > > const &f, double tol=0);
+
+std::vector<Geom::Piecewise<Geom::D2<Geom::SBasis> > > split_at_discontinuities (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwsbin, double tol = .0001);
+
+Point unitTangentAt(D2<SBasis> const & a, Coord t, unsigned n = 3);
+
+//bounds specializations with order
+inline OptRect bounds_fast(D2<SBasis> const & s, unsigned order=0) {
+    OptRect retval;
+    OptInterval xint = bounds_fast(s[X], order);
+    if (xint) {
+        OptInterval yint = bounds_fast(s[Y], order);
+        if (yint) {
+            retval = Rect(*xint, *yint);
+        }
+    }
+    return retval;
+}
+inline OptRect bounds_local(D2<SBasis> const & s, OptInterval i, unsigned order=0) {
+    OptRect retval;
+    OptInterval xint = bounds_local(s[X], i, order);
+    OptInterval yint = bounds_local(s[Y], i, order);
+    if (xint && yint) {
+        retval = Rect(*xint, *yint);
+    }
+    return retval;
+}
+
+std::vector<Interval> level_set( D2<SBasis> const &f, Rect region);
+std::vector<Interval> level_set( D2<SBasis> const &f, Point p, double tol);
+std::vector<std::vector<Interval> > level_sets( D2<SBasis> const &f, std::vector<Rect> regions);
+std::vector<std::vector<Interval> > level_sets( D2<SBasis> const &f, std::vector<Point> pts, double tol);
+
+
+} // end namespace Geom
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/ellipse.h b/include/2geom/ellipse.h
new file mode 100644
index 0000000..0d1567a
--- /dev/null
+++ b/include/2geom/ellipse.h
@@ -0,0 +1,260 @@
+/** @file
+ * @brief Ellipse shape
+ *//*
+ * Authors:
+ *   Marco Cecchetti <mrcekets at gmail.com>
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright 2008  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+
+#ifndef LIB2GEOM_SEEN_ELLIPSE_H
+#define LIB2GEOM_SEEN_ELLIPSE_H
+
+#include <vector>
+#include <2geom/angle.h>
+#include <2geom/bezier-curve.h>
+#include <2geom/exception.h>
+#include <2geom/forward.h>
+#include <2geom/line.h>
+#include <2geom/transforms.h>
+
+namespace Geom {
+
+class EllipticalArc;
+class Circle;
+
+/** @brief Set of points with a constant sum of distances from two foci.
+ *
+ * An ellipse can be specified in several ways. Internally, 2Geom uses
+ * the SVG style representation: center, rays and angle between the +X ray
+ * and the +X axis. Another popular way is to use an implicit equation,
+ * which is as follows:
+ * \f$Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0\f$
+ *
+ * @ingroup Shapes */
+class Ellipse
+    : boost::multipliable< Ellipse, Translate
+    , boost::multipliable< Ellipse, Scale
+    , boost::multipliable< Ellipse, Rotate
+    , boost::multipliable< Ellipse, Zoom
+    , boost::multipliable< Ellipse, Affine
+    , boost::equality_comparable< Ellipse
+      > > > > > >
+{
+    Point _center;
+    Point _rays;
+    Angle _angle;
+public:
+    Ellipse() {}
+    Ellipse(Point const &c, Point const &r, Coord angle)
+        : _center(c)
+        , _rays(r)
+        , _angle(angle)
+    {}
+    Ellipse(Coord cx, Coord cy, Coord rx, Coord ry, Coord angle)
+        : _center(cx, cy)
+        , _rays(rx, ry)
+        , _angle(angle)
+    {}
+    Ellipse(double A, double B, double C, double D, double E, double F) {
+        setCoefficients(A, B, C, D, E, F);
+    }
+    /// Construct ellipse from a circle.
+    Ellipse(Geom::Circle const &c);
+
+    /// Set center, rays and angle.
+    void set(Point const &c, Point const &r, Coord angle) {
+        _center = c;
+        _rays = r;
+        _angle = angle;
+    }
+    /// Set center, rays and angle as constituent values.
+    void set(Coord cx, Coord cy, Coord rx, Coord ry, Coord a) {
+        _center[X] = cx;
+        _center[Y] = cy;
+        _rays[X] = rx;
+        _rays[Y] = ry;
+        _angle = a;
+    }
+    /// Set an ellipse by solving its implicit equation.
+    void setCoefficients(double A, double B, double C, double D, double E, double F);
+    /// Set the center.
+    void setCenter(Point const &p) { _center = p; }
+    /// Set the center by coordinates.
+    void setCenter(Coord cx, Coord cy) { _center[X] = cx; _center[Y] = cy; }
+    /// Set both rays of the ellipse.
+    void setRays(Point const &p) { _rays = p; }
+    /// Set both rays of the ellipse as coordinates.
+    void setRays(Coord x, Coord y) { _rays[X] = x; _rays[Y] = y; }
+    /// Set one of the rays of the ellipse.
+    void setRay(Coord r, Dim2 d) { _rays[d] = r; }
+    /// Set the angle the X ray makes with the +X axis.
+    void setRotationAngle(Angle a) { _angle = a; }
+
+    Point center() const { return _center; }
+    Coord center(Dim2 d) const { return _center[d]; }
+    /// Get both rays as a point.
+    Point rays() const { return _rays; }
+    /// Get one ray of the ellipse.
+    Coord ray(Dim2 d) const { return _rays[d]; }
+    /// Get the angle the X ray makes with the +X axis.
+    Angle rotationAngle() const { return _angle; }
+    /// Get the point corresponding to the +X ray of the ellipse.
+    Point initialPoint() const;
+    /// Get the point corresponding to the +X ray of the ellipse.
+    Point finalPoint() const { return initialPoint(); }
+
+    /** @brief Create an ellipse passing through the specified points
+     * At least five points have to be specified. */
+    void fit(std::vector<Point> const& points);
+
+    /** @brief Create an elliptical arc from a section of the ellipse.
+     * This is mainly useful to determine the flags of the new arc.
+     * The passed points should lie on the ellipse, otherwise the results
+     * will be undefined.
+     * @param ip Initial point of the arc
+     * @param inner Point in the middle of the arc, used to pick one of two possibilities
+     * @param fp Final point of the arc
+     * @return Newly allocated arc, delete when no longer used */
+    EllipticalArc *arc(Point const &ip, Point const &inner, Point const &fp);
+
+    /** @brief Return an ellipse with less degrees of freedom.
+     * The canonical form always has the angle less than \f$\frac{\pi}{2}\f$,
+     * and zero if the rays are equal (i.e. the ellipse is a circle). */
+    Ellipse canonicalForm() const;
+    void makeCanonical();
+
+    /** @brief Compute the transform that maps the unit circle to this ellipse.
+     * Each ellipse can be interpreted as a translated, scaled and rotate unit circle.
+     * This function returns the transform that maps the unit circle to this ellipse.
+     * @return Transform from unit circle to the ellipse */
+    Affine unitCircleTransform() const;
+    /** @brief Compute the transform that maps this ellipse to the unit circle.
+     * This may be a little more precise and/or faster than simply using
+     * unitCircleTransform().inverse(). An exception will be thrown for
+     * degenerate ellipses. */
+    Affine inverseUnitCircleTransform() const;
+
+    LineSegment majorAxis() const { return ray(X) >= ray(Y) ? axis(X) : axis(Y); }
+    LineSegment minorAxis() const { return ray(X) < ray(Y) ? axis(X) : axis(Y); }
+    LineSegment semimajorAxis(int sign = 1) const {
+        return ray(X) >= ray(Y) ? semiaxis(X, sign) : semiaxis(Y, sign);
+    }
+    LineSegment semiminorAxis(int sign = 1) const {
+        return ray(X) < ray(Y) ? semiaxis(X, sign) : semiaxis(Y, sign);
+    }
+    LineSegment axis(Dim2 d) const;
+    LineSegment semiaxis(Dim2 d, int sign = 1) const;
+
+    /// Get the tight-fitting bounding box of the ellipse.
+    Rect boundsExact() const;
+
+    /** @brief Get a fast to compute bounding box which contains the ellipse.
+     *
+     * The returned rectangle engulfs the ellipse but it may not be the smallest
+     * axis-aligned rectangle with this property.
+     */
+    Rect boundsFast() const;
+
+    /// Get the coefficients of the ellipse's implicit equation.
+    std::vector<double> coefficients() const;
+    void coefficients(Coord &A, Coord &B, Coord &C, Coord &D, Coord &E, Coord &F) const;
+
+    /** @brief Evaluate a point on the ellipse.
+     * The parameter range is \f$[0, 2\pi)\f$; larger and smaller values
+     * wrap around. */
+    Point pointAt(Coord t) const;
+    /// Evaluate a single coordinate of a point on the ellipse.
+    Coord valueAt(Coord t, Dim2 d) const;
+
+    /** @brief Find the time value of a point on an ellipse.
+     * If the point is not on the ellipse, the returned time value will correspond
+     * to an intersection with a ray from the origin passing through the point
+     * with the ellipse. Note that this is NOT the nearest point on the ellipse. */
+    Coord timeAt(Point const &p) const;
+
+    /// Get the value of the derivative at time t normalized to unit length.
+    Point unitTangentAt(Coord t) const;
+
+    /// Check whether the ellipse contains the given point.
+    bool contains(Point const &p) const;
+
+    /// Compute intersections with an infinite line.
+    std::vector<ShapeIntersection> intersect(Line const &line) const;
+    /// Compute intersections with a line segment.
+    std::vector<ShapeIntersection> intersect(LineSegment const &seg) const;
+    /// Compute intersections with another ellipse.
+    std::vector<ShapeIntersection> intersect(Ellipse const &other) const;
+    /// Compute intersections with a 2D Bezier polynomial.
+    std::vector<ShapeIntersection> intersect(D2<Bezier> const &other) const;
+
+    Ellipse &operator*=(Translate const &t) {
+        _center *= t;
+        return *this;
+    }
+    Ellipse &operator*=(Scale const &s) {
+        _center *= s;
+        _rays *= s;
+        return *this;
+    }
+    Ellipse &operator*=(Zoom const &z) {
+        _center *= z;
+        _rays *= z.scale();
+        return *this;
+    }
+    Ellipse &operator*=(Rotate const &r);
+    Ellipse &operator*=(Affine const &m);
+
+    /// Compare ellipses for exact equality.
+    bool operator==(Ellipse const &other) const;
+};
+
+/** @brief Test whether two ellipses are approximately the same.
+ * This will check whether no point on ellipse a is further away from
+ * the corresponding point on ellipse b than precision.
+ * @relates Ellipse */
+bool are_near(Ellipse const &a, Ellipse const &b, Coord precision = EPSILON);
+
+/** @brief Outputs ellipse data, useful for debugging.
+ * @relates Ellipse */
+std::ostream &operator<<(std::ostream &out, Ellipse const &e);
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_ELLIPSE_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/elliptical-arc.h b/include/2geom/elliptical-arc.h
new file mode 100644
index 0000000..567e207
--- /dev/null
+++ b/include/2geom/elliptical-arc.h
@@ -0,0 +1,344 @@
+/**
+ * \file
+ * \brief  Elliptical arc curve
+ *
+ *//*
+ * Authors:
+ *    MenTaLguY <mental@rydia.net>
+ *    Marco Cecchetti <mrcekets at gmail.com>
+ *    Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * 
+ * Copyright 2007-2009 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_ELLIPTICAL_ARC_H
+#define LIB2GEOM_SEEN_ELLIPTICAL_ARC_H
+
+#include <algorithm>
+#include <2geom/affine.h>
+#include <2geom/angle.h>
+#include <2geom/bezier-curve.h>
+#include <2geom/curve.h>
+#include <2geom/ellipse.h>
+#include <2geom/sbasis-curve.h>  // for non-native methods
+#include <2geom/utils.h>
+
+namespace Geom 
+{
+
+class EllipticalArc : public Curve
+{
+public:
+    /** @brief Creates an arc with all variables set to zero. */
+    EllipticalArc()
+        : _initial_point(0,0)
+        , _final_point(0,0)
+        , _large_arc(false)
+    {}
+    /** @brief Create a new elliptical arc.
+     * @param ip Initial point of the arc
+     * @param r Rays of the ellipse as a point
+     * @param rot Angle of rotation of the X axis of the ellipse in radians
+     * @param large If true, the large arc is chosen (always >= 180 degrees), otherwise
+     *              the smaller arc is chosen
+     * @param sweep If true, the clockwise arc is chosen, otherwise the counter-clockwise
+     *              arc is chosen
+     * @param fp Final point of the arc */
+    EllipticalArc( Point const &ip, Point const &r,
+                   Coord rot_angle, bool large_arc, bool sweep,
+                   Point const &fp
+                 )
+        : _initial_point(ip)
+        , _final_point(fp)
+        , _ellipse(0, 0, r[X], r[Y], rot_angle)
+        , _angles(0, 0, sweep)
+        , _large_arc(large_arc)
+    {
+        _updateCenterAndAngles();
+    }
+
+    /// Create a new elliptical arc, giving the ellipse's rays as separate coordinates.
+    EllipticalArc( Point const &ip, Coord rx, Coord ry,
+                   Coord rot_angle, bool large_arc, bool sweep,
+                   Point const &fp
+                 )
+        : _initial_point(ip)
+        , _final_point(fp)
+        , _ellipse(0, 0, rx, ry, rot_angle)
+        , _angles(0, 0, sweep)
+        , _large_arc(large_arc)
+    {
+        _updateCenterAndAngles();
+    }
+
+    /// @name Retrieve basic information
+    /// @{
+
+    /** @brief Get a coordinate of the elliptical arc's center.
+     * @param d The dimension to retrieve
+     * @return The selected coordinate of the center */
+    Coord center(Dim2 d) const { return _ellipse.center(d); }
+
+    /** @brief Get the arc's center
+     * @return The arc's center, situated on the intersection of the ellipse's rays */
+    Point center() const { return _ellipse.center(); }
+
+    /** @brief Get one of the ellipse's rays
+     * @param d Dimension to retrieve
+     * @return The selected ray of the ellipse */
+    Coord ray(Dim2 d) const { return _ellipse.ray(d); }
+
+    /** @brief Get both rays as a point
+     * @return Point with X equal to the X ray and Y to Y ray */
+    Point rays() const { return _ellipse.rays(); }
+
+    /** @brief Get the defining ellipse's rotation
+     * @return Angle between the +X ray of the ellipse and the +X axis */
+    Angle rotationAngle() const {
+        return _ellipse.rotationAngle();
+    }
+
+    /** @brief Whether the arc is larger than half an ellipse.
+     * @return True if the arc is larger than \f$\pi\f$, false otherwise */
+    bool largeArc() const { return _large_arc; }
+
+    /** @brief Whether the arc turns clockwise
+     * @return True if the arc makes a clockwise turn when going from initial to final
+     *         point, false otherwise */
+    bool sweep() const { return _angles.sweep(); }
+
+    Angle initialAngle() const { return _angles.initialAngle(); }
+    Angle finalAngle() const { return _angles.finalAngle(); }
+    /// @}
+
+    /// @name Modify parameters
+    /// @{
+
+    /// Change all of the arc's parameters.
+    void set( Point const &ip, double rx, double ry,
+              double rot_angle, bool large_arc, bool sweep,
+              Point const &fp
+            )
+    {
+        _initial_point = ip;
+        _final_point = fp;
+        _ellipse.setRays(rx, ry);
+        _ellipse.setRotationAngle(rot_angle);
+        _angles.setSweep(sweep);
+        _large_arc = large_arc;
+        _updateCenterAndAngles();
+    }
+
+    /// Change all of the arc's parameters.
+    void set( Point const &ip, Point const &r,
+              Angle rot_angle, bool large_arc, bool sweep,
+              Point const &fp
+            )
+    {
+        _initial_point = ip;
+        _final_point = fp;
+        _ellipse.setRays(r);
+        _ellipse.setRotationAngle(rot_angle);
+        _angles.setSweep(sweep);
+        _large_arc = large_arc;
+        _updateCenterAndAngles();
+    }
+
+    /** @brief Change the initial and final point in one operation.
+     * This method exists because modifying any of the endpoints causes rather costly
+     * recalculations of the center and extreme angles.
+     * @param ip New initial point
+     * @param fp New final point */
+    void setEndpoints(Point const &ip, Point const &fp) {
+        _initial_point = ip;
+        _final_point = fp;
+        _updateCenterAndAngles();
+    }
+    /// @}
+
+    /// @name Evaluate the arc as a function
+    /// @{
+    /** Check whether the arc contains the given angle
+     * @param t The angle to check
+     * @return True if the arc contains the angle, false otherwise */
+    bool containsAngle(Angle angle) const { return _angles.contains(angle); }
+
+    /** @brief Evaluate the arc at the specified angular coordinate
+     * @param t Angle
+     * @return Point corresponding to the given angle */
+    Point pointAtAngle(Coord t) const;
+
+    /** @brief Evaluate one of the arc's coordinates at the specified angle
+     * @param t Angle
+     * @param d The dimension to retrieve
+     * @return Selected coordinate of the arc at the specified angle */
+    Coord valueAtAngle(Coord t, Dim2 d) const;
+
+    /// Compute the curve time value corresponding to the given angular value.
+    Coord timeAtAngle(Angle a) const { return _angles.timeAtAngle(a); }
+
+    /// Compute the angular domain value corresponding to the given time value.
+    Angle angleAt(Coord t) const { return _angles.angleAt(t); }
+
+    /** @brief Compute the amount by which the angle parameter changes going from start to end.
+     * This has range \f$(-2\pi, 2\pi)\f$ and thus cannot be represented as instance
+     * of the class Angle. Add this to the initial angle to obtain the final angle. */
+    Coord sweepAngle() const { return _angles.sweepAngle(); }
+
+    /** @brief Get the elliptical angle spanned by the arc.
+     * This is basically the absolute value of sweepAngle(). */
+    Coord angularExtent() const { return _angles.extent(); }
+
+    /// Get the angular interval of the arc.
+    AngleInterval angularInterval() const { return _angles; }
+
+    /// Evaluate the arc in the curve domain, i.e. \f$[0, 1]\f$.
+    Point pointAt(Coord t) const override;
+
+    /// Evaluate a single coordinate on the arc in the curve domain.
+    Coord valueAt(Coord t, Dim2 d) const override;
+
+    /** @brief Compute a transform that maps the unit circle to the arc's ellipse.
+     * Each ellipse can be interpreted as a translated, scaled and rotate unit circle.
+     * This function returns the transform that maps the unit circle to the arc's ellipse.
+     * @return Transform from unit circle to the arc's ellipse */
+    Affine unitCircleTransform() const {
+        Affine result = _ellipse.unitCircleTransform();
+        return result;
+    }
+
+    /** @brief Compute a transform that maps the arc's ellipse to the unit circle. */
+    Affine inverseUnitCircleTransform() const {
+        Affine result = _ellipse.inverseUnitCircleTransform();
+        return result;
+    }
+    /// @}
+
+    /// @name Deal with degenerate ellipses.
+    /// @{
+    /** @brief Check whether both rays are nonzero.
+     * If they are not, the arc is represented as a line segment instead. */
+    bool isChord() const {
+        return ray(X) == 0 || ray(Y) == 0;
+    }
+
+    /** @brief Get the line segment connecting the arc's endpoints.
+     * @return A linear segment with initial and final point corresponding to those of the arc. */
+    LineSegment chord() const { return LineSegment(_initial_point, _final_point); }
+    /// @}
+
+    // implementation of overloads goes here
+    Point initialPoint() const override { return _initial_point; }
+    Point finalPoint() const override { return _final_point; }
+    Curve* duplicate() const override { return new EllipticalArc(*this); }
+    void setInitial(Point const &p) override {
+        _initial_point = p;
+        _updateCenterAndAngles();
+    }
+    void setFinal(Point const &p) override {
+        _final_point = p;
+        _updateCenterAndAngles();
+    }
+    bool isDegenerate() const override {
+        return _initial_point == _final_point;
+    }
+    bool isLineSegment() const override { return isChord(); }
+    Rect boundsFast() const override {
+        return boundsExact();
+    }
+    Rect boundsExact() const override;
+    void expandToTransformed(Rect &bbox, Affine const &transform) const override;
+    // TODO: native implementation of the following methods
+    OptRect boundsLocal(OptInterval const &i, unsigned int deg) const override {
+        return SBasisCurve(toSBasis()).boundsLocal(i, deg);
+    }
+    std::vector<double> roots(double v, Dim2 d) const override;
+#ifdef HAVE_GSL
+    std::vector<double> allNearestTimes( Point const& p, double from = 0, double to = 1 ) const override;
+    double nearestTime( Point const& p, double from = 0, double to = 1 ) const override {
+        if ( are_near(ray(X), ray(Y)) && are_near(center(), p) ) {
+            return from;
+        }
+        return allNearestTimes(p, from, to).front();
+    }
+#endif
+    std::vector<CurveIntersection> intersect(Curve const &other, Coord eps=EPSILON) const override;
+    int degreesOfFreedom() const override { return 7; }
+    Curve *derivative() const override;
+
+    using Curve::operator*=;
+    void operator*=(Translate const &tr) override;
+    void operator*=(Scale const &s) override;
+    void operator*=(Rotate const &r) override;
+    void operator*=(Zoom const &z) override;
+    void operator*=(Affine const &m) override;
+
+    std::vector<Point> pointAndDerivatives(Coord t, unsigned int n) const override;
+    D2<SBasis> toSBasis() const override;
+    Curve *portion(double f, double t) const override;
+    Curve *reverse() const override;
+    bool operator==(Curve const &c) const override;
+    bool isNear(Curve const &other, Coord precision) const override;
+    void feed(PathSink &sink, bool moveto_initial) const override;
+    int winding(Point const &p) const override;
+
+private:
+    void _updateCenterAndAngles();
+    std::vector<ShapeIntersection> _filterIntersections(std::vector<ShapeIntersection> &&xs, bool is_first) const;
+    bool _validateIntersection(ShapeIntersection &xing, bool is_first) const;
+    std::vector<ShapeIntersection> _intersectSameEllipse(EllipticalArc const *other) const;
+
+    Point _initial_point, _final_point;
+    Ellipse _ellipse;
+    AngleInterval _angles;
+    bool _large_arc;
+}; // end class EllipticalArc
+
+
+// implemented in elliptical-arc-from-sbasis.cpp
+/** @brief Fit an elliptical arc to an SBasis fragment.
+ * @relates EllipticalArc */
+bool arc_from_sbasis(EllipticalArc &ea, D2<SBasis> const &in,
+                     double tolerance = EPSILON, unsigned num_samples = 20);
+
+/** @brief Debug output for elliptical arcs.
+ * @relates EllipticalArc */
+std::ostream &operator<<(std::ostream &out, EllipticalArc const &ea);
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_ELLIPTICAL_ARC_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/exception.h b/include/2geom/exception.h
new file mode 100644
index 0000000..b472aae
--- /dev/null
+++ b/include/2geom/exception.h
@@ -0,0 +1,157 @@
+/**
+ * \file
+ * \brief  Defines the different types of exceptions that 2geom can throw.
+ *
+ * There are two main exception classes: LogicalError and RangeError.
+ * Logical errors are 2geom faults/bugs; RangeErrors are 'user' faults,
+ * e.g. invalid arguments to lib2geom methods.
+ * This way, the 'user' can distinguish between groups of exceptions
+ * ('user' is the coder that uses lib2geom)
+ *
+ * Several macro's are defined for easily throwing exceptions 
+ * (e.g. THROW_CONTINUITYERROR). 
+ */
+/* Copyright 2007 Johan Engelen <goejendaagh@zonnet.nl>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_EXCEPTION_H
+#define LIB2GEOM_SEEN_EXCEPTION_H
+
+#include <exception>
+#include <sstream>
+#include <string>
+
+namespace Geom {
+
+/**
+ * Base exception class, all 2geom exceptions should be derived from this one.
+ */
+class Exception : public std::exception {
+public:
+    Exception(const char * message, const char *file, const int line) {
+        std::ostringstream os;
+        os << "lib2geom exception: " << message << " (" << file << ":" << line << ")";
+        msgstr = os.str();
+    }
+
+    ~Exception() noexcept override {} // necessary to destroy the string object!!!
+
+    const char* what() const noexcept override {
+        return msgstr.c_str();
+    }
+protected:
+    std::string msgstr;
+};
+#define THROW_EXCEPTION(message) throw(Geom::Exception(message, __FILE__, __LINE__))
+
+//-----------------------------------------------------------------------
+
+class LogicalError : public Exception {
+public:
+    LogicalError(const char * message, const char *file, const int line)
+        : Exception(message, file, line) {}
+};
+#define THROW_LOGICALERROR(message) throw(LogicalError(message, __FILE__, __LINE__))
+
+class RangeError : public Exception {
+public:
+    RangeError(const char * message, const char *file, const int line)
+        : Exception(message, file, line) {}
+};
+#define THROW_RANGEERROR(message) throw(RangeError(message, __FILE__, __LINE__))
+
+//-----------------------------------------------------------------------
+// Special case exceptions. Best used with the defines :)
+
+class NotImplemented : public LogicalError {
+public:
+    NotImplemented(const char *file, const int line)
+        : LogicalError("Method not implemented", file, line) {}
+};
+#define THROW_NOTIMPLEMENTED(i) throw(NotImplemented(__FILE__, __LINE__))
+
+class InvariantsViolation : public LogicalError {
+public:
+    InvariantsViolation(const char *file, const int line)
+        : LogicalError("Invariants violation", file, line) {}
+};
+#define THROW_INVARIANTSVIOLATION(i) throw(InvariantsViolation(__FILE__, __LINE__))
+#define ASSERT_INVARIANTS(e)       ((e) ? (void)0 : THROW_INVARIANTSVIOLATION())
+
+class NotInvertible : public RangeError {
+public:
+    NotInvertible(const char *file, const int line)
+        : RangeError("Function does not have a unique inverse", file, line) {}
+};
+#define THROW_NOTINVERTIBLE(i) throw(NotInvertible(__FILE__, __LINE__))
+
+class InfiniteSolutions : public RangeError {
+public:
+	InfiniteSolutions(const char *file, const int line)
+        : RangeError("There are infinite solutions", file, line) {}
+};
+#define THROW_INFINITESOLUTIONS(i) throw(InfiniteSolutions(__FILE__, __LINE__))
+
+class InfinitelyManySolutions : public RangeError {
+private:
+    char const *const _message;
+public:
+    InfinitelyManySolutions(const char *file, const int line, char const *message)
+        : RangeError("There are infinitely many solutions", file, line)
+        , _message{message}
+    {}
+    char const *what() const noexcept override { return _message; }
+};
+#define THROW_INFINITELY_MANY_SOLUTIONS(msg) throw(InfinitelyManySolutions(__FILE__, __LINE__, msg))
+
+class ContinuityError : public RangeError {
+public:
+    ContinuityError(const char *file, const int line)
+        : RangeError("Non-contiguous path", file, line) {}
+};
+#define THROW_CONTINUITYERROR(i) throw(ContinuityError(__FILE__, __LINE__))
+
+struct SVGPathParseError : public std::exception {
+    char const *what() const noexcept override { return "parse error"; }
+};
+
+
+} // namespace Geom
+
+#endif
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/forward.h b/include/2geom/forward.h
new file mode 100644
index 0000000..2790924
--- /dev/null
+++ b/include/2geom/forward.h
@@ -0,0 +1,127 @@
+/**
+ * \file
+ * \brief  Contains forward declarations of 2geom types
+ *//*
+ * Authors:
+ *  Johan Engelen <goejendaagh@zonnet.nl>
+ *  Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright (C) 2008-2010 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_FORWARD_H
+#define LIB2GEOM_SEEN_FORWARD_H
+
+namespace Geom {
+
+// primitives
+typedef double Coord;
+typedef int IntCoord;
+class Point;
+class IntPoint;
+class Line;
+class Ray;
+template <typename> class GenericInterval;
+template <typename> class GenericOptInterval;
+class Interval;
+class OptInterval;
+typedef GenericInterval<IntCoord> IntInterval;
+typedef GenericOptInterval<IntCoord> OptIntInterval;
+template <typename> class GenericRect;
+template <typename> class GenericOptRect;
+class Rect;
+class OptRect;
+typedef GenericRect<IntCoord> IntRect;
+typedef GenericOptRect<IntCoord> OptIntRect;
+
+// fragments
+class Linear;
+class Bezier;
+class SBasis;
+class Poly;
+
+// shapes
+class Circle;
+class Ellipse;
+class ConvexHull;
+
+// curves
+class Curve;
+class SBasisCurve;
+class BezierCurve;
+template <unsigned degree> class BezierCurveN;
+typedef BezierCurveN<1> LineSegment;
+typedef BezierCurveN<2> QuadraticBezier;
+typedef BezierCurveN<3> CubicBezier;
+class EllipticalArc;
+
+// paths and path sequences
+class Path;
+class PathVector;
+struct PathTime;
+class PathInterval;
+struct PathVectorTime;
+
+// errors
+class Exception;
+class LogicalError;
+class RangeError;
+class NotImplemented;
+class InvariantsViolation;
+class NotInvertible;
+class ContinuityError;
+
+// transforms
+class Affine;
+class Translate;
+class Rotate;
+class Scale;
+class HShear;
+class VShear;
+class Zoom;
+
+// templates
+template <typename> class D2;
+template <typename> class Piecewise;
+
+// misc
+class SVGPathSink;
+template <typename> class SVGPathGenerator;
+
+}
+
+#endif // SEEN_GEOM_FORWARD_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/generic-interval.h b/include/2geom/generic-interval.h
new file mode 100644
index 0000000..1d3cfdb
--- /dev/null
+++ b/include/2geom/generic-interval.h
@@ -0,0 +1,374 @@
+/**
+ *  @file
+ *  @brief Closed interval of generic values
+ *//*
+ * Copyright 2011 Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_GENERIC_INTERVAL_H
+#define LIB2GEOM_SEEN_GENERIC_INTERVAL_H
+
+#include <cassert>
+#include <iostream>
+#include <optional>
+#include <2geom/coord.h>
+
+namespace Geom {
+
+template <typename C>
+class GenericOptInterval;
+
+/**
+ * @brief A range of numbers which is never empty.
+ * @ingroup Primitives
+ */
+template <typename C>
+class GenericInterval
+    : CoordTraits<C>::IntervalOps
+{
+    typedef typename CoordTraits<C>::IntervalType CInterval;
+    typedef GenericInterval<C> Self;
+protected:
+    C _b[2];
+public:
+    /// @name Create intervals.
+    /// @{
+    /** @brief Create an interval that contains only zero. */
+    GenericInterval() { _b[0] = 0;  _b[1] = 0; }
+    /** @brief Create an interval that contains a single point. */
+    explicit GenericInterval(C u) { _b[0] = _b[1] = u; }
+    /** @brief Create an interval that contains all points between @c u and @c v. */
+    GenericInterval(C u, C v) {
+        if (u <= v) {
+            _b[0] = u; _b[1] = v;
+        } else {
+            _b[0] = v; _b[1] = u;
+        }
+    }
+
+    /** @brief Create an interval containing a range of values.
+     * The resulting interval will contain all values from the given range.
+     * The return type of iterators must be convertible to C. The given range
+     * must not be empty. For potentially empty ranges, see GenericOptInterval.
+     * @param start Beginning of the range
+     * @param end   End of the range
+     * @return Interval that contains all values from [start, end). */
+    template <typename InputIterator>
+    static CInterval from_range(InputIterator start, InputIterator end) {
+        assert(start != end);
+        CInterval result(*start++);
+        for (; start != end; ++start) result.expandTo(*start);
+        return result;
+    }
+    /** @brief Create an interval from a C-style array of values it should contain. */
+    static CInterval from_array(C const *c, unsigned n) {
+        CInterval result = from_range(c, c+n);
+        return result;
+    }
+    /// @}
+
+    /// @name Inspect contained values.
+    /// @{
+    C min() const { return _b[0]; }
+    C max() const { return _b[1]; }
+    C extent() const { return max() - min(); }
+    C middle() const { return (max() + min()) / 2; }
+    bool isSingular() const { return min() == max(); }
+    C operator[](unsigned i) const { assert(i < 2); return _b[i]; }
+    C clamp(C val) const {
+        if (val < min()) return min();
+        if (val > max()) return max();
+        return val;
+    }
+    /// Return the closer end of the interval.
+    C nearestEnd(C val) const {
+        C dmin = std::abs(val - min()), dmax = std::abs(val - max());
+        return dmin <= dmax ? min() : max();
+    }
+    /// @}
+
+    /// @name Test coordinates and other intervals for inclusion.
+    /// @{
+    /** @brief Check whether the interval includes this number. */
+    bool contains(C val) const {
+        return min() <= val && val <= max();
+    }
+    /** @brief Check whether the interval includes the given interval. */
+    bool contains(CInterval const &val) const {
+        return min() <= val.min() && val.max() <= max();
+    }
+    /** @brief Check whether the intervals have any common elements. */
+    bool intersects(CInterval const &val) const {
+        return contains(val.min()) || contains(val.max()) || val.contains(*this);
+    }
+    /// @}
+
+    /// @name Modify the interval.
+    /// @{
+    //TODO: NaN handleage for the next two?
+    /** @brief Set the lower boundary of the interval.
+     * When the given number is larger than the interval's largest element,
+     * it will be reduced to the single number @c val. */
+    void setMin(C val) {
+        if(val > _b[1]) {
+            _b[0] = _b[1] = val;
+        } else {
+            _b[0] = val;
+        }
+    }
+    /** @brief Set the upper boundary of the interval.
+     * When the given number is smaller than the interval's smallest element,
+     * it will be reduced to the single number @c val. */
+    void setMax(C val) {
+        if(val < _b[0]) {
+            _b[1] = _b[0] = val;
+        } else {
+            _b[1] = val;
+        }
+    }
+    /// Set both ends of the interval simultaneously
+    void setEnds(C a, C b) {
+        if (a <= b) {
+            _b[0] = a;
+            _b[1] = b;
+        } else {
+            _b[0] = b;
+            _b[1] = a;
+        }
+    }
+    /** @brief Extend the interval to include the given number. */
+    void expandTo(C val) {
+       if(val < _b[0]) _b[0] = val;
+       if(val > _b[1]) _b[1] = val;  //no else, as we want to handle NaN
+    }
+    /** @brief Expand or shrink the interval in both directions by the given amount.
+     * After this method, the interval's length (extent) will be increased by
+     * <code>amount * 2</code>. Negative values can be given; they will shrink the interval.
+     * Shrinking by a value larger than half the interval's length will create a degenerate
+     * interval containing only the midpoint of the original. */
+    void expandBy(C amount) {
+        _b[0] -= amount;
+        _b[1] += amount;
+        if (_b[0] > _b[1]) {
+            C halfway = (_b[0]+_b[1])/2;
+            _b[0] = _b[1] = halfway;
+        }
+    }
+    /** @brief Union the interval with another one.
+     * The resulting interval will contain all points of both intervals.
+     * It might also contain some points which didn't belong to either - this happens
+     * when the intervals did not have any common elements. */
+    void unionWith(CInterval const &a) {
+        if(a._b[0] < _b[0]) _b[0] = a._b[0];
+        if(a._b[1] > _b[1]) _b[1] = a._b[1];
+    }
+    /// @}
+
+    /// @name Operators
+    /// @{
+    //IMPL: OffsetableConcept
+    //TODO: rename output_type to something else in the concept
+    typedef C output_type;
+    /** @brief Offset the interval by a specified amount */
+    Self &operator+=(C amnt) {
+        _b[0] += amnt; _b[1] += amnt;
+        return *this;
+    }
+    /** @brief Offset the interval by the negation of the specified amount */
+    Self &operator-=(C amnt) {
+        _b[0] -= amnt; _b[1] -= amnt;
+        return *this;
+    }
+    
+    /** @brief Return an interval mirrored about 0 */
+    Self operator-() const { Self r(-_b[1], -_b[0]); return r; }
+    // IMPL: AddableConcept
+    /** @brief Add two intervals.
+     * Sum is defined as the set of points that can be obtained by adding any two values
+     * from both operands: \f$S = \{x \in A, y \in B: x + y\}\f$ */
+    Self &operator+=(CInterval const &o) {
+        _b[0] += o._b[0];
+        _b[1] += o._b[1];
+        return *this;
+    }
+    /** @brief Subtract two intervals.
+     * Difference is defined as the set of points that can be obtained by subtracting
+     * any value from the second operand from any value from the first operand:
+     * \f$S = \{x \in A, y \in B: x - y\}\f$ */
+    Self &operator-=(CInterval const &o) {
+        // equal to *this += -o
+        _b[0] -= o._b[1];
+        _b[1] -= o._b[0];
+        return *this;
+    }
+    /** @brief Union two intervals.
+     * Note that the intersection-and-assignment operator is not defined,
+     * because the result of an intersection can be empty, while Interval cannot. */
+    Self &operator|=(CInterval const &o) {
+        unionWith(o);
+        return *this;
+    }
+    /** @brief Test for interval equality. */
+    bool operator==(CInterval const &other) const {
+        return min() == other.min() && max() == other.max();
+    }
+    /// @}
+};
+
+/** @brief Union two intervals
+ * @relates GenericInterval */
+template <typename C>
+inline GenericInterval<C> unify(GenericInterval<C> const &a, GenericInterval<C> const &b) {
+    return a | b;
+}
+
+/**
+ * @brief A range of numbers that can be empty.
+ * @ingroup Primitives
+ */
+template <typename C>
+class GenericOptInterval
+    : public std::optional<typename CoordTraits<C>::IntervalType>
+    , boost::orable< GenericOptInterval<C>
+    , boost::andable< GenericOptInterval<C>
+      > >
+{
+    typedef typename CoordTraits<C>::IntervalType CInterval;
+    typedef typename CoordTraits<C>::OptIntervalType OptCInterval;
+    typedef std::optional<CInterval> Base;
+public:
+    /// @name Create optionally empty intervals.
+    /// @{
+    /** @brief Create an empty interval. */
+    GenericOptInterval() : Base() {}
+    /** @brief Wrap an existing interval. */
+    GenericOptInterval(GenericInterval<C> const &a) : Base(CInterval(a)) {}
+    /** @brief Create an interval containing a single point. */
+    GenericOptInterval(C u) : Base(CInterval(u)) {}
+    /** @brief Create an interval containing a range of numbers. */
+    GenericOptInterval(C u, C v) : Base(CInterval(u,v)) {}
+
+    /** @brief Create a possibly empty interval containing a range of values.
+     * The resulting interval will contain all values from the given range.
+     * The return type of iterators must be convertible to C. The given range
+     * may be empty.
+     * @param start Beginning of the range
+     * @param end   End of the range
+     * @return Interval that contains all values from [start, end), or nothing if the range
+     *         is empty. */
+    template <typename InputIterator>
+    static GenericOptInterval<C> from_range(InputIterator start, InputIterator end) {
+        if (start == end) {
+            GenericOptInterval<C> ret;
+            return ret;
+        }
+        GenericOptInterval<C> ret(CInterval::from_range(start, end));
+        return ret;
+    }
+    /// @}
+
+    /** @brief Check whether this interval is empty. */
+    bool empty() { return !*this; }
+
+    /** @brief Union with another interval, gracefully handling empty ones. */
+    void unionWith(GenericOptInterval<C> const &a) {
+        if (a) {
+            if (*this) { // check that we are not empty
+                (*this)->unionWith(*a);
+            } else {
+                *this = *a;
+            }
+        }
+    }
+    void intersectWith(GenericOptInterval<C> const &o) {
+        if (o && *this) {
+            if (!*this) return;
+            C u = std::max((*this)->min(), o->min());
+            C v = std::min((*this)->max(), o->max());
+            if (u <= v) {
+                *this = CInterval(u, v);
+                return;
+            }
+        }
+        (*static_cast<Base*>(this)) = std::nullopt;
+    }
+    GenericOptInterval<C> &operator|=(OptCInterval const &o) {
+        unionWith(o);
+        return *this;
+    }
+    GenericOptInterval<C> &operator&=(OptCInterval const &o) {
+        intersectWith(o);
+        return *this;
+    }
+
+    // The equality operators inherited from std::optional don't work with derived types, because
+    // the template overload ignores that the devived type is also an optional. It would result in
+    // `GenericInterval() != GenericInterval()` being true.
+    template <typename U, typename = std::enable_if_t<std::is_base_of_v<Base, U>>>
+    bool operator==(U const &other) const
+    {
+        return static_cast<Base const &>(*this) == static_cast<Base const &>(other);
+    }
+    template <typename U, typename = std::enable_if_t<std::is_base_of_v<Base, U>>>
+    bool operator!=(U const &other) const
+    {
+        return static_cast<Base const &>(*this) != static_cast<Base const &>(other);
+    }
+};
+
+/** @brief Intersect two intervals and return a possibly empty range of numbers
+ * @relates GenericOptInterval */
+template <typename C>
+inline GenericOptInterval<C> intersect(GenericInterval<C> const &a, GenericInterval<C> const &b) {
+    return GenericOptInterval<C>(a) & GenericOptInterval<C>(b);
+}
+/** @brief Intersect two intervals and return a possibly empty range of numbers
+ * @relates GenericOptInterval */
+template <typename C>
+inline GenericOptInterval<C> operator&(GenericInterval<C> const &a, GenericInterval<C> const &b) {
+    return GenericOptInterval<C>(a) & GenericOptInterval<C>(b);
+}
+
+template <typename C>
+inline std::ostream &operator<< (std::ostream &os, 
+                                 Geom::GenericInterval<C> const &I) {
+    os << "Interval("<<I.min() << ", "<<I.max() << ")";
+    return os;
+}
+
+} // namespace Geom
+#endif // !LIB2GEOM_SEEN_GENERIC_INTERVAL_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/generic-rect.h b/include/2geom/generic-rect.h
new file mode 100644
index 0000000..4524d43
--- /dev/null
+++ b/include/2geom/generic-rect.h
@@ -0,0 +1,547 @@
+/**
+ * \file
+ * \brief Axis-aligned rectangle
+ *//*
+ * Authors:
+ *   Michael Sloan <mgsloan@gmail.com>
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Copyright 2007-2011 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Authors of original rect class:
+ *   Lauris Kaplinski <lauris@kaplinski.com>
+ *   Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *   bulia byak <buliabyak@users.sf.net>
+ *   MenTaLguY <mental@rydia.net>
+ */
+
+#ifndef LIB2GEOM_SEEN_GENERIC_RECT_H
+#define LIB2GEOM_SEEN_GENERIC_RECT_H
+
+#include <limits>
+#include <iostream>
+#include <optional>
+#include <2geom/coord.h>
+
+namespace Geom {
+
+template <typename C>
+class GenericOptRect;
+
+/**
+ * @brief Axis aligned, non-empty, generic rectangle.
+ * @ingroup Primitives
+ */
+template <typename C>
+class GenericRect
+    : CoordTraits<C>::RectOps
+{
+    typedef typename CoordTraits<C>::IntervalType CInterval;
+    typedef typename CoordTraits<C>::PointType CPoint;
+    typedef typename CoordTraits<C>::RectType CRect;
+    typedef typename CoordTraits<C>::OptRectType OptCRect;
+protected:
+    CInterval f[2];
+public:
+    typedef CInterval D1Value;
+    typedef CInterval &D1Reference;
+    typedef CInterval const &D1ConstReference;
+
+    /// @name Create rectangles.
+    /// @{
+    /** @brief Create a rectangle that contains only the point at (0,0). */
+    GenericRect() { f[X] = f[Y] = CInterval(); }
+    /** @brief Create a rectangle from X and Y intervals. */
+    GenericRect(CInterval const &a, CInterval const &b) {
+        f[X] = a;
+        f[Y] = b;
+    }
+    /** @brief Create a rectangle from two points. */
+    GenericRect(CPoint const &a, CPoint const &b) {
+        f[X] = CInterval(a[X], b[X]);
+        f[Y] = CInterval(a[Y], b[Y]);
+    }
+    /** @brief Create rectangle from coordinates of two points. */
+    GenericRect(C x0, C y0, C x1, C y1) {
+        f[X] = CInterval(x0, x1);
+        f[Y] = CInterval(y0, y1);
+    }
+    /** @brief Create a rectangle from a range of points.
+     * The resulting rectangle will contain all points from the range.
+     * The return type of iterators must be convertible to Point.
+     * The range must not be empty. For possibly empty ranges, see OptRect.
+     * @param start Beginning of the range
+     * @param end   End of the range
+     * @return Rectangle that contains all points from [start, end). */
+    template <typename InputIterator>
+    static CRect from_range(InputIterator start, InputIterator end) {
+        assert(start != end);
+        CPoint p1 = *start++;
+        CRect result(p1, p1);
+        for (; start != end; ++start) {
+            result.expandTo(*start);
+        }
+        return result;
+    }
+    /** @brief Create a rectangle from a C-style array of points it should contain. */
+    static CRect from_array(CPoint const *c, unsigned n) {
+        CRect result = GenericRect<C>::from_range(c, c+n);
+        return result;
+    }
+    /** @brief Create rectangle from origin and dimensions. */
+    static CRect from_xywh(C x, C y, C w, C h) {
+        CPoint xy(x, y);
+        CPoint wh(w, h);
+        CRect result(xy, xy + wh);
+        return result;
+    }
+    /** @brief Create rectangle from origin and dimensions. */
+    static CRect from_xywh(CPoint const &xy, CPoint const &wh) {
+        CRect result(xy, xy + wh);
+        return result;
+    }
+    /// Create infinite rectangle.
+    static CRect infinite() {
+        CPoint p0(std::numeric_limits<C>::min(), std::numeric_limits<C>::min());
+        CPoint p1(std::numeric_limits<C>::max(), std::numeric_limits<C>::max());
+        CRect result(p0, p1);
+        return result;
+    }
+    /// @}
+
+    /// @name Inspect dimensions.
+    /// @{
+    CInterval &operator[](unsigned i)             { return f[i]; }
+    CInterval const &operator[](unsigned i) const { return f[i]; }
+    CInterval &operator[](Dim2 d)                 { return f[d]; }
+    CInterval const &operator[](Dim2 d) const     { return f[d]; }
+
+    /** @brief Get the corner of the rectangle with smallest coordinate values.
+     * In 2Geom standard coordinate system, this means upper left. */
+    CPoint min() const { CPoint p(f[X].min(), f[Y].min()); return p; }
+    /** @brief Get the corner of the rectangle with largest coordinate values.
+     * In 2Geom standard coordinate system, this means lower right. */
+    CPoint max() const { CPoint p(f[X].max(), f[Y].max()); return p; }
+    /** @brief Return the n-th corner of the rectangle.
+     * Returns corners in the direction of growing angles, starting from
+     * the one given by min(). For the standard coordinate system used
+     * in 2Geom (+Y downwards), this means clockwise starting from
+     * the upper left. */
+    CPoint corner(unsigned i) const {
+        switch(i % 4) {
+            case 0:  return CPoint(f[X].min(), f[Y].min());
+            case 1:  return CPoint(f[X].max(), f[Y].min());
+            case 2:  return CPoint(f[X].max(), f[Y].max());
+            default: return CPoint(f[X].min(), f[Y].max());
+        }
+    }
+        
+    //We should probably remove these - they're coord sys gnostic
+    /** @brief Return top coordinate of the rectangle (+Y is downwards). */
+    C top() const { return f[Y].min(); }
+    /** @brief Return bottom coordinate of the rectangle (+Y is downwards). */
+    C bottom() const { return f[Y].max(); }
+    /** @brief Return leftmost coordinate of the rectangle (+X is to the right). */
+    C left() const { return f[X].min(); }
+    /** @brief Return rightmost coordinate of the rectangle (+X is to the right). */
+    C right() const { return f[X].max(); }
+
+    /** @brief Get the horizontal extent of the rectangle. */
+    C width() const { return f[X].extent(); }
+    /** @brief Get the vertical extent of the rectangle. */
+    C height() const { return f[Y].extent(); }
+    /** @brief Get the ratio of width to height of the rectangle. */
+    Coord aspectRatio() const { return Coord(width()) / Coord(height()); }
+
+    /** @brief Get rectangle's width and height as a point.
+     * @return Point with X coordinate corresponding to the width and the Y coordinate
+     *         corresponding to the height of the rectangle. */
+    CPoint dimensions() const { return CPoint(f[X].extent(), f[Y].extent()); }
+    /** @brief Get the point in the geometric center of the rectangle. */
+    CPoint midpoint() const { return CPoint(f[X].middle(), f[Y].middle()); }
+
+    /** @brief Compute rectangle's area. */
+    C area() const { return f[X].extent() * f[Y].extent(); }
+    /** @brief Check whether the rectangle has zero area. */
+    bool hasZeroArea() const { return f[X].isSingular() || f[Y].isSingular(); }
+
+    /** @brief Get the larger extent (width or height) of the rectangle. */
+    C maxExtent() const { return std::max(f[X].extent(), f[Y].extent()); }
+    /** @brief Get the smaller extent (width or height) of the rectangle. */
+    C minExtent() const { return std::min(f[X].extent(), f[Y].extent()); }
+
+    /** @brief Get rectangle's distance SQUARED away from the given point **/
+    C distanceSq(const CPoint pt) const {
+        auto v = clamp(pt) - pt;
+        return v.x() * v.x() + v.y() * v.y();
+    }
+
+    /** @brief Clamp point to the rectangle. */
+    CPoint clamp(CPoint const &p) const {
+        CPoint result(f[X].clamp(p[X]), f[Y].clamp(p[Y]));
+        return result;
+    }
+    /** @brief Get the nearest point on the edge of the rectangle. */
+    CPoint nearestEdgePoint(CPoint const &p) const {
+        CPoint result = p;
+        if (!contains(p)) {
+            result = clamp(p);
+        } else {
+            C cx = f[X].nearestEnd(p[X]);
+            C cy = f[Y].nearestEnd(p[Y]);
+            if (std::abs(cx - p[X]) <= std::abs(cy - p[Y])) {
+                result[X] = cx;
+            } else {
+                result[Y] = cy;
+            }
+        }
+        return result;
+    }
+    /// @}
+
+    /// @name Test other rectangles and points for inclusion.
+    /// @{
+    /** @brief Check whether the rectangles have any common points. */
+    bool intersects(GenericRect<C> const &r) const { 
+        return f[X].intersects(r[X]) && f[Y].intersects(r[Y]);
+    }
+    /** @brief Check whether the rectangle includes all points in the given rectangle. */
+    bool contains(GenericRect<C> const &r) const { 
+        return f[X].contains(r[X]) && f[Y].contains(r[Y]);
+    }
+
+    /** @brief Check whether the rectangles have any common points.
+     * Empty rectangles will not intersect with any other rectangle. */
+    inline bool intersects(OptCRect const &r) const;
+    /** @brief Check whether the rectangle includes all points in the given rectangle.
+     * Empty rectangles will be contained in any non-empty rectangle. */
+    inline bool contains(OptCRect const &r) const;
+
+    /** @brief Check whether the given point is within the rectangle. */
+    bool contains(CPoint const &p) const {
+        return f[X].contains(p[X]) && f[Y].contains(p[Y]);
+    }
+    /// @}
+
+    /// @name Modify the rectangle.
+    /// @{
+    /** @brief Set the minimum X coordinate of the rectangle. */
+    void setLeft(C val) {
+        f[X].setMin(val);
+    }
+    /** @brief Set the maximum X coordinate of the rectangle. */
+    void setRight(C val) {
+        f[X].setMax(val);
+    }
+    /** @brief Set the minimum Y coordinate of the rectangle. */
+    void setTop(C val) {
+        f[Y].setMin(val);
+    }
+    /** @brief Set the maximum Y coordinate of the rectangle. */
+    void setBottom(C val) {
+        f[Y].setMax(val);
+    }
+    /** @brief Set the upper left point of the rectangle. */
+    void setMin(CPoint const &p) {
+        f[X].setMin(p[X]);
+        f[Y].setMin(p[Y]);
+    }
+    /** @brief Set the lower right point of the rectangle. */
+    void setMax(CPoint const &p) {
+        f[X].setMax(p[X]);
+        f[Y].setMax(p[Y]);
+    }
+    /** @brief Enlarge the rectangle to contain the given point. */
+    void expandTo(CPoint const &p)        { 
+        f[X].expandTo(p[X]);  f[Y].expandTo(p[Y]);
+    }
+    /** @brief Enlarge the rectangle to contain the argument. */
+    void unionWith(CRect const &b) { 
+        f[X].unionWith(b[X]); f[Y].unionWith(b[Y]);
+    }
+    /** @brief Enlarge the rectangle to contain the argument.
+     * Unioning with an empty rectangle results in no changes. */
+    void unionWith(OptCRect const &b);
+
+    /** @brief Expand the rectangle in both directions by the specified amount.
+     * Note that this is different from scaling. Negative values will shrink the
+     * rectangle. If <code>-amount</code> is larger than
+     * half of the width, the X interval will contain only the X coordinate
+     * of the midpoint; same for height. */
+    void expandBy(C amount) {
+        expandBy(amount, amount);
+    }
+    /** @brief Expand the rectangle in both directions.
+     * Note that this is different from scaling. Negative values will shrink the
+     * rectangle. If <code>-x</code> is larger than
+     * half of the width, the X interval will contain only the X coordinate
+     * of the midpoint; same for height. */
+    void expandBy(C x, C y) { 
+        f[X].expandBy(x);  f[Y].expandBy(y);
+    }
+    /** @brief Expand the rectangle by the coordinates of the given point.
+     * This will expand the width by the X coordinate of the point in both directions
+     * and the height by Y coordinate of the point. Negative coordinate values will
+     * shrink the rectangle. If <code>-p[X]</code> is larger than half of the width,
+     * the X interval will contain only the X coordinate of the midpoint;
+     * same for height. */
+    void expandBy(CPoint const &p) {
+        expandBy(p[X], p[Y]);
+    }
+    /// @}
+
+    /// @name Operators
+    /// @{
+    /** @brief Offset the rectangle by a vector. */
+    GenericRect<C> &operator+=(CPoint const &p) {
+        f[X] += p[X];
+        f[Y] += p[Y];
+        return *this;
+    }
+    /** @brief Offset the rectangle by the negation of a vector. */
+    GenericRect<C> &operator-=(CPoint const &p) {
+        f[X] -= p[X];
+        f[Y] -= p[Y];
+        return *this;
+    }
+    /** @brief Union two rectangles. */
+    GenericRect<C> &operator|=(CRect const &o) {
+        unionWith(o);
+        return *this;
+    }
+    GenericRect<C> &operator|=(OptCRect const &o) {
+        unionWith(o);
+        return *this;
+    }
+    /** @brief Test for equality of rectangles. */
+    bool operator==(CRect const &o) const { return f[X] == o[X] && f[Y] == o[Y]; }
+    /// @}
+};
+
+/**
+ * @brief Axis-aligned generic rectangle that can be empty.
+ * @ingroup Primitives
+ */
+template <typename C>
+class GenericOptRect
+    : public std::optional<typename CoordTraits<C>::RectType>
+    , boost::equality_comparable< typename CoordTraits<C>::OptRectType
+    , boost::equality_comparable< typename CoordTraits<C>::OptRectType, typename CoordTraits<C>::RectType
+    , boost::orable< typename CoordTraits<C>::OptRectType
+    , boost::andable< typename CoordTraits<C>::OptRectType
+    , boost::andable< typename CoordTraits<C>::OptRectType, typename CoordTraits<C>::RectType
+      > > > > >
+{
+    typedef typename CoordTraits<C>::IntervalType CInterval;
+    typedef typename CoordTraits<C>::OptIntervalType OptCInterval;
+    typedef typename CoordTraits<C>::PointType CPoint;
+    typedef typename CoordTraits<C>::RectType CRect;
+    typedef typename CoordTraits<C>::OptRectType OptCRect;
+    typedef std::optional<CRect> Base;
+public:
+    typedef CInterval D1Value;
+    typedef CInterval &D1Reference;
+    typedef CInterval const &D1ConstReference;
+
+    /// @name Create potentially empty rectangles.
+    /// @{
+    GenericOptRect() : Base() {}
+    GenericOptRect(GenericRect<C> const &a) : Base(CRect(a)) {}
+    GenericOptRect(CPoint const &a, CPoint const &b) : Base(CRect(a, b)) {}
+    GenericOptRect(C x0, C y0, C x1, C y1) : Base(CRect(x0, y0, x1, y1)) {}
+    /// Creates an empty OptRect when one of the argument intervals is empty.
+    GenericOptRect(OptCInterval const &x_int, OptCInterval const &y_int) {
+        if (x_int && y_int) {
+            *this = CRect(*x_int, *y_int);
+        }
+        // else, stay empty.
+    }
+    
+    /** @brief Create a rectangle from a range of points.
+     * The resulting rectangle will contain all points from the range.
+     * If the range contains no points, the result will be an empty rectangle.
+     * The return type of iterators must be convertible to the corresponding
+     * point type (Point or IntPoint).
+     * @param start Beginning of the range
+     * @param end   End of the range
+     * @return Rectangle that contains all points from [start, end). */
+    template <typename InputIterator>
+    static OptCRect from_range(InputIterator start, InputIterator end) {
+        OptCRect result;
+        for (; start != end; ++start) {
+            result.expandTo(*start);
+        }
+        return result;
+    }
+    /// @}
+
+    /// @name Check other rectangles and points for inclusion.
+    /// @{
+    /** @brief Check for emptiness. */
+    inline bool empty() const { return !*this; };
+    /** @brief Check whether the rectangles have any common points.
+     * Empty rectangles will not intersect with any other rectangle. */
+    bool intersects(CRect const &r) const { return r.intersects(*this); }
+    /** @brief Check whether the rectangle includes all points in the given rectangle.
+     * Empty rectangles will be contained in any non-empty rectangle. */
+    bool contains(CRect const &r) const { return *this && (*this)->contains(r); }
+
+    /** @brief Check whether the rectangles have any common points.
+     * Empty rectangles will not intersect with any other rectangle.
+     * Two empty rectangles will not intersect each other. */
+    bool intersects(OptCRect const &r) const { return *this && (*this)->intersects(r); }
+    /** @brief Check whether the rectangle includes all points in the given rectangle.
+     * Empty rectangles will be contained in any non-empty rectangle.
+     * An empty rectangle will not contain other empty rectangles. */
+    bool contains(OptCRect const &r) const { return *this && (*this)->contains(r); }
+
+    /** @brief Check whether the given point is within the rectangle.
+     * An empty rectangle will not contain any points. */
+    bool contains(CPoint const &p) const { return *this && (*this)->contains(p); }
+    /// @}
+
+    /** @brief Returns an empty optional (testing false) if the rectangle has zero area. */
+    OptCRect regularized() const {
+        return *this && !(*this)->hasZeroArea() ? *this : OptCRect();
+    }
+
+    /// @name Modify the potentially empty rectangle.
+    /// @{
+    /** @brief Enlarge the rectangle to contain the argument.
+     * If this rectangle is empty, after callng this method it will
+     * be equal to the argument. */
+    void unionWith(CRect const &b) {
+        if (*this) {
+            (*this)->unionWith(b);
+        } else {
+            *this = b;
+        }
+    }
+    /** @brief Enlarge the rectangle to contain the argument.
+     * Unioning with an empty rectangle results in no changes.
+     * If this rectangle is empty, after calling this method it will
+     * be equal to the argument. */
+    void unionWith(OptCRect const &b) {
+        if (b) unionWith(*b);
+    }
+    /** @brief Leave only the area overlapping with the argument.
+     * If the rectangles do not have any points in common, after calling
+     * this method the rectangle will be empty. */
+    void intersectWith(CRect const &b) {
+        if (!*this) return;
+        OptCInterval x = (**this)[X] & b[X], y = (**this)[Y] & b[Y];
+        if (x && y) {
+            *this = CRect(*x, *y);
+        } else {
+            *(static_cast<Base*>(this)) = std::nullopt;
+        }
+    }
+    /** @brief Leave only the area overlapping with the argument.
+     * If the argument is empty or the rectangles do not have any points
+     * in common, after calling this method the rectangle will be empty. */
+    void intersectWith(OptCRect const &b) {
+        if (b) {
+            intersectWith(*b);
+        } else {
+            *(static_cast<Base*>(this)) = std::nullopt;
+        }
+    }
+    /** @brief Create or enlarge the rectangle to contain the given point.
+     * If the rectangle is empty, after calling this method it will be non-empty
+     * and it will contain only the given point. */
+    void expandTo(CPoint const &p) {
+        if (*this) {
+            (*this)->expandTo(p);
+        } else {
+            *this = CRect(p, p);
+        }
+    }
+    /// @}
+    
+    /// @name Operators
+    /// @{
+    /** @brief Union with @a b */
+    GenericOptRect<C> &operator|=(OptCRect const &b) {
+        unionWith(b);
+        return *this;
+    }
+    /** @brief Intersect with @a b */
+    GenericOptRect<C> &operator&=(CRect const &b) {
+        intersectWith(b);
+        return *this;
+    }
+    /** @brief Intersect with @a b */
+    GenericOptRect<C> &operator&=(OptCRect const &b) {
+        intersectWith(b);
+        return *this;
+    }
+    /** @brief Test for equality.
+     * All empty rectangles are equal. */
+    bool operator==(OptCRect const &other) const {
+        if (!*this != !other) return false;
+        return *this ? (**this == *other) : true;
+    }
+    bool operator==(CRect const &other) const {
+        if (!*this) return false;
+        return **this == other;
+    }
+    /// @}
+};
+
+template <typename C>
+inline void GenericRect<C>::unionWith(OptCRect const &b) { 
+    if (b) {
+        unionWith(*b);
+    }
+}
+template <typename C>
+inline bool GenericRect<C>::intersects(OptCRect const &r) const {
+    return r && intersects(*r);
+}
+template <typename C>
+inline bool GenericRect<C>::contains(OptCRect const &r) const {
+    return !r || contains(*r);
+}
+
+template <typename C>
+inline std::ostream &operator<<(std::ostream &out, GenericRect<C> const &r) {
+    out << "Rect " << r[X] << " x " << r[Y];
+    return out;
+}
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_RECT_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/geom.h b/include/2geom/geom.h
new file mode 100644
index 0000000..7393ff4
--- /dev/null
+++ b/include/2geom/geom.h
@@ -0,0 +1,66 @@
+/**
+ *  \file
+ *  \brief Various geometrical calculations
+ *
+ *  Authors:
+ *   Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *
+ * Copyright (C) 1999-2002 authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+#ifndef LIB2GEOM_SEEN_GEOM_H
+#define LIB2GEOM_SEEN_GEOM_H
+
+//TODO: move somewhere else
+
+#include <vector>
+#include <2geom/forward.h>
+#include <optional>
+#include <2geom/bezier-curve.h>
+#include <2geom/line.h>
+
+namespace Geom {
+
+std::optional<Geom::LineSegment>
+rect_line_intersect(Geom::Rect &r,
+                    Geom::LineSegment ls);
+
+int centroid(std::vector<Geom::Point> const &p, Geom::Point& centroid, double &area);
+
+}
+
+#endif
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/int-interval.h b/include/2geom/int-interval.h
new file mode 100644
index 0000000..0faf48d
--- /dev/null
+++ b/include/2geom/int-interval.h
@@ -0,0 +1,63 @@
+/**
+ *  \file
+ *  \brief Closed interval of integer values
+ *//*
+ * Copyright 2011 Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_INT_INTERVAL_H
+#define LIB2GEOM_SEEN_INT_INTERVAL_H
+
+#include <2geom/coord.h>
+#include <2geom/generic-interval.h>
+
+namespace Geom {
+
+/**
+ * @brief Range of integers that is never empty.
+ * @ingroup Primitives
+ */
+typedef GenericInterval<IntCoord> IntInterval;
+
+/**
+ * @brief Range of integers that can be empty.
+ * @ingroup Primitives
+ */
+typedef GenericOptInterval<IntCoord> OptIntInterval;
+
+} // namespace Geom
+#endif // !LIB2GEOM_SEEN_INT_INTERVAL_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/int-point.h b/include/2geom/int-point.h
new file mode 100644
index 0000000..6dbed11
--- /dev/null
+++ b/include/2geom/int-point.h
@@ -0,0 +1,202 @@
+/**
+ *  \file
+ *  \brief Cartesian point / 2D vector with integer coordinates
+ *//*
+ * Copyright 2011 Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_INT_POINT_H
+#define LIB2GEOM_SEEN_INT_POINT_H
+
+#include <stdexcept>
+#include <boost/operators.hpp>
+#include <2geom/coord.h>
+
+namespace Geom {
+
+/**
+ * @brief Two-dimensional point with integer coordinates.
+ *
+ * This class is an exact equivalent of Point, except it stores integer coordinates.
+ * Integer points are useful in contexts related to rasterized graphics, for example
+ * for bounding boxes when rendering SVG.
+ *
+ * @see Point
+ * @ingroup Primitives */
+class IntPoint
+    : boost::additive< IntPoint
+    , boost::totally_ordered< IntPoint
+    , boost::multiplicative< IntPoint, IntCoord
+    , boost::multiplicative< IntPoint
+    > > > >
+{
+    IntCoord _pt[2] = { 0, 0 };
+public:
+    /// @name Create integer points
+    /// @{
+    /** Construct a point at the origin. */
+    IntPoint() = default;
+    /** Construct a point from its coordinates. */
+    IntPoint(IntCoord x, IntCoord y)
+        : _pt{ x, y }
+    {}
+    /// @}
+
+    /// @name Access the coordinates of a point
+    /// @{
+    IntCoord operator[](unsigned i) const {
+        if ( i > Y ) throw std::out_of_range("index out of range");
+        return _pt[i];
+    }
+    IntCoord &operator[](unsigned i) {
+        if ( i > Y ) throw std::out_of_range("index out of range");
+        return _pt[i];
+    }
+    IntCoord operator[](Dim2 d) const { return _pt[d]; }
+    IntCoord &operator[](Dim2 d) { return _pt[d]; }
+
+    IntCoord x() const noexcept { return _pt[X]; }
+    IntCoord &x() noexcept { return _pt[X]; }
+    IntCoord y() const noexcept { return _pt[Y]; }
+    IntCoord &y() noexcept { return _pt[Y]; }
+    /// @}
+
+    /// @name Vector-like arithmetic operations
+    /// @{
+    IntPoint operator-() const {
+        return IntPoint(-_pt[X], -_pt[Y]);
+    }
+    IntPoint &operator+=(IntPoint const &o) {
+        _pt[X] += o._pt[X];
+        _pt[Y] += o._pt[Y];
+        return *this;
+    }
+    IntPoint &operator-=(IntPoint const &o) {
+        _pt[X] -= o._pt[X];
+        _pt[Y] -= o._pt[Y];
+        return *this;
+    }
+    IntPoint &operator*=(IntPoint const &o) {
+        _pt[X] *= o._pt[X];
+        _pt[Y] *= o._pt[Y];
+        return *this;
+    }
+    IntPoint &operator*=(IntCoord o) {
+        _pt[X] *= o;
+        _pt[Y] *= o;
+        return *this;
+    }
+    IntPoint &operator/=(IntPoint const &o) {
+        _pt[X] /= o._pt[X];
+        _pt[Y] /= o._pt[Y];
+        return *this;
+    }
+    IntPoint &operator/=(IntCoord o) {
+        _pt[X] /= o;
+        _pt[Y] /= o;
+        return *this;
+    }
+    /// @}
+    
+    /// @name Various utilities
+    /// @{
+    /** @brief Equality operator. */
+    bool operator==(IntPoint const &in_pnt) const {
+        return ((_pt[X] == in_pnt[X]) && (_pt[Y] == in_pnt[Y]));
+    }
+    /** @brief Lexicographical ordering for points.
+     * Y coordinate is regarded as more significant. When sorting according to this
+     * ordering, the points will be sorted according to the Y coordinate, and within
+     * points with the same Y coordinate according to the X coordinate. */
+    bool operator<(IntPoint const &p) const {
+        return ( ( _pt[Y] < p[Y] ) ||
+             (( _pt[Y] == p[Y] ) && ( _pt[X] < p[X] )));
+    }
+    /// @}
+    
+    /** @brief Lexicographical ordering functor.
+     * @param d The more significant dimension */
+    template <Dim2 d> struct LexLess;
+    /** @brief Lexicographical ordering functor.
+     * @param d The more significant dimension */
+    template <Dim2 d> struct LexGreater;
+    /** @brief Lexicographical ordering functor with runtime dimension. */
+    struct LexLessRt {
+        LexLessRt(Dim2 d) : dim(d) {}
+        inline bool operator()(IntPoint const &a, IntPoint const &b) const;
+    private:
+        Dim2 dim;
+    };
+    /** @brief Lexicographical ordering functor with runtime dimension. */
+    struct LexGreaterRt {
+        LexGreaterRt(Dim2 d) : dim(d) {}
+        inline bool operator()(IntPoint const &a, IntPoint const &b) const;
+    private:
+        Dim2 dim;
+    };
+};
+
+template<> struct IntPoint::LexLess<X> {
+    bool operator()(IntPoint const &a, IntPoint const &b) const {
+        return a[X] < b[X] || (a[X] == b[X] && a[Y] < b[Y]);
+    }
+};
+template<> struct IntPoint::LexLess<Y> {
+    bool operator()(IntPoint const &a, IntPoint const &b) const {
+        return a[Y] < b[Y] || (a[Y] == b[Y] && a[X] < b[X]);
+    }
+};
+template<> struct IntPoint::LexGreater<X> {
+    bool operator()(IntPoint const &a, IntPoint const &b) const {
+        return a[X] > b[X] || (a[X] == b[X] && a[Y] > b[Y]);
+    }
+};
+template<> struct IntPoint::LexGreater<Y> {
+    bool operator()(IntPoint const &a, IntPoint const &b) const {
+        return a[Y] > b[Y] || (a[Y] == b[Y] && a[X] > b[X]);
+    }
+};
+inline bool IntPoint::LexLessRt::operator()(IntPoint const &a, IntPoint const &b) const {
+    return dim ? IntPoint::LexLess<Y>()(a, b) : IntPoint::LexLess<X>()(a, b);
+}
+inline bool IntPoint::LexGreaterRt::operator()(IntPoint const &a, IntPoint const &b) const {
+    return dim ? IntPoint::LexGreater<Y>()(a, b) : IntPoint::LexGreater<X>()(a, b);
+}
+
+}  // namespace Geom
+
+#endif // !SEEN_GEOM_INT_POINT_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/int-rect.h b/include/2geom/int-rect.h
new file mode 100644
index 0000000..567d42d
--- /dev/null
+++ b/include/2geom/int-rect.h
@@ -0,0 +1,75 @@
+/**
+ *  \file
+ *  \brief Axis-aligned rectangle with integer coordinates
+ *//*
+ * Copyright 2011 Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_INT_RECT_H
+#define LIB2GEOM_SEEN_INT_RECT_H
+
+#include <2geom/coord.h>
+#include <2geom/int-interval.h>
+#include <2geom/generic-rect.h>
+
+namespace Geom {
+
+typedef GenericRect<IntCoord> IntRect;
+typedef GenericOptRect<IntCoord> OptIntRect;
+
+// the functions below do not work when defined generically
+inline OptIntRect operator&(IntRect const &a, IntRect const &b) {
+    OptIntRect ret(a);
+    ret.intersectWith(b);
+    return ret;
+}
+inline OptIntRect intersect(IntRect const &a, IntRect const &b) {
+    return a & b;
+}
+inline OptIntRect intersect(OptIntRect const &a, OptIntRect const &b) {
+    return a & b;
+}
+inline IntRect unify(IntRect const &a, IntRect const &b) {
+    return a | b;
+}
+inline OptIntRect unify(OptIntRect const &a, OptIntRect const &b) {
+    return a | b;
+}
+
+} // end namespace Geom
+
+#endif // !LIB2GEOM_SEEN_INT_RECT_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/intersection-graph.h b/include/2geom/intersection-graph.h
new file mode 100644
index 0000000..940c43c
--- /dev/null
+++ b/include/2geom/intersection-graph.h
@@ -0,0 +1,259 @@
+/**
+ * \file
+ * \brief Path intersection graph
+ *//*
+ * Authors:
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * 
+ * Copyright 2015 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef SEEN_LIB2GEOM_INTERSECTION_GRAPH_H
+#define SEEN_LIB2GEOM_INTERSECTION_GRAPH_H
+
+#include <set>
+#include <vector>
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <boost/intrusive/list.hpp>
+#include <2geom/forward.h>
+#include <2geom/pathvector.h>
+
+namespace Geom {
+
+/** @class PathIntersectionGraph
+ * @brief Intermediate data for computing Boolean operations on paths.
+ *
+ * This class implements the Greiner-Hormann clipping algorithm,
+ * with improvements inspired by Foster and Overfelt as well as some
+ * original contributions.
+ *
+ * For the purposes of boolean operations, a shape is defined as a PathVector
+ * using the "even-odd" rule, i.e., regions with odd winding are considered part
+ * of the shape, whereas regions with even winding are not.
+ *
+ * For this reason, the two path-vectors are sometimes called "shapes" or "operands" of
+ * the boolean operation. Each path-vector may contain several paths, which are called
+ * either "paths" or "components" in the documentation.
+ *
+ * @ingroup Paths
+ */
+class PathIntersectionGraph
+{
+    // this is called PathIntersectionGraph so that we can also have a class for polygons,
+    // e.g. PolygonIntersectionGraph, which is going to be significantly faster
+public:
+    /** @brief Construct a path intersection graph for two shapes described via their boundaries.
+     *  The boundaries are passed as path-vectors.
+     *
+     *  @param a – the first operand, also referred to as operand A.
+     *  @param b – the second operand, also referred to as operand B.
+     *  @param precision – precision setting used for intersection calculations.
+     */
+    PathIntersectionGraph(PathVector const &a, PathVector const &b, Coord precision = EPSILON);
+
+    /**
+     * @brief Get the union of the shapes, A ∪ B.
+     *
+     * A point belongs to the union if and only if it belongs to at least one of the operands.
+     *
+     * @return A path-vector describing the union of the operands A and B.
+     */
+    PathVector getUnion();
+
+    /**
+     * @brief Get the intersection of the shapes, A ∩ B.
+     *
+     * A point belongs to the intersection if and only if it belongs to both shapes.
+     *
+     * @return A path-vector describing the intersection of the operands A and B.
+     */
+    PathVector getIntersection();
+
+    /**
+     * @brief Get the difference of the shapes, A ∖ B.
+     *
+     * A point belongs to the difference if and only if it belongs to A but not to B.
+     *
+     * @return A path-vector describing the difference of the operands A and B.
+     */
+    PathVector getAminusB();
+
+    /**
+     * @brief Get the opposite difference of the shapes, B ∖ A.
+     *
+     * A point belongs to the difference if and only if it belongs to B but not to A.
+     *
+     * @return A path-vector describing the difference of the operands B and A.
+     */
+    PathVector getBminusA();
+
+    /**
+     * @brief Get the symmetric difference of the shapes, A ∆ B.
+     *
+     * A point belongs to the symmetric difference if and only if it belongs to one of the two
+     * shapes A or B, but not both. This is equivalent to the logical XOR operation: the elements
+     * of A ∆ B are points which are in A XOR in B.
+     *
+     * @return A path-vector describing the symmetric difference of the operands A and B.
+     */
+    PathVector getXOR();
+
+    /// Returns the number of intersections used when computing Boolean operations.
+    std::size_t size() const;
+
+    /**
+     * @brief Get the geometric points where the two path-vectors intersect.
+     *
+     * Degenerate intersection points, where the shapes merely "kiss", are not retured.
+     *
+     * @param defective – whether to return only the defective crossings or only the true crossings.
+     * @return If defective is true, returns a vector containing all defective intersection points,
+     * i.e., points that are neither true transverse intersections nor degenerate intersections.
+     * If defective is false, returns all true transverse intersections.
+     */
+    std::vector<Point> intersectionPoints(bool defective = false) const;
+
+    /**
+     * @brief Get the geometric points located on path portions between consecutive intersections.
+     *
+     * These points were used for the winding number calculations which determined which path portions
+     * lie inside the other shape and which lie outside.
+     *
+     * @return A vector containing all sample points used for winding calculations.
+     */
+    std::vector<Point> windingPoints() const {
+        return _winding_points;
+    }
+
+    void fragments(PathVector &in, PathVector &out) const;
+
+
+    bool valid() const { return _graph_valid; }
+
+private:
+    enum InOutFlag {
+        INSIDE,
+        OUTSIDE,
+        BOTH
+    };
+
+    struct IntersectionVertex {
+        boost::intrusive::list_member_hook<> _hook;
+        boost::intrusive::list_member_hook<> _proc_hook;
+        PathVectorTime pos; ///< Intersection time.
+        Point p; ///< Geometric position of the intersection point; guarantees that endpoints are exact.
+        IntersectionVertex *neighbor; ///< A pointer to the corresponding vertex on the other shape.
+        /** Tells us whether the edge originating at this intersection lies inside or outside of
+         *  the shape given by the other path-vector. The "edge originating" at this intersection is
+         *  the portion of the path between this intersection and the next intersection, in the
+         *  direction of increasing path time. */
+        InOutFlag next_edge;
+        unsigned which; ///< Index of the operand path-vector that this intersection vertex lies on.
+        /** Whether the intersection is defective, which means that for some reason the paths
+         *  neither cross transversally through each other nor "kiss" at a common tangency point.
+         */
+        bool defective;
+    };
+
+    typedef boost::intrusive::list
+        < IntersectionVertex
+        , boost::intrusive::member_hook
+            < IntersectionVertex
+            , boost::intrusive::list_member_hook<>
+            , &IntersectionVertex::_hook
+            >
+        > IntersectionList;
+
+    typedef boost::intrusive::list
+        < IntersectionVertex
+        , boost::intrusive::member_hook
+            < IntersectionVertex
+            , boost::intrusive::list_member_hook<>
+            , &IntersectionVertex::_proc_hook
+            >
+        > UnprocessedList;
+
+    /// Stores processed intersection information for a single path in an operand path-vector.
+    struct PathData {
+        IntersectionList xlist; ///< List of crossings on this particular path.
+        std::size_t path_index; ///< Index of the path in its path-vector.
+        int which; ///< Index of the path-vector (in PathIntersectionGraph::_pv) that the path belongs to.
+        /** Whether this path as a whole is contained INSIDE or OUTSIDE relative to the other path-vector.
+         *  The value BOTH means that some portions of the path are inside while others are outside.
+         */
+        InOutFlag status;
+
+        PathData(int w, std::size_t pi)
+            : path_index(pi)
+            , which(w)
+            , status(BOTH)
+        {}
+    };
+
+    struct IntersectionVertexLess;
+    typedef IntersectionList::iterator ILIter;
+    typedef IntersectionList::const_iterator CILIter;
+
+    PathVector _getResult(bool enter_a, bool enter_b);
+    void _handleNonintersectingPaths(PathVector &result, unsigned which, bool inside);
+    void _prepareArguments();
+    bool _prepareIntersectionLists(Coord precision);
+    void _assignEdgeWindingParities(Coord precision);
+    void _assignComponentStatusFromDegenerateIntersections();
+    void _removeDegenerateIntersections();
+    void _verify();
+
+    ILIter _getNeighbor(ILIter iter);
+    PathData &_getPathData(ILIter iter);
+
+    PathVector _pv[2]; ///< Stores the two operand path-vectors, A at _pv[0] and B at _pv[1].
+    boost::ptr_vector<IntersectionVertex> _xs; ///< Stores all crossings between the two shapes.
+    boost::ptr_vector<PathData> _components[2]; ///< Stores the crossing information for the operands.
+    UnprocessedList _ulist; ///< Temporarily holds all unprocessed during a boolean operation.
+    bool _graph_valid; ///< Whether all intersections are regular.
+    /** Stores sample points located on paths of the operand path-vectors,
+     *  between consecutive intersections.
+     */
+    std::vector<Point> _winding_points;
+
+    friend std::ostream &operator<<(std::ostream &, PathIntersectionGraph const &);
+};
+
+std::ostream &operator<<(std::ostream &os, PathIntersectionGraph const &pig);
+
+} // namespace Geom
+
+#endif // SEEN_LIB2GEOM_PATH_GRAPH_H
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/intersection.h b/include/2geom/intersection.h
new file mode 100644
index 0000000..8a23811
--- /dev/null
+++ b/include/2geom/intersection.h
@@ -0,0 +1,147 @@
+/**
+ * \file
+ * \brief Intersection utilities
+ *//*
+ * Authors:
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * 
+ * Copyright 2015 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef SEEN_LIB2GEOM_INTERSECTION_H
+#define SEEN_LIB2GEOM_INTERSECTION_H
+
+#include <2geom/coord.h>
+#include <2geom/point.h>
+
+namespace Geom {
+
+
+/** @brief Intersection between two shapes.
+ */
+template <typename TimeA = Coord, typename TimeB = TimeA>
+class Intersection
+    : boost::totally_ordered< Intersection<TimeA, TimeB> >
+{
+public:
+    /** @brief Construct from shape references and time values.
+     * By default, the intersection point will be halfway between the evaluated
+     * points on the two shapes. */
+    template <typename TA, typename TB>
+    Intersection(TA const &sa, TB const &sb, TimeA const &ta, TimeB const &tb)
+        : first(ta)
+        , second(tb)
+        , _point(lerp(0.5, sa.pointAt(ta), sb.pointAt(tb)))
+    {}
+
+    /// Additionally report the intersection point.
+    Intersection(TimeA const &ta, TimeB const &tb, Point const &p)
+        : first(ta)
+        , second(tb)
+        , _point(p)
+    {}
+
+    /// Intersection point, as calculated by the intersection algorithm.
+    Point point() const {
+        return _point;
+    }
+    /// Implicit conversion to Point.
+    operator Point() const {
+        return _point;
+    }
+
+    friend inline void swap(Intersection &a, Intersection &b) {
+        using std::swap;
+        swap(a.first, b.first);
+        swap(a.second, b.second);
+        swap(a._point, b._point);
+    }
+
+    bool operator==(Intersection const &other) const {
+        if (first != other.first) return false;
+        if (second != other.second) return false;
+        return true;
+    }
+    bool operator<(Intersection const &other) const {
+        if (first < other.first) return true;
+        if (first == other.first && second < other.second) return true;
+        return false;
+    }
+
+public:
+    /// First shape and time value.
+    TimeA first;
+    /// Second shape and time value.
+    TimeB second;
+private:
+    // Recalculation of the intersection point from the time values is in many cases
+    // less precise than the value obtained directly from the intersection algorithm,
+    // so we need to store it.
+    Point _point;
+};
+
+
+// TODO: move into new header?
+template <typename T>
+struct ShapeTraits {
+    typedef Coord TimeType;
+    typedef Interval IntervalType;
+    typedef T AffineClosureType;
+    typedef Intersection<> IntersectionType;
+};
+
+template <typename A, typename B> inline
+std::vector< Intersection<A, B> > transpose(std::vector< Intersection<B, A> > const &in) {
+    std::vector< Intersection<A, B> > result;
+    for (std::size_t i = 0; i < in.size(); ++i) {
+        result.push_back(Intersection<A, B>(in[i].second, in[i].first, in[i].point()));
+    }
+    return result;
+}
+
+template <typename T> inline
+void transpose_in_place(std::vector< Intersection<T, T> > &xs) {
+    for (std::size_t i = 0; i < xs.size(); ++i) {
+        std::swap(xs[i].first, xs[i].second);
+    }
+}
+
+typedef Intersection<> ShapeIntersection;
+
+
+} // namespace Geom
+
+#endif // SEEN_LIB2GEOM_INTERSECTION_H
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/interval.h b/include/2geom/interval.h
new file mode 100644
index 0000000..11c8f28
--- /dev/null
+++ b/include/2geom/interval.h
@@ -0,0 +1,245 @@
+/**
+ * \file
+ * \brief  Simple closed interval class
+ *//*
+ * Copyright 2007 Michael Sloan <mgsloan@gmail.com>
+ *
+ * Original Rect/Range code by:
+ *   Lauris Kaplinski <lauris@kaplinski.com>
+ *   Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *   bulia byak <buliabyak@users.sf.net>
+ *   MenTaLguY <mental@rydia.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+#ifndef LIB2GEOM_SEEN_INTERVAL_H
+#define LIB2GEOM_SEEN_INTERVAL_H
+
+#include <boost/none.hpp>
+#include <boost/operators.hpp>
+#include <2geom/coord.h>
+#include <2geom/math-utils.h>
+#include <2geom/generic-interval.h>
+#include <2geom/int-interval.h>
+
+namespace Geom {
+
+/**
+ * @brief Range of real numbers that is never empty.
+ *
+ * Intervals are closed ranges \f$[a, b]\f$, which means they include their endpoints.
+ * To use them as open ranges, you can use the interiorContains() methods.
+ *
+ * @ingroup Primitives
+ */
+class Interval
+    : public GenericInterval<Coord>
+{
+    typedef GenericInterval<Coord> Base;
+public:
+    /// @name Create intervals.
+    /// @{
+    /** @brief Create an interval that contains only zero. */
+    Interval() {}
+    /** @brief Create an interval that contains a single point. */
+    explicit Interval(Coord u) : Base(u) {}
+    /** @brief Create an interval that contains all points between @c u and @c v. */
+    Interval(Coord u, Coord v) : Base(u,v) {}
+    /** @brief Convert from integer interval */
+    Interval(IntInterval const &i) : Base(i.min(), i.max()) {}
+    Interval(Base const &b) : Base(b) {}
+
+    /** @brief Create an interval containing a range of values.
+     * The resulting interval will contain all values from the given range.
+     * The return type of iterators must be convertible to Coord. The given range
+     * must not be empty. For potentially empty ranges, see OptInterval.
+     * @param start Beginning of the range
+     * @param end   End of the range
+     * @return Interval that contains all values from [start, end). */
+    template <typename InputIterator>
+    static Interval from_range(InputIterator start, InputIterator end) {
+        Interval result = Base::from_range(start, end);
+        return result;
+    }
+    /** @brief Create an interval from a C-style array of values it should contain. */
+    static Interval from_array(Coord const *c, unsigned n) {
+        Interval result = from_range(c, c+n);
+        return result;
+    }
+    /// @}
+
+    /// @name Inspect contained values.
+    /// @{
+    /// Check whether both endpoints are finite.
+    bool isFinite() const {
+        return std::isfinite(min()) && std::isfinite(max());
+    }
+    /** @brief Map the interval [0,1] onto this one.
+     * This method simply performs 1D linear interpolation between endpoints. */
+    Coord valueAt(Coord t) const {
+        return lerp(t, min(), max());
+    }
+    /** @brief Compute a time value that maps to the given value.
+     * The supplied value does not need to be in the interval for this method to work. */
+    Coord timeAt(Coord v) const {
+        return (v - min()) / extent();
+    }
+    /// Find closest time in [0,1] that maps to the given value. */
+    Coord nearestTime(Coord v) const {
+        if (v <= min()) return 0;
+        if (v >= max()) return 1;
+        return timeAt(v);
+    }
+    /// @}
+
+    /// @name Test coordinates and other intervals for inclusion.
+    /// @{
+    /** @brief Check whether the interior of the interval includes this number.
+     * Interior means all numbers in the interval except its ends. */
+    bool interiorContains(Coord val) const { return min() < val && val < max(); }
+    /** @brief Check whether the interior of the interval includes the given interval.
+     * Interior means all numbers in the interval except its ends. */
+    bool interiorContains(Interval const &val) const { return min() < val.min() && val.max() < max(); }
+    /// Check whether the number is contained in the union of the interior and the lower boundary.
+    bool lowerContains(Coord val) const { return min() <= val && val < max(); }
+    /// Check whether the given interval is contained in the union of the interior and the lower boundary.
+    bool lowerContains(Interval const &val) const { return min() <= val.min() && val.max() < max(); }
+    /// Check whether the number is contained in the union of the interior and the upper boundary.
+    bool upperContains(Coord val) { return min() < val && val <= max(); }
+    /// Check whether the given interval is contained in the union of the interior and the upper boundary.
+    bool upperContains(Interval const &val) const { return min() < val.min() && val.max() <= max(); }
+    /** @brief Check whether the interiors of the intervals have any common elements.
+     * A single point in common is not considered an intersection. */
+    bool interiorIntersects(Interval const &val) const {
+        return std::max(min(), val.min()) < std::min(max(), val.max());
+    }
+    /// @}
+
+    /// @name Operators
+    /// @{
+    // IMPL: ScalableConcept
+    /** @brief Scale an interval */
+    Interval &operator*=(Coord s) {
+        using std::swap;
+        _b[0] *= s;
+        _b[1] *= s;
+        if(s < 0) swap(_b[0], _b[1]);
+        return *this;
+    }
+    /** @brief Scale an interval by the inverse of the specified value */
+    Interval &operator/=(Coord s) {
+        using std::swap;
+        _b[0] /= s;
+        _b[1] /= s;
+        if(s < 0) swap(_b[0], _b[1]);
+        return *this;
+    }
+    /** @brief Multiply two intervals.
+     * Product is defined as the set of points that can be obtained by multiplying
+     * any value from the second operand by any value from the first operand:
+     * \f$S = \{x \in A, y \in B: x * y\}\f$ */
+    Interval &operator*=(Interval const &o) {
+        // TODO implement properly
+        Coord mn = min(), mx = max();
+        expandTo(mn * o.min());
+        expandTo(mn * o.max());
+        expandTo(mx * o.min());
+        expandTo(mx * o.max());
+        return *this;
+    }
+    bool operator==(IntInterval const &ii) const {
+        return min() == Coord(ii.min()) && max() == Coord(ii.max());
+    }
+    bool operator==(Interval const &other) const {
+        return Base::operator==(other);
+    }
+    /// @}
+
+    /// @name Rounding to integer values
+    /// @{
+    /** @brief Return the smallest integer interval which contains this one. */
+    IntInterval roundOutwards() const {
+        IntInterval ret(floor(min()), ceil(max()));
+        return ret;
+    }
+    /** @brief Return the largest integer interval which is contained in this one. */
+    OptIntInterval roundInwards() const {
+        IntCoord u = ceil(min()), v = floor(max());
+        if (u > v) { OptIntInterval e; return e; }
+        IntInterval ret(u, v);
+        return ret;
+    }
+    /// @}
+};
+
+/**
+ * @brief Range of real numbers that can be empty.
+ * @ingroup Primitives
+ */
+class OptInterval
+    : public GenericOptInterval<Coord>
+{
+    typedef GenericOptInterval<Coord> Base;
+public:
+    using Base::Base;
+    using Base::operator==;
+    using Base::operator!=;
+
+    OptInterval(Base const &b) : Base(b) {}
+
+    /** @brief Promote from IntInterval. */
+    OptInterval(IntInterval const &i) : Base(Interval(i)) {}
+    /** @brief Promote from OptIntInterval. */
+    OptInterval(OptIntInterval const &i) : Base() {
+        if (i) *this = Interval(*i);
+    }
+};
+
+// functions required for Python bindings
+inline Interval unify(Interval const &a, Interval const &b)
+{
+    Interval r = a | b;
+    return r;
+}
+inline OptInterval intersect(Interval const &a, Interval const &b)
+{
+    OptInterval r = a & b;
+    return r;
+}
+
+} // end namespace Geom
+
+#endif //SEEN_INTERVAL_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/intervaltree/interval_tree.h b/include/2geom/intervaltree/interval_tree.h
new file mode 100644
index 0000000..85f91f9
--- /dev/null
+++ b/include/2geom/intervaltree/interval_tree.h
@@ -0,0 +1,126 @@
+#ifndef E_INTERVAL_TREE
+#define E_INTERVAL_TREE
+
+// From Emin Martinian, licenced LGPL and MPL with permission
+
+#include <vector>
+#include <math.h>
+#include <limits>
+#include <iostream>
+
+using std::vector;
+
+//  The interval_tree.h and interval_tree.cc files contain code for 
+//  interval trees implemented using red-black-trees as described in 
+//  the book _Introduction_To_Algorithms_ by Cormen, Leisserson, 
+//  and Rivest.  
+
+//  CONVENTIONS:  
+//                Function names: Each word in a function name begins with 
+//                a capital letter.  An example funcntion name is  
+//                CreateRedTree(a,b,c). Furthermore, each function name 
+//                should begin with a capital letter to easily distinguish 
+//                them from variables. 
+//                                                                     
+//                Variable names: Each word in a variable name begins with 
+//                a capital letter EXCEPT the first letter of the variable 
+//                name.  For example, int newLongInt.  Global variables have 
+//                names beginning with "g".  An example of a global 
+//                variable name is gNewtonsConstant. 
+
+
+#ifndef MAX_INT
+#define MAX_INT INT_MAX // some architechturs define INT_MAX not MAX_INT
+#endif
+
+// The Interval class is an Abstract Base Class.  This means that no
+// instance of the Interval class can exist.  Only classes which
+// inherit from the Interval class can exist.  Furthermore any class
+// which inherits from the Interval class must define the member
+// functions GetLowPoint and GetHighPoint.
+//
+// The GetLowPoint should return the lowest point of the interval and
+// the GetHighPoint should return the highest point of the interval.  
+
+class Interval {
+public:
+  Interval();
+  virtual ~Interval();
+  virtual int GetLowPoint() const = 0;
+  virtual int GetHighPoint() const = 0;
+  virtual void Print() const;
+};
+
+class IntervalTreeNode {
+  friend class IntervalTree;
+public:
+  void Print(IntervalTreeNode*,
+	     IntervalTreeNode*) const;
+  IntervalTreeNode();
+  IntervalTreeNode(Interval *);
+  ~IntervalTreeNode();
+protected:
+  Interval * storedInterval;
+  int key;
+  int high;
+  int maxHigh;
+  bool red; /* if red=0 then the node is black */
+  IntervalTreeNode * left;
+  IntervalTreeNode * right;
+  IntervalTreeNode * parent;
+};
+
+struct it_recursion_node {
+public:
+  /*  this structure stores the information needed when we take the */
+  /*  right branch in searching for intervals but possibly come back */
+  /*  and check the left branch as well. */
+
+  IntervalTreeNode * start_node;
+  unsigned int parentIndex;
+  bool tryRightBranch;
+} ;
+
+
+class IntervalTree {
+public:
+  IntervalTree();
+  ~IntervalTree();
+  void Print() const;
+  Interval * DeleteNode(IntervalTreeNode *);
+  IntervalTreeNode * Insert(Interval *);
+  IntervalTreeNode * GetPredecessorOf(IntervalTreeNode *) const;
+  IntervalTreeNode * GetSuccessorOf(IntervalTreeNode *) const;
+  vector<void *> Enumerate(int low, int high) ;
+  void CheckAssumptions() const;
+protected:
+  /*  A sentinel is used for root and for nil.  These sentinels are */
+  /*  created when ITTreeCreate is caled.  root->left should always */
+  /*  point to the node which is the root of the tree.  nil points to a */
+  /*  node which should always be black but has arbitrary children and */
+  /*  parent and no key or info.  The point of using these sentinels is so */
+  /*  that the root and nil nodes do not require special cases in the code */
+  IntervalTreeNode * root;
+  IntervalTreeNode * nil;
+  void LeftRotate(IntervalTreeNode *);
+  void RightRotate(IntervalTreeNode *);
+  void TreeInsertHelp(IntervalTreeNode *);
+  void TreePrintHelper(IntervalTreeNode *) const;
+  void FixUpMaxHigh(IntervalTreeNode *);
+  void DeleteFixUp(IntervalTreeNode *);
+  void CheckMaxHighFields(IntervalTreeNode *) const;
+  int CheckMaxHighFieldsHelper(IntervalTreeNode * y, 
+			const int currentHigh,
+			int match) const;
+private:
+  unsigned int recursionNodeStackSize;
+  it_recursion_node * recursionNodeStack;
+  unsigned int currentParent;
+  unsigned int recursionNodeStackTop;
+};
+
+
+#endif
+
+
+
diff --git a/include/2geom/line.h b/include/2geom/line.h
new file mode 100644
index 0000000..9a56602
--- /dev/null
+++ b/include/2geom/line.h
@@ -0,0 +1,605 @@
+/**
+ * \file
+ * \brief  Infinite straight line
+ *//*
+ * Authors:
+ *   Marco Cecchetti <mrcekets at gmail.com>
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Copyright 2008-2011 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_LINE_H
+#define LIB2GEOM_SEEN_LINE_H
+
+#include <cmath>
+#include <optional>
+#include <2geom/bezier-curve.h> // for LineSegment
+#include <2geom/rect.h>
+#include <2geom/crossing.h>
+#include <2geom/exception.h>
+#include <2geom/ray.h>
+#include <2geom/angle.h>
+#include <2geom/intersection.h>
+
+namespace Geom
+{
+
+// class docs in cpp file
+class Line
+    : boost::equality_comparable< Line >
+{
+private:
+    Point _initial;
+    Point _final;
+public:
+    /// @name Creating lines.
+    /// @{
+    /** @brief Create a default horizontal line.
+     * Creates a line with unit speed going in +X direction. */
+    Line()
+        : _initial(0,0), _final(1,0)
+    {}
+    /** @brief Create a line with the specified inclination.
+     * @param origin One of the points on the line
+     * @param angle Angle of the line in mathematical convention */
+    Line(Point const &origin, Coord angle)
+        : _initial(origin)
+    {
+        Point v;
+        sincos(angle, v[Y], v[X]);
+        _final = _initial + v;
+    }
+
+    /** @brief Create a line going through two points.
+     * The first point will be at time 0, while the second one
+     * will be at time 1.
+     * @param a Initial point
+     * @param b First point */
+    Line(Point const &a, Point const &b)
+        : _initial(a)
+        , _final(b)
+    {}
+
+    /** @brief Create a line based on the coefficients of its equation.
+     @see Line::setCoefficients() */
+    Line(double a, double b, double c) {
+        setCoefficients(a, b, c);
+    }
+
+    /// Create a line by extending a line segment.
+    explicit Line(LineSegment const &seg)
+        : _initial(seg.initialPoint())
+        , _final(seg.finalPoint())
+    {}
+
+    /// Create a line by extending a ray.
+    explicit Line(Ray const &r)
+        : _initial(r.origin())
+        , _final(r.origin() + r.vector())
+    {}
+
+    /// Create a line normal to a vector at a specified distance from origin.
+    static Line from_normal_distance(Point const &n, Coord c) {
+        Point start = c * n.normalized();
+        Line l(start, start + rot90(n));
+        return l;
+    }
+    /** @brief Create a line from origin and unit vector.
+     * Note that each line direction has two possible unit vectors.
+     * @param o Point through which the line will pass
+     * @param v Unit vector of the line's direction */
+    static Line from_origin_and_vector(Point const &o, Point const &v) {
+        Line l(o, o + v);
+        return l;
+    }
+
+    Line* duplicate() const {
+        return new Line(*this);
+    }
+    /// @}
+
+    /// @name Retrieve and set the line's parameters.
+    /// @{
+
+    /// Get the line's origin point.
+    Point origin() const { return _initial; }
+    /** @brief Get the line's raw direction vector.
+     * The length of the retrieved vector is equal to the length of a segment parametrized by
+     * a time interval of length 1. */
+    Point vector() const { return _final - _initial; }
+    /** @brief Get the line's normalized direction vector.
+     * The retrieved vector is normalized to unit length. */
+    Point versor() const { return (_final - _initial).normalized(); }
+    /// Angle the line makes with the X axis, in mathematical convention.
+    Coord angle() const {
+        Point d = _final - _initial;
+        double a = std::atan2(d[Y], d[X]);
+        if (a < 0) a += M_PI;
+        if (a == M_PI) a = 0;
+        return a;
+    }
+
+    /** @brief Set the point at zero time.
+     * The orientation remains unchanged, modulo numeric errors during addition. */
+    void setOrigin(Point const &p) {
+        Point d = p - _initial;
+        _initial = p;
+        _final += d;
+    }
+    /** @brief Set the speed of the line.
+     * Origin remains unchanged. */
+    void setVector(Point const &v) {
+        _final = _initial + v;
+    }
+
+    /** @brief Set the angle the line makes with the X axis.
+     * Origin remains unchanged. */
+    void setAngle(Coord angle) {
+        Point v;
+        sincos(angle, v[Y], v[X]);
+        v *= distance(_initial, _final);
+        _final = _initial + v;
+    }
+
+    /// Set a line based on two points it should pass through.
+    void setPoints(Point const &a, Point const &b) {
+        _initial = a;
+        _final = b;
+    }
+
+    /** @brief Set the coefficients of the line equation.
+     * The line equation is: \f$ax + by = c\f$. Points that satisfy the equation
+     * are on the line. */
+    void setCoefficients(double a, double b, double c);
+
+    /** @brief Get the coefficients of the line equation as a vector.
+     * @return STL vector @a v such that @a v[0] contains \f$a\f$, @a v[1] contains \f$b\f$,
+     * and @a v[2] contains \f$c\f$. */
+    std::vector<double> coefficients() const;
+
+    /// Get the coefficients of the line equation by reference.
+    void coefficients(Coord &a, Coord &b, Coord &c) const;
+
+    /** @brief Check if the line has more than one point.
+     * A degenerate line can be created if the line is created from a line equation
+     * that has no solutions.
+     * @return True if the line has no points or exactly one point */
+    bool isDegenerate() const {
+        return _initial == _final;
+    }
+    /// Check if the line is horizontal (y is constant).
+    bool isHorizontal() const {
+        return _initial[Y] == _final[Y];
+    }
+    /// Check if the line is vertical (x is constant).
+    bool isVertical() const {
+        return _initial[X] == _final[X];
+    }
+
+    /** @brief Reparametrize the line so that it has unit speed.
+     * Note that the direction of the line may also change. */
+    void normalize() {
+        // this helps with the nasty case of a line that starts somewhere far
+        // and ends very close to the origin
+        if (L2sq(_final) < L2sq(_initial)) {
+            std::swap(_initial, _final);
+        }
+        Point v = _final - _initial;
+        v.normalize();
+        _final = _initial + v;
+    }
+    /** @brief Return a new line reparametrized for unit speed. */
+    Line normalized() const {
+        Point v = _final - _initial;
+        v.normalize();
+        Line ret(_initial, _initial + v);
+        return ret;
+    }
+    /// @}
+
+    /// @name Evaluate the line as a function.
+    ///@{
+    Point initialPoint() const {
+        return _initial;
+    }
+    Point finalPoint() const {
+        return _final;
+    }
+    Point pointAt(Coord t) const {
+        return lerp(t, _initial, _final);;
+    }
+
+    Coord valueAt(Coord t, Dim2 d) const {
+        return lerp(t, _initial[d], _final[d]);
+    }
+
+    Coord timeAt(Point const &p) const;
+
+    /** @brief Get a time value corresponding to a projection of a point on the line.
+     * @param p Arbitrary point.
+     * @return Time value corresponding to a point closest to @c p. */
+    Coord timeAtProjection(Point const& p) const {
+        if ( isDegenerate() ) return 0;
+        Point v = vector();
+        return dot(p - _initial, v) / dot(v, v);
+    }
+
+    /** @brief Find a point on the line closest to the query point.
+     * This is an alias for timeAtProjection(). */
+    Coord nearestTime(Point const &p) const {
+        return timeAtProjection(p);
+    }
+
+    std::vector<Coord> roots(Coord v, Dim2 d) const;
+    Coord root(Coord v, Dim2 d) const;
+    /// @}
+
+    /// @name Create other objects based on this line.
+    /// @{
+    void reverse() {
+        std::swap(_final, _initial);
+    }
+    /** @brief Create a line containing the same points, but in opposite direction.
+     * @return Line \f$g\f$ such that \f$g(t) = f(1-t)\f$ */
+    Line reversed() const {
+        Line result(_final, _initial);
+        return result;
+    }
+
+    /** @brief Same as segment(), but allocate the line segment dynamically. */
+    // TODO remove this?
+    Curve* portion(Coord  f, Coord t) const {
+        LineSegment* seg = new LineSegment(pointAt(f), pointAt(t));
+        return seg;
+    }
+
+    /** @brief Create a segment of this line.
+     * @param f Time value for the initial point of the segment
+     * @param t Time value for the final point of the segment
+     * @return Created line segment */
+    LineSegment segment(Coord  f, Coord t) const {
+        return LineSegment(pointAt(f), pointAt(t));
+    }
+
+    /// Return the portion of the line that is inside the given rectangle
+    std::optional<LineSegment> clip(Rect const &r) const;
+
+    /** @brief Create a ray starting at the specified time value.
+     * The created ray will go in the direction of the line's vector (in the direction
+     * of increasing time values).
+     * @param t Time value where the ray should start
+     * @return Ray starting at t and going in the direction of the vector */
+    Ray ray(Coord t) {
+        Ray result;
+        result.setOrigin(pointAt(t));
+        result.setVector(vector());
+        return result;
+    }
+
+    /** @brief Create a derivative of the line.
+     * The new line will always be degenerate. Its origin will be equal to this
+     * line's vector. */
+    Line derivative() const {
+        Point v = vector();
+        Line result(v, v);
+        return result;
+    }
+
+    /// Create a line transformed by an affine transformation.
+    Line transformed(Affine const& m) const {
+        Line l(_initial * m, _final * m);
+        return l;
+    }
+
+    /** @brief Get a unit vector normal to the line.
+     * If Y grows upwards, then this is the left normal. If Y grows downwards,
+     * then this is the right normal. */
+    Point normal() const {
+        return rot90(vector()).normalized();
+    }
+
+    // what does this do?
+    Point normalAndDist(double & dist) const {
+        Point n = normal();
+        dist = -dot(n, _initial);
+        return n;
+    }
+
+    /// Compute an affine matrix representing a reflection about the line.
+    Affine reflection() const {
+        Point v = versor();
+        Coord x2 = v[X]*v[X], y2 = v[Y]*v[Y], xy = v[X]*v[Y];
+        Affine m(x2-y2, 2.*xy,
+                 2.*xy, y2-x2,
+                 _initial[X], _initial[Y]);
+        m = Translate(-_initial) * m;
+        return m;
+    }
+
+    /** @brief Compute an affine which transforms all points on the line to zero X or Y coordinate.
+     * This operation is useful in reducing intersection problems to root-finding problems.
+     * There are many affines which do this transformation. This function returns one that
+     * preserves angles, areas and distances - a rotation combined with a translation, and
+     * additionally moves the initial point of the line to (0,0). This way it works without
+     * problems even for lines perpendicular to the target, though may in some cases have
+     * lower precision than e.g. a shear transform.
+     * @param d Which coordinate of points on the line should be zero after the transformation */
+    Affine rotationToZero(Dim2 d) const {
+        Point v = vector();
+        if (d == X) {
+            std::swap(v[X], v[Y]);
+        } else {
+            v[Y] = -v[Y];
+        }
+        Affine m = Translate(-_initial) * Rotate(v);
+        return m;
+    }
+    /** @brief Compute a rotation affine which transforms the line to one of the axes.
+     * @param d Which line should be the axis */
+    Affine rotationToAxis(Dim2 d) const {
+        Affine m = rotationToZero(other_dimension(d));
+        return m;
+    }
+
+    Affine transformTo(Line const &other) const;
+    /// @}
+
+    std::vector<ShapeIntersection> intersect(Line const &other) const;
+    std::vector<ShapeIntersection> intersect(Ray const &r) const;
+    std::vector<ShapeIntersection> intersect(LineSegment const &ls) const;
+
+    template <typename T>
+    Line &operator*=(T const &tr) {
+        BOOST_CONCEPT_ASSERT((TransformConcept<T>));
+        _initial *= tr;
+        _final *= tr;
+        return *this;
+    }
+
+    bool operator==(Line const &other) const {
+        if (distance(pointAt(nearestTime(other._initial)), other._initial) != 0) return false;
+        if (distance(pointAt(nearestTime(other._final)), other._final) != 0) return false;
+        return true;
+    }
+
+    template <typename T>
+    friend Line operator*(Line const &l, T const &tr) {
+        BOOST_CONCEPT_ASSERT((TransformConcept<T>));
+        Line result(l);
+        result *= tr;
+        return result;
+    }
+}; // end class Line
+
+/** @brief Removes intersections outside of the unit interval.
+ * A helper used to implement line segment intersections.
+ * @param xs Line intersections
+ * @param a Whether the first time value has to be in the unit interval
+ * @param b Whether the second time value has to be in the unit interval
+ * @return Appropriately filtered intersections */
+void filter_line_segment_intersections(std::vector<ShapeIntersection> &xs, bool a=false, bool b=true);
+void filter_ray_intersections(std::vector<ShapeIntersection> &xs, bool a=false, bool b=true);
+
+/// @brief Compute distance from point to line.
+/// @relates Line
+inline
+double distance(Point const &p, Line const &line)
+{
+    if (line.isDegenerate()) {
+        return ::Geom::distance(p, line.initialPoint());
+    } else {
+        Coord t = line.nearestTime(p);
+        return ::Geom::distance(line.pointAt(t), p);
+    }
+}
+
+inline
+bool are_near(Point const &p, Line const &line, double eps = EPSILON)
+{
+    return are_near(distance(p, line), 0, eps);
+}
+
+inline
+bool are_parallel(Line const &l1, Line const &l2, double eps = EPSILON)
+{
+    return are_near(cross(l1.vector(), l2.vector()), 0, eps);
+}
+
+/** @brief Test whether two lines are approximately the same.
+ * This tests for being parallel and the origin of one line being close to the other,
+ * so it tests whether the images of the lines are similar, not whether the same time values
+ * correspond to similar points. For example a line from (1,1) to (2,2) and a line from
+ * (-1,-1) to (0,0) will be the same, because their images match, even though there is
+ * no time value for which the lines give similar points.
+ * @relates Line */
+inline
+bool are_same(Line const &l1, Line const &l2, double eps = EPSILON)
+{
+    return are_parallel(l1, l2, eps) && are_near(l1.origin(), l2, eps);
+}
+
+/// Test whether two lines are perpendicular.
+/// @relates Line
+inline
+bool are_orthogonal(Line const &l1, Line const &l2, double eps = EPSILON)
+{
+    return are_near(dot(l1.vector(), l2.vector()), 0, eps);
+}
+
+// evaluate the angle between l1 and l2 rotating l1 in cw direction
+// until it overlaps l2
+// the returned value is an angle in the interval [0, PI[
+inline
+double angle_between(Line const& l1, Line const& l2)
+{
+    double angle = angle_between(l1.vector(), l2.vector());
+    if (angle < 0) angle += M_PI;
+    if (angle == M_PI) angle = 0;
+    return angle;
+}
+
+inline
+double distance(Point const &p, LineSegment const &seg)
+{
+    double t = seg.nearestTime(p);
+    return distance(p, seg.pointAt(t));
+}
+
+inline
+bool are_near(Point const &p, LineSegment const &seg, double eps = EPSILON)
+{
+    return are_near(distance(p, seg), 0, eps);
+}
+
+// build a line passing by _point and orthogonal to _line
+inline
+Line make_orthogonal_line(Point const &p, Line const &line)
+{
+    Point d = line.vector().cw();
+    Line l(p, p + d);
+    return l;
+}
+
+// build a line passing by _point and parallel to _line
+inline
+Line make_parallel_line(Point const &p, Line const &line)
+{
+    Line result(line);
+    result.setOrigin(p);
+    return result;
+}
+
+// build a line passing by the middle point of _segment and orthogonal to it.
+inline
+Line make_bisector_line(LineSegment const& _segment)
+{
+    return make_orthogonal_line( middle_point(_segment), Line(_segment) );
+}
+
+// build the bisector line of the angle between ray(O,A) and ray(O,B)
+inline
+Line make_angle_bisector_line(Point const &A, Point const &O, Point const &B)
+{
+    AngleInterval ival(Angle(A-O), Angle(B-O));
+    Angle bisect = ival.angleAt(0.5);
+    return Line(O, bisect);
+}
+
+// prj(P) = rot(v, Point( rot(-v, P-O)[X], 0 )) + O
+inline
+Point projection(Point const &p, Line const &line)
+{
+    return line.pointAt(line.nearestTime(p));
+}
+
+inline
+LineSegment projection(LineSegment const &seg, Line const &line)
+{
+    return line.segment(line.nearestTime(seg.initialPoint()),
+                        line.nearestTime(seg.finalPoint()));
+}
+
+inline
+std::optional<LineSegment> clip(Line const &l, Rect const &r) {
+    return l.clip(r);
+}
+
+
+namespace detail
+{
+
+OptCrossing intersection_impl(Ray const& r1, Line const& l2, unsigned int i);
+OptCrossing intersection_impl( LineSegment const& ls1,
+                             Line const& l2,
+                             unsigned int i );
+OptCrossing intersection_impl( LineSegment const& ls1,
+                             Ray const& r2,
+                             unsigned int i );
+}
+
+
+inline
+OptCrossing intersection(Ray const& r1, Line const& l2)
+{
+    return detail::intersection_impl(r1,  l2, 0);
+
+}
+
+inline
+OptCrossing intersection(Line const& l1, Ray const& r2)
+{
+    return detail::intersection_impl(r2,  l1, 1);
+}
+
+inline
+OptCrossing intersection(LineSegment const& ls1, Line const& l2)
+{
+    return detail::intersection_impl(ls1,  l2, 0);
+}
+
+inline
+OptCrossing intersection(Line const& l1, LineSegment const& ls2)
+{
+    return detail::intersection_impl(ls2,  l1, 1);
+}
+
+inline
+OptCrossing intersection(LineSegment const& ls1, Ray const& r2)
+{
+    return detail::intersection_impl(ls1,  r2, 0);
+
+}
+
+inline
+OptCrossing intersection(Ray const& r1, LineSegment const& ls2)
+{
+    return detail::intersection_impl(ls2,  r1, 1);
+}
+
+
+OptCrossing intersection(Line const& l1, Line const& l2);
+
+OptCrossing intersection(Ray const& r1, Ray const& r2);
+
+OptCrossing intersection(LineSegment const& ls1, LineSegment const& ls2);
+
+
+} // end namespace Geom
+
+
+#endif // LIB2GEOM_SEEN_LINE_H
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/linear.h b/include/2geom/linear.h
new file mode 100644
index 0000000..75c6e01
--- /dev/null
+++ b/include/2geom/linear.h
@@ -0,0 +1,167 @@
+/**
+ * \file
+ * \brief  Linear fragment function class
+ *//*
+ *  Authors:
+ *   Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *   Michael Sloan <mgsloan@gmail.com>
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright (C) 2006-2015 authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_LINEAR_H
+#define LIB2GEOM_SEEN_LINEAR_H
+
+#include <2geom/interval.h>
+#include <2geom/math-utils.h>
+
+namespace Geom {
+
+class SBasis;
+
+/**
+ * @brief Function that interpolates linearly between two values.
+ * @ingroup Fragments
+ */
+class Linear
+    : boost::additive< Linear
+    , boost::arithmetic< Linear, Coord
+    , boost::equality_comparable< Linear
+      > > >
+{
+public:
+    Coord a[2];
+    Linear() {a[0]=0; a[1]=0;}
+    Linear(Coord aa, Coord b) {a[0] = aa; a[1] = b;}
+    Linear(Coord aa) {a[0] = aa; a[1] = aa;}
+
+    Coord operator[](unsigned i) const {
+        assert(i < 2);
+        return a[i];
+    }
+    Coord &operator[](unsigned i) {
+        assert(i < 2);
+        return a[i];
+    }
+
+    //IMPL: FragmentConcept
+    typedef Coord output_type;
+    bool isZero(Coord eps=EPSILON) const { return are_near(a[0], 0., eps) && are_near(a[1], 0., eps); }
+    bool isConstant(Coord eps=EPSILON) const { return are_near(a[0], a[1], eps); }
+    bool isFinite() const { return std::isfinite(a[0]) && std::isfinite(a[1]); }
+
+    Coord at0() const { return a[0]; }
+    Coord &at0() { return a[0]; }
+    Coord at1() const { return a[1]; }
+    Coord &at1() { return a[1]; }
+
+    Coord valueAt(Coord t) const { return lerp(t, a[0], a[1]); }
+    Coord operator()(Coord t) const { return valueAt(t); }
+
+    // not very useful, but required for FragmentConcept
+    std::vector<Coord> valueAndDerivatives(Coord t, unsigned n) {
+        std::vector<Coord> result(n+1, 0.0);
+        result[0] = valueAt(t);
+        if (n >= 1) {
+            result[1] = a[1] - a[0];
+        }
+        return result;
+    }
+
+    //defined in sbasis.h
+    inline SBasis toSBasis() const;
+
+    OptInterval bounds_exact() const { return Interval(a[0], a[1]); }
+    OptInterval bounds_fast() const { return bounds_exact(); }
+    OptInterval bounds_local(double u, double v) const { return Interval(valueAt(u), valueAt(v)); }
+
+    double tri() const {
+        return a[1] - a[0];
+    }
+    double hat() const {
+        return (a[1] + a[0])/2;
+    }
+
+    // addition of other Linears
+    Linear &operator+=(Linear const &other) {
+        a[0] += other.a[0];
+        a[1] += other.a[1];
+        return *this;
+    }
+    Linear &operator-=(Linear const &other) {
+        a[0] -= other.a[0];
+        a[1] -= other.a[1];
+        return *this;
+    }
+
+    // 
+    Linear &operator+=(Coord x) {
+        a[0] += x; a[1] += x;
+        return *this;
+    }
+    Linear &operator-=(Coord x) {
+        a[0] -= x; a[1] -= x;
+        return *this;
+    }
+    Linear &operator*=(Coord x) {
+        a[0] *= x; a[1] *= x;
+        return *this;
+    }
+    Linear &operator/=(Coord x) {
+        a[0] /= x; a[1] /= x;
+        return *this;
+    }
+    Linear operator-() const {
+        Linear ret(-a[0], -a[1]);
+        return ret;
+    }
+
+    bool operator==(Linear const &other) const {
+        return a[0] == other.a[0] && a[1] == other.a[1];
+    }
+};
+
+inline Linear reverse(Linear const &a) { return Linear(a[1], a[0]); }
+inline Linear portion(Linear const &a, Coord from, Coord to) {
+    Linear result(a.valueAt(from), a.valueAt(to));
+    return result;
+}
+
+} // end namespace Geom
+
+#endif //LIB2GEOM_SEEN_LINEAR_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/math-utils.h b/include/2geom/math-utils.h
new file mode 100644
index 0000000..4c35a80
--- /dev/null
+++ b/include/2geom/math-utils.h
@@ -0,0 +1,140 @@
+/**
+ * \file
+ * \brief Low level math functions and compatibility wrappers
+ *//*
+ * Authors:
+ *   Johan Engelen <goejendaagh@zonnet.nl>
+ *   Michael G. Sloan <mgsloan@gmail.com>
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Copyright 2006-2009 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_MATH_UTILS_H
+#define LIB2GEOM_SEEN_MATH_UTILS_H
+
+#include <math.h> // sincos is usually only available in math.h
+#include <array>
+#include <cmath>
+#include <utility> // for std::pair
+#include <boost/math/special_functions/fpclassify.hpp>
+
+namespace Geom {
+
+/** @brief Sign function - indicates the sign of a numeric type.
+ * Mathsy people will know this is basically the derivative of abs, except for the fact
+ * that it is defined on 0.
+ * @return -1 when x is negative, 1 when positive, and 0 if equal to 0. */
+template <class T> inline int sgn(const T& x) {
+    return (x < 0 ? -1 : (x > 0 ? 1 : 0) );
+// can we 'optimize' this with:
+//    return ( (T(0) < x) - (x < T(0)) );
+}
+
+template <class T> inline T sqr(const T& x) {return x * x;}
+template <class T> inline T cube(const T& x) {return x * x * x;}
+
+/** Between function - returns true if a number x is within a range: (min < x) && (max > x).
+ * The values delimiting the range and the number must have the same type.
+ */
+template <class T> inline const T& between (const T& min, const T& max, const T& x)
+    { return (min < x) && (max > x); }
+
+/** @brief Returns @a x rounded to the nearest multiple of \f$10^{p}\f$.
+
+    Implemented in terms of round, i.e. we make no guarantees as to what happens if x is
+    half way between two rounded numbers.
+
+    Note: places is the number of decimal places without using scientific (e) notation, not the
+    number of significant figures.  This function may not be suitable for values of x whose
+    magnitude is so far from 1 that one would want to use scientific (e) notation.
+
+    places may be negative: e.g. places = -2 means rounding to a multiple of .01
+**/
+inline double decimal_round(double x, int p) {
+    //TODO: possibly implement with modulus instead?
+    double const multiplier = ::pow(10.0, p);
+    return ::round( x * multiplier ) / multiplier;
+}
+
+/** @brief Simultaneously compute a sine and a cosine of the same angle.
+ * This function can be up to 2 times faster than separate computation, depending
+ * on the platform. It uses the standard library function sincos() if available.
+ * @param angle Angle
+ * @param sin_ Variable that will store the sine
+ * @param cos_ Variable that will store the cosine */
+inline void sincos(double angle, double &sin_, double &cos_) {
+#ifdef HAVE_SINCOS
+    ::sincos(angle, &sin_, &cos_);
+#else
+    sin_ = ::sin(angle);
+    cos_ = ::cos(angle);
+#endif
+}
+
+/** @brief Scale the doubles in the passed array to make them "reasonably large".
+ *
+ * All doubles in the passed array will get scaled by the same power of 2 (which is
+ * a lossless operation) in such a way that their geometric average gets closer to 1.
+ *
+ * @tparam N The size of the passed array.
+ * @param[in,out] values The doubles to be rescaled in place.
+ * @return The exponent in the power of two by which the doubles got scaled.
+ */
+template <size_t N>
+inline int rescale_homogenous(std::array<double, N> &values)
+{
+    if constexpr (N == 0) {
+        return 0;
+    }
+    std::array<int, N> exponents;
+    std::array<double, N> mantissas;
+    int average = 0;
+    for (size_t i = 0; i < N; i++) {
+        mantissas[i] = std::frexp(values[i], &exponents[i]);
+        average += exponents[i];
+    }
+    average /= (int)N;
+    for (size_t i = 0; i < N; i++) {
+        values[i] = std::ldexp(mantissas[i], exponents[i] - average);
+    }
+    return -average;
+}
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_MATH_UTILS_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/nearest-time.h b/include/2geom/nearest-time.h
new file mode 100644
index 0000000..007cd27
--- /dev/null
+++ b/include/2geom/nearest-time.h
@@ -0,0 +1,141 @@
+/** @file
+ * @brief Nearest time routines for D2<SBasis> and Piecewise<D2<SBasis>>
+ *//*
+ * Authors:
+ *   Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 2007-2008  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+
+#ifndef LIB2GEOM_SEEN_NEAREST_TIME_H
+#define LIB2GEOM_SEEN_NEAREST_TIME_H
+
+
+#include <vector>
+
+#include <2geom/d2.h>
+#include <2geom/piecewise.h>
+#include <2geom/exception.h>
+#include <2geom/bezier.h>
+
+
+namespace Geom
+{
+
+/*
+ * Given a line L specified by a point A and direction vector v,
+ * return the point on L nearest to p. Note that the returned value
+ * is with respect to the _normalized_ direction of v!
+ */
+inline double nearest_time(Point const &p, Point const &A, Point const &v)
+{
+    Point d(p - A);
+    return d[0] * v[0] + d[1] * v[1];
+}
+
+Coord nearest_time(Point const &p, D2<Bezier> const &bez, Coord from = 0, Coord to = 1);
+
+////////////////////////////////////////////////////////////////////////////////
+// D2<SBasis> versions
+
+/*
+ * Return the parameter t of a nearest point on the portion of the curve "c",
+ * related to the interval [from, to], to the point "p".
+ * The needed curve derivative "deriv" is passed as parameter.
+ * The function return the first nearest point to "p" that is found.
+ */
+double nearest_time(Point const &p,
+                    D2<SBasis> const &c, D2<SBasis> const &deriv,
+                    double from = 0, double to = 1);
+
+inline
+double nearest_time(Point const &p,
+                    D2<SBasis> const &c,
+                    double from = 0, double to = 1 )
+{
+    return nearest_time(p, c, Geom::derivative(c), from, to);
+}
+
+/*
+ * Return the parameters t of all the nearest times on the portion of
+ * the curve "c", related to the interval [from, to], to the point "p".
+ * The needed curve derivative "dc" is passed as parameter.
+ */
+std::vector<double>
+all_nearest_times(Point const& p,
+                  D2<SBasis> const& c, D2<SBasis> const& dc,
+                  double from = 0, double to = 1 );
+
+inline
+std::vector<double>
+all_nearest_times(Point const &p,
+                  D2<SBasis> const &c,
+                  double from = 0, double to = 1)
+{
+    return all_nearest_times(p, c,  Geom::derivative(c), from, to);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Piecewise< D2<SBasis> > versions
+
+double nearest_time(Point const &p,
+                    Piecewise< D2<SBasis> > const &c,
+                    double from, double to);
+
+inline
+double nearest_time(Point const& p, Piecewise< D2<SBasis> > const &c)
+{
+    return nearest_time(p, c, c.cuts[0], c.cuts[c.size()]);
+}
+
+
+std::vector<double>
+all_nearest_times(Point const &p,
+                  Piecewise< D2<SBasis> > const &c,
+                  double from, double to);
+
+inline
+std::vector<double>
+all_nearest_times( Point const& p, Piecewise< D2<SBasis> > const& c )
+{
+    return all_nearest_times(p, c, c.cuts[0], c.cuts[c.size()]);
+}
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_NEAREST_TIME_H
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/numeric/fitting-model.h b/include/2geom/numeric/fitting-model.h
new file mode 100644
index 0000000..0316f57
--- /dev/null
+++ b/include/2geom/numeric/fitting-model.h
@@ -0,0 +1,521 @@
+/*
+ * Fitting Models for Geom Types
+ *
+ * Authors:
+ *      Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 2008  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+
+#ifndef _NL_FITTING_MODEL_H_
+#define _NL_FITTING_MODEL_H_
+
+
+#include <2geom/d2.h>
+#include <2geom/sbasis.h>
+#include <2geom/bezier.h>
+#include <2geom/bezier-curve.h>
+#include <2geom/polynomial.h>
+#include <2geom/ellipse.h>
+#include <2geom/circle.h>
+#include <2geom/utils.h>
+#include <2geom/conicsec.h>
+
+
+namespace Geom { namespace NL {
+
+/*
+ * A model is an abstraction for an expression dependent from a parameter where
+ * the coefficients of this expression are the unknowns of the fitting problem.
+ * For a ceratain number of parameter values we know the related values
+ * the expression evaluates to: from each parameter value we get a row of
+ * the matrix of the fitting problem, from each expression value we get
+ * the related constant term.
+ * Example: given the model a*x^2 + b*x + c = 0; from x = 1 we get
+ * the equation a + b + c = 0, in this example the constant term is always
+ * the same for each parameter value.
+ *
+ * A model is required to implement 3 methods:
+ *
+ *  - size : returns the number of unknown coefficients that appear in
+ *           the expression of the fitting problem;
+ *  - feed : its input is a parameter value and the related expression value,
+ *           it generates a matrix row and a new entry of the constant vector
+ *           of the fitting problem;
+ *  - instance : it has an input parameter represented by the raw vector
+ *               solution of the fitting problem and an output parameter
+ *               of type InstanceType that return a specific object that is
+ *               generated using the fitting problem solution, in the example
+ *               above the object could be a Poly type.
+ */
+
+/*
+ *   completely unknown models must inherit from this template class;
+ *   example: the model a*x^2 + b*x + c = 0 to be solved wrt a, b, c;
+ *   example: the model A(t) = known_sample_value_at(t) to be solved wrt
+ *       the coefficients of the curve A(t) expressed in S-Basis form;
+ *   parameter type: the type of x and t variable in the examples above;
+ *   value type:     the type of the known sample values (in the first example
+ *                   is constant )
+ *   instance type:  the type of the objects produced by using
+ *                   the fitting raw data solution
+ */
+
+
+
+
+template< typename ParameterType, typename ValueType, typename InstanceType >
+class LinearFittingModel
+{
+  public:
+    typedef ParameterType       parameter_type;
+    typedef ValueType           value_type;
+    typedef InstanceType        instance_type;
+
+    static const bool WITH_FIXED_TERMS = false;
+
+    /*
+     * a LinearFittingModel must implement the following methods:
+     *
+     * void feed( VectorView & vector,
+     *            parameter_type const& sample_parameter ) const;
+     *
+     * size_t size() const;
+     *
+     * void instance(instance_type &, raw_type const& raw_data) const;
+     *
+     */
+};
+
+
+/*
+ *   partially known models must inherit from this template class
+ *   example: the model a*x^2 + 2*x + c = 0 to be solved wrt a and c
+ */
+template< typename ParameterType, typename ValueType, typename InstanceType >
+class LinearFittingModelWithFixedTerms
+{
+  public:
+    typedef ParameterType       parameter_type;
+    typedef ValueType           value_type;
+    typedef InstanceType        instance_type;
+
+    static const bool WITH_FIXED_TERMS = true;
+
+    /*
+     * a LinearFittingModelWithFixedTerms must implement the following methods:
+     *
+     * void feed( VectorView & vector,
+     *            value_type & fixed_term,
+     *            parameter_type const& sample_parameter ) const;
+     *
+     * size_t size() const;
+     *
+     * void instance(instance_type &, raw_type const& raw_data) const;
+     *
+     */
+
+
+};
+
+
+// incomplete model, it can be inherited to make up different kinds of
+// instance type; the raw data is a vector of coefficients of a polynomial
+// represented in standard power basis
+template< typename InstanceType >
+class LFMPowerBasis
+    : public LinearFittingModel<double, double, InstanceType>
+{
+  public:
+    LFMPowerBasis(size_t degree)
+        : m_size(degree + 1)
+    {
+    }
+
+    void feed( VectorView & coeff, double sample_parameter ) const
+    {
+        coeff[0] = 1;
+        double x_i = 1;
+        for (size_t i = 1; i < coeff.size(); ++i)
+        {
+          x_i *= sample_parameter;
+          coeff[i] = x_i;
+        }
+    }
+
+    size_t size() const
+    {
+        return m_size;
+    }
+
+  private:
+    size_t m_size;
+};
+
+
+// this model generates Geom::Poly objects
+class LFMPoly
+    : public LFMPowerBasis<Poly>
+{
+  public:
+    LFMPoly(size_t degree)
+        : LFMPowerBasis<Poly>(degree)
+    {
+    }
+
+    void instance(Poly & poly, ConstVectorView const& raw_data) const
+    {
+        poly.clear();
+        poly.resize(size());
+        for (size_t i = 0; i < raw_data.size(); ++i)
+        {
+            poly[i] =  raw_data[i];
+        }
+    }
+};
+
+
+// incomplete model, it can be inherited to make up different kinds of
+// instance type; the raw data is a vector of coefficients of a polynomial
+// represented in standard power basis with leading term coefficient equal to 1
+template< typename InstanceType >
+class LFMNormalizedPowerBasis
+    : public LinearFittingModelWithFixedTerms<double, double, InstanceType>
+{
+  public:
+    LFMNormalizedPowerBasis(size_t _degree)
+        : m_model( _degree - 1)
+    {
+        assert(_degree > 0);
+    }
+
+
+    void feed( VectorView & coeff,
+               double & known_term,
+               double sample_parameter ) const
+    {
+        m_model.feed(coeff, sample_parameter);
+        known_term = coeff[m_model.size()-1] * sample_parameter;
+    }
+
+    size_t size() const
+    {
+        return m_model.size();
+    }
+
+  private:
+    LFMPowerBasis<InstanceType> m_model;
+};
+
+
+// incomplete model, it can be inherited to make up different kinds of
+// instance type; the raw data is a vector of coefficients of the equation
+// of an ellipse curve
+//template< typename InstanceType >
+//class LFMEllipseEquation
+//    : public LinearFittingModelWithFixedTerms<Point, double, InstanceType>
+//{
+//  public:
+//    void feed( VectorView & coeff, double & fixed_term, Point const& p ) const
+//    {
+//        coeff[0] = p[X] * p[Y];
+//        coeff[1] = p[Y] * p[Y];
+//        coeff[2] = p[X];
+//        coeff[3] = p[Y];
+//        coeff[4] = 1;
+//        fixed_term = p[X] * p[X];
+//    }
+//
+//    size_t size() const
+//    {
+//        return 5;
+//    }
+//};
+
+// incomplete model, it can be inherited to make up different kinds of
+// instance type; the raw data is a vector of coefficients of the equation
+// of a conic section
+template< typename InstanceType >
+class LFMConicEquation
+    : public LinearFittingModelWithFixedTerms<Point, double, InstanceType>
+{
+  public:
+    void feed( VectorView & coeff, double & fixed_term, Point const& p ) const
+    {
+        coeff[0] = p[X] * p[Y];
+        coeff[1] = p[Y] * p[Y];
+        coeff[2] = p[X];
+        coeff[3] = p[Y];
+        coeff[4] = 1;
+        fixed_term = p[X] * p[X];
+    }
+
+    size_t size() const
+    {
+        return 5;
+    }
+};
+
+// this model generates Ellipse curves
+class LFMConicSection
+    : public LFMConicEquation<xAx>
+{
+  public:
+    void instance(xAx & c, ConstVectorView const& coeff) const
+    {
+        c.set(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]);
+    }
+};
+
+// this model generates Ellipse curves
+class LFMEllipse
+    : public LFMConicEquation<Ellipse>
+{
+  public:
+    void instance(Ellipse & e, ConstVectorView const& coeff) const
+    {
+        e.setCoefficients(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]);
+    }
+};
+
+
+// incomplete model, it can be inherited to make up different kinds of
+// instance type; the raw data is a vector of coefficients of the equation
+// of a circle curve
+template< typename InstanceType >
+class LFMCircleEquation
+    : public LinearFittingModelWithFixedTerms<Point, double, InstanceType>
+{
+  public:
+    void feed( VectorView & coeff, double & fixed_term, Point const& p ) const
+    {
+        coeff[0] = p[X];
+        coeff[1] = p[Y];
+        coeff[2] = 1;
+        fixed_term = p[X] * p[X] + p[Y] * p[Y];
+    }
+
+    size_t size() const
+    {
+        return 3;
+    }
+};
+
+
+// this model generates Ellipse curves
+class LFMCircle
+    : public LFMCircleEquation<Circle>
+{
+  public:
+    void instance(Circle & c, ConstVectorView const& coeff) const
+    {
+        c.setCoefficients(1, coeff[0], coeff[1], coeff[2]);
+    }
+};
+
+
+// this model generates SBasis objects
+class LFMSBasis
+    : public LinearFittingModel<double, double, SBasis>
+{
+  public:
+    LFMSBasis( size_t _order )
+        : m_size( 2*(_order+1) ),
+          m_order(_order)
+    {
+    }
+
+    void feed( VectorView & coeff, double t ) const
+    {
+        double u0 = 1-t;
+        double u1 = t;
+        double s = u0 * u1;
+        coeff[0] = u0;
+        coeff[1] = u1;
+        for (size_t i = 2; i < size(); i+=2)
+        {
+            u0 *= s;
+            u1 *= s;
+            coeff[i] = u0;
+            coeff[i+1] = u1;
+        }
+    }
+
+    size_t size() const
+    {
+        return m_size;
+    }
+
+    void instance(SBasis & sb, ConstVectorView const& raw_data) const
+    {
+        sb.resize(m_order+1);
+        for (unsigned int i = 0, k = 0; i < raw_data.size(); i+=2, ++k)
+        {
+            sb[k][0] = raw_data[i];
+            sb[k][1] = raw_data[i+1];
+        }
+    }
+
+  private:
+    size_t m_size;
+    size_t m_order;
+};
+
+
+// this model generates D2<SBasis> objects
+class LFMD2SBasis
+    : public LinearFittingModel< double, Point, D2<SBasis> >
+{
+  public:
+    LFMD2SBasis( size_t _order )
+        : mosb(_order)
+    {
+    }
+
+    void feed( VectorView & coeff, double t ) const
+    {
+        mosb.feed(coeff, t);
+    }
+
+    size_t size() const
+    {
+        return mosb.size();
+    }
+
+    void instance(D2<SBasis> & d2sb, ConstMatrixView const& raw_data) const
+    {
+        mosb.instance(d2sb[X], raw_data.column_const_view(X));
+        mosb.instance(d2sb[Y], raw_data.column_const_view(Y));
+    }
+
+  private:
+    LFMSBasis mosb;
+};
+
+
+// this model generates Bezier objects
+class LFMBezier
+    : public LinearFittingModel<double, double, Bezier>
+{
+  public:
+    LFMBezier( size_t _order )
+        : m_size(_order + 1),
+          m_order(_order)
+    {
+        binomial_coefficients(m_bc, m_order);
+    }
+
+    void feed( VectorView & coeff, double t ) const
+    {
+        double s = 1;
+        for (size_t i = 0; i < size(); ++i)
+        {
+            coeff[i] = s * m_bc[i];
+            s *= t;
+        }
+        double u = 1-t;
+        s = 1;
+        for (size_t i = size()-1; i > 0; --i)
+        {
+            coeff[i] *= s;
+            s *= u;
+        }
+        coeff[0] *= s;
+    }
+
+    size_t size() const
+    {
+        return m_size;
+    }
+
+    void instance(Bezier & b, ConstVectorView const& raw_data) const
+    {
+        assert(b.size() == raw_data.size());
+        for (unsigned int i = 0; i < raw_data.size(); ++i)
+        {
+            b[i] = raw_data[i];
+        }
+    }
+
+  private:
+    size_t m_size;
+    size_t m_order;
+    std::vector<size_t> m_bc;
+};
+
+
+// this model generates Bezier curves
+template <unsigned degree>
+class LFMBezierCurveN
+    : public LinearFittingModel< double, Point, BezierCurveN<degree> >
+{
+  public:
+    LFMBezierCurveN()
+        : mob(degree+1)
+    {
+    }
+
+    void feed( VectorView & coeff, double t ) const
+    {
+        mob.feed(coeff, t);
+    }
+
+    size_t size() const
+    {
+        return mob.size();
+    }
+
+    void instance(BezierCurveN<degree> & bc, ConstMatrixView const& raw_data) const
+    {
+        Bezier bx(degree);
+        Bezier by(degree);
+        mob.instance(bx, raw_data.column_const_view(X));
+        mob.instance(by, raw_data.column_const_view(Y));
+        bc = BezierCurveN<degree>(bx, by);
+    }
+
+  private:
+    LFMBezier mob;
+};
+
+}  // end namespace NL
+}  // end namespace Geom
+
+
+#endif // _NL_FITTING_MODEL_H_
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/numeric/fitting-tool.h b/include/2geom/numeric/fitting-tool.h
new file mode 100644
index 0000000..78d66ca
--- /dev/null
+++ b/include/2geom/numeric/fitting-tool.h
@@ -0,0 +1,562 @@
+/*
+ * Fitting Tools
+ *
+ * Authors:
+ *      Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 2008  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+
+#ifndef _NL_FITTING_TOOL_H_
+#define _NL_FITTING_TOOL_H_
+
+
+#include <2geom/numeric/vector.h>
+#include <2geom/numeric/matrix.h>
+
+#include <2geom/point.h>
+
+#include <vector>
+
+
+/*
+ *  The least_square_fitter class represents a tool for solving a fitting
+ *  problem with respect to a given model that represents an expression
+ *  dependent from a parameter where the coefficients of this expression
+ *  are the unknowns of the fitting problem.
+ *  The minimizing solution is found by computing the pseudo-inverse
+ *  of the problem matrix
+ */
+
+
+namespace Geom { namespace NL {
+
+namespace detail {
+
+template< typename ModelT>
+class lsf_base
+{
+  public:
+    typedef ModelT                                   model_type;
+    typedef typename model_type::parameter_type      parameter_type;
+    typedef typename model_type::value_type          value_type;
+
+    lsf_base( model_type const& _model, size_t forecasted_samples )
+        : m_model(_model),
+          m_total_samples(0),
+          m_matrix(forecasted_samples, m_model.size()),
+          m_psdinv_matrix(NULL)
+    {
+    }
+
+    // compute pseudo inverse
+    void update()
+    {
+        if (total_samples() == 0) return;
+        if (m_psdinv_matrix != NULL)
+        {
+            delete m_psdinv_matrix;
+        }
+        MatrixView mv(m_matrix, 0, 0, total_samples(), m_matrix.columns());
+        m_psdinv_matrix = new Matrix( pseudo_inverse(mv) );
+        assert(m_psdinv_matrix != NULL);
+    }
+
+    size_t total_samples() const
+    {
+        return m_total_samples;
+    }
+
+    bool is_full() const
+    {
+        return (total_samples() == m_matrix.rows());
+    }
+
+    void clear()
+    {
+        m_total_samples = 0;
+    }
+
+    virtual
+    ~lsf_base()
+    {
+        if (m_psdinv_matrix != NULL)
+        {
+            delete m_psdinv_matrix;
+        }
+    }
+
+  protected:
+    const model_type &      m_model;
+    size_t                  m_total_samples;
+    Matrix                  m_matrix;
+    Matrix*                 m_psdinv_matrix;
+
+};  // end class lsf_base
+
+
+
+
+template< typename ModelT, typename ValueType = typename ModelT::value_type>
+class lsf_solution
+{
+};
+
+// a fitting process on samples with value of type double
+// produces a solution of type Vector
+template< typename ModelT>
+class lsf_solution<ModelT, double>
+    : public lsf_base<ModelT>
+{
+public:
+    typedef ModelT                                  model_type;
+    typedef typename model_type::parameter_type     parameter_type;
+    typedef typename model_type::value_type         value_type;
+    typedef Vector                                  solution_type;
+    typedef lsf_base<model_type>                    base_type;
+
+    using base_type::m_model;
+    using base_type::m_psdinv_matrix;
+    using base_type::total_samples;
+
+public:
+    lsf_solution<ModelT, double>( model_type const& _model,
+                                  size_t forecasted_samples )
+        : base_type(_model, forecasted_samples),
+          m_solution(_model.size())
+    {
+    }
+
+    template< typename VectorT >
+    solution_type& result(VectorT const& sample_values)
+    {
+        assert(sample_values.size() == total_samples());
+        ConstVectorView sv(sample_values);
+        m_solution = (*m_psdinv_matrix) * sv;
+        return m_solution;
+    }
+
+    // a comparison between old sample values and the new ones is performed
+    // in order to minimize computation
+    // prerequisite:
+    //     old_sample_values.size() == new_sample_values.size()
+    //     no update() call can be performed between two result invocations
+    template< typename VectorT >
+    solution_type& result( VectorT const& old_sample_values,
+                           VectorT const& new_sample_values )
+    {
+        assert(old_sample_values.size() == total_samples());
+        assert(new_sample_values.size() == total_samples());
+        Vector diff(total_samples());
+        for (size_t i = 0; i < diff.size(); ++i)
+        {
+            diff[i] = new_sample_values[i] - old_sample_values[i];
+        }
+        Vector column(m_model.size());
+        Vector delta(m_model.size(), 0.0);
+        for (size_t i = 0; i < diff.size(); ++i)
+        {
+            if (diff[i] != 0)
+            {
+                column = m_psdinv_matrix->column_view(i);
+                column.scale(diff[i]);
+                delta += column;
+            }
+        }
+        m_solution += delta;
+        return m_solution;
+    }
+
+    solution_type& result()
+    {
+        return m_solution;
+    }
+
+private:
+    solution_type m_solution;
+
+}; // end class lsf_solution<ModelT, double>
+
+
+// a fitting process on samples with value of type Point
+// produces a solution of type Matrix (with 2 columns)
+template< typename ModelT>
+class lsf_solution<ModelT, Point>
+    : public lsf_base<ModelT>
+{
+public:
+    typedef ModelT                                  model_type;
+    typedef typename model_type::parameter_type     parameter_type;
+    typedef typename model_type::value_type         value_type;
+    typedef Matrix                                  solution_type;
+    typedef lsf_base<model_type>                    base_type;
+
+    using base_type::m_model;
+    using base_type::m_psdinv_matrix;
+    using base_type::total_samples;
+
+public:
+    lsf_solution<ModelT, Point>( model_type const& _model,
+                                 size_t forecasted_samples )
+        : base_type(_model, forecasted_samples),
+          m_solution(_model.size(), 2)
+    {
+    }
+
+    solution_type& result(std::vector<Point> const& sample_values)
+    {
+        assert(sample_values.size() == total_samples());
+        Matrix svm(total_samples(), 2);
+        for (size_t i = 0; i < total_samples(); ++i)
+        {
+            svm(i, X) = sample_values[i][X];
+            svm(i, Y) = sample_values[i][Y];
+        }
+        m_solution = (*m_psdinv_matrix) * svm;
+        return m_solution;
+    }
+
+    // a comparison between old sample values and the new ones is performed
+    // in order to minimize computation
+    // prerequisite:
+    //     old_sample_values.size() == new_sample_values.size()
+    //     no update() call can to be performed between two result invocations
+    solution_type& result( std::vector<Point> const& old_sample_values,
+                           std::vector<Point> const& new_sample_values )
+    {
+        assert(old_sample_values.size() == total_samples());
+        assert(new_sample_values.size() == total_samples());
+        Matrix diff(total_samples(), 2);
+        for (size_t i = 0; i < total_samples(); ++i)
+        {
+            diff(i, X) =  new_sample_values[i][X] - old_sample_values[i][X];
+            diff(i, Y) =  new_sample_values[i][Y] - old_sample_values[i][Y];
+        }
+        Vector column(m_model.size());
+        Matrix delta(m_model.size(), 2, 0.0);
+        VectorView deltax = delta.column_view(X);
+        VectorView deltay = delta.column_view(Y);
+        for (size_t i = 0; i < total_samples(); ++i)
+        {
+            if (diff(i, X) != 0)
+            {
+                column = m_psdinv_matrix->column_view(i);
+                column.scale(diff(i, X));
+                deltax += column;
+            }
+            if (diff(i, Y) != 0)
+            {
+                column = m_psdinv_matrix->column_view(i);
+                column.scale(diff(i, Y));
+                deltay += column;
+            }
+        }
+        m_solution += delta;
+        return m_solution;
+    }
+
+    solution_type& result()
+    {
+        return m_solution;
+    }
+
+private:
+    solution_type m_solution;
+
+}; // end class lsf_solution<ModelT, Point>
+
+
+
+
+template< typename ModelT,
+          bool WITH_FIXED_TERMS = ModelT::WITH_FIXED_TERMS >
+class lsf_with_fixed_terms
+{
+};
+
+
+// fitting tool for completely unknown models
+template< typename ModelT>
+class lsf_with_fixed_terms<ModelT, false>
+    : public lsf_solution<ModelT>
+{
+  public:
+    typedef ModelT                                      model_type;
+    typedef typename model_type::parameter_type         parameter_type;
+    typedef typename model_type::value_type             value_type;
+    typedef lsf_solution<model_type>                    base_type;
+    typedef typename base_type::solution_type           solution_type;
+
+    using base_type::total_samples;
+    using base_type::is_full;
+    using base_type::m_matrix;
+    using base_type::m_total_samples;
+    using base_type::m_model;
+
+  public:
+      lsf_with_fixed_terms<ModelT, false>( model_type const& _model,
+                                           size_t forecasted_samples )
+        : base_type(_model, forecasted_samples)
+    {
+    }
+
+    void append(parameter_type const& sample_parameter)
+    {
+        assert(!is_full());
+        VectorView row = m_matrix.row_view(total_samples());
+        m_model.feed(row, sample_parameter);
+        ++m_total_samples;
+    }
+
+    void append_copy(size_t sample_index)
+    {
+        assert(!is_full());
+        assert(sample_index < total_samples());
+        VectorView dest_row = m_matrix.row_view(total_samples());
+        VectorView source_row = m_matrix.row_view(sample_index);
+        dest_row = source_row;
+        ++m_total_samples;
+    }
+
+}; // end class lsf_with_fixed_terms<ModelT, false>
+
+
+// fitting tool for partially known models
+template< typename ModelT>
+class lsf_with_fixed_terms<ModelT, true>
+    : public lsf_solution<ModelT>
+{
+  public:
+    typedef ModelT                                      model_type;
+    typedef typename model_type::parameter_type         parameter_type;
+    typedef typename model_type::value_type             value_type;
+    typedef lsf_solution<model_type>                    base_type;
+    typedef typename base_type::solution_type           solution_type;
+
+    using base_type::total_samples;
+    using base_type::is_full;
+    using base_type::m_matrix;
+    using base_type::m_total_samples;
+    using base_type::m_model;
+
+  public:
+    lsf_with_fixed_terms<ModelT, true>( model_type const& _model,
+                                        size_t forecasted_samples )
+        : base_type(_model, forecasted_samples),
+          m_vector(forecasted_samples),
+          m_vector_view(NULL)
+    {
+    }
+    void append(parameter_type const& sample_parameter)
+    {
+        assert(!is_full());
+        VectorView row = m_matrix.row_view(total_samples());
+        m_model.feed(row, m_vector[total_samples()], sample_parameter);
+        ++m_total_samples;
+    }
+
+    void append_copy(size_t sample_index)
+    {
+        assert(!is_full());
+        assert(sample_index < total_samples());
+        VectorView dest_row = m_matrix.row_view(total_samples());
+        VectorView source_row = m_matrix.row_view(sample_index);
+        dest_row = source_row;
+        m_vector[total_samples()] = m_vector[sample_index];
+        ++m_total_samples;
+    }
+
+    void update()
+    {
+        base_type::update();
+        if (total_samples() == 0) return;
+        if (m_vector_view != NULL)
+        {
+            delete m_vector_view;
+        }
+        m_vector_view = new VectorView(m_vector, base_type::total_samples());
+        assert(m_vector_view != NULL);
+    }
+
+    
+    ~lsf_with_fixed_terms<model_type, true>() override
+    {
+        if (m_vector_view != NULL)
+        {
+            delete m_vector_view;
+        }
+    }
+
+  protected:
+    Vector          m_vector;
+    VectorView*     m_vector_view;
+
+}; // end class lsf_with_fixed_terms<ModelT, true>
+
+
+} // end namespace detail
+
+
+
+
+template< typename ModelT,
+          typename ValueType = typename ModelT::value_type,
+          bool WITH_FIXED_TERMS = ModelT::WITH_FIXED_TERMS >
+class least_squeares_fitter
+{
+};
+
+
+template< typename ModelT, typename ValueType >
+class least_squeares_fitter<ModelT, ValueType, false>
+    : public detail::lsf_with_fixed_terms<ModelT>
+{
+  public:
+    typedef ModelT                                      model_type;
+    typedef detail::lsf_with_fixed_terms<model_type>    base_type;
+    typedef typename base_type::parameter_type          parameter_type;
+    typedef typename base_type::value_type              value_type;
+    typedef typename base_type::solution_type           solution_type;
+
+  public:
+    least_squeares_fitter<ModelT, ValueType, false>( model_type const& _model,
+                                                     size_t forecasted_samples )
+          : base_type(_model, forecasted_samples)
+  {
+  }
+}; // end class least_squeares_fitter<ModelT, ValueType, true>
+
+
+template< typename ModelT>
+class least_squeares_fitter<ModelT, double, true>
+    : public detail::lsf_with_fixed_terms<ModelT>
+{
+  public:
+    typedef ModelT                                      model_type;
+    typedef detail::lsf_with_fixed_terms<model_type>    base_type;
+    typedef typename base_type::parameter_type          parameter_type;
+    typedef typename base_type::value_type              value_type;
+    typedef typename base_type::solution_type           solution_type;
+
+    using base_type::m_vector_view;
+    //using base_type::result; // VSC legacy support
+    solution_type& result( std::vector<Point> const& old_sample_values,
+                           std::vector<Point> const& new_sample_values )
+    {
+        return base_type::result(old_sample_values, new_sample_values);
+    }
+ 
+    solution_type& result()
+    {
+        return base_type::result();
+    }
+
+  public:
+    least_squeares_fitter<ModelT, double, true>( model_type const& _model,
+                                                 size_t forecasted_samples )
+        : base_type(_model, forecasted_samples)
+    {
+    }
+
+    template< typename VectorT >
+    solution_type& result(VectorT const& sample_values)
+    {
+        assert(sample_values.size() == m_vector_view->size());
+        Vector sv(sample_values.size());
+        for (size_t i = 0; i < sv.size(); ++i)
+            sv[i] = sample_values[i] - (*m_vector_view)[i];
+        return base_type::result(sv);
+    }
+
+}; // end class least_squeares_fitter<ModelT, double, true>
+
+
+template< typename ModelT>
+class least_squeares_fitter<ModelT, Point, true>
+    : public detail::lsf_with_fixed_terms<ModelT>
+{
+  public:
+    typedef ModelT                                      model_type;
+    typedef detail::lsf_with_fixed_terms<model_type>    base_type;
+    typedef typename base_type::parameter_type          parameter_type;
+    typedef typename base_type::value_type              value_type;
+    typedef typename base_type::solution_type           solution_type;
+
+    using base_type::m_vector_view;
+    //using base_type::result; // VCS legacy support
+    solution_type& result( std::vector<Point> const& old_sample_values,
+                           std::vector<Point> const& new_sample_values )
+    {
+        return base_type::result(old_sample_values, new_sample_values);
+    }
+ 
+    solution_type& result()
+    {
+        return base_type::result();
+    }
+
+
+  public:
+    least_squeares_fitter<ModelT, Point, true>( model_type const& _model,
+                                                size_t forecasted_samples )
+        : base_type(_model, forecasted_samples)
+    {
+    }
+
+    solution_type& result(std::vector<Point> const& sample_values)
+    {
+        assert(sample_values.size() == m_vector_view->size());
+        NL::Matrix sv(sample_values.size(), 2);
+        for (size_t i = 0; i < sample_values.size(); ++i)
+        {
+            sv(i, X) = sample_values[i][X] - (*m_vector_view)[i];
+            sv(i, Y) = sample_values[i][Y] - (*m_vector_view)[i];
+        }
+        return base_type::result(sv);
+    }
+
+};  // end class least_squeares_fitter<ModelT, Point, true>
+
+
+}  // end namespace NL
+}  // end namespace Geom
+
+
+
+#endif  // _NL_FITTING_TOOL_H_
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/numeric/linear_system.h b/include/2geom/numeric/linear_system.h
new file mode 100644
index 0000000..f793e20
--- /dev/null
+++ b/include/2geom/numeric/linear_system.h
@@ -0,0 +1,138 @@
+/*
+ * LinearSystem class wraps some gsl routines for solving linear systems
+ *
+ * Authors:
+ * 		Marco Cecchetti <mrcekets at gmail.com>
+ * 
+ * Copyright 2008  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+
+#ifndef _NL_LINEAR_SYSTEM_H_
+#define _NL_LINEAR_SYSTEM_H_
+
+
+#include <cassert>
+
+#include <gsl/gsl_linalg.h>
+
+#include <2geom/numeric/matrix.h>
+#include <2geom/numeric/vector.h>
+
+
+namespace Geom { namespace NL {
+
+
+class LinearSystem
+{
+public:
+	LinearSystem(MatrixView & _matrix, VectorView & _vector)
+		: m_matrix(_matrix), m_vector(_vector), m_solution(_matrix.columns())
+	{
+	}
+	
+	LinearSystem(Matrix & _matrix, Vector & _vector)
+		: m_matrix(_matrix), m_vector(_vector), m_solution(_matrix.columns())
+	{
+	}
+	
+	const Vector & LU_solve()
+	{
+		assert( matrix().rows() == matrix().columns() 
+				&& matrix().rows() == vector().size() );
+		int s;
+		gsl_permutation * p = gsl_permutation_alloc(matrix().rows());
+		gsl_linalg_LU_decomp (matrix().get_gsl_matrix(), p, &s);
+		gsl_linalg_LU_solve( matrix().get_gsl_matrix(), 
+							 p, 
+				             vector().get_gsl_vector(), 
+				             m_solution.get_gsl_vector()
+				           );
+		gsl_permutation_free(p);
+		return solution();
+	}
+	
+	const Vector & SV_solve()
+	{
+		assert( matrix().rows() >= matrix().columns()
+				&& matrix().rows() == vector().size() );
+		
+		gsl_matrix* U = matrix().get_gsl_matrix();
+		gsl_matrix* V = gsl_matrix_alloc(matrix().columns(), matrix().columns());
+		gsl_vector* S = gsl_vector_alloc(matrix().columns());
+		gsl_vector* work = gsl_vector_alloc(matrix().columns());
+		
+		gsl_linalg_SV_decomp( U, V, S, work );
+		
+		gsl_vector* b = vector().get_gsl_vector();
+		gsl_vector* x = m_solution.get_gsl_vector();
+		
+		gsl_linalg_SV_solve( U, V, S, b, x);
+		
+		gsl_matrix_free(V);
+		gsl_vector_free(S);
+		gsl_vector_free(work);
+		
+		return solution();			  
+	}
+	
+	MatrixView & matrix()
+	{
+		return m_matrix;
+	}
+	
+	VectorView & vector()
+	{
+		return m_vector;
+	}
+	
+	const Vector & solution() const
+	{
+		return m_solution;
+	}
+	
+private:
+	MatrixView m_matrix;
+	VectorView m_vector;
+	Vector m_solution;
+};
+
+
+} } // end namespaces
+
+
+#endif /*_NL_LINEAR_SYSTEM_H_*/
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/numeric/matrix.h b/include/2geom/numeric/matrix.h
new file mode 100644
index 0000000..02851b4
--- /dev/null
+++ b/include/2geom/numeric/matrix.h
@@ -0,0 +1,603 @@
+/*
+ * Matrix, MatrixView, ConstMatrixView classes wrap the gsl matrix routines;
+ * "views" mimic the semantic of C++ references: any operation performed
+ * on a "view" is actually performed on the "viewed object"
+ *
+ * Authors:
+ * 		Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 2008  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+
+
+
+#ifndef _NL_MATRIX_H_
+#define _NL_MATRIX_H_
+
+#include <2geom/exception.h>
+#include <2geom/numeric/vector.h>
+
+#include <cassert>
+#include <utility>    // for std::pair
+#include <algorithm>  // for std::swap
+#include <sstream>
+#include <string>
+#include <gsl/gsl_matrix.h>
+#include <gsl/gsl_linalg.h>
+
+
+namespace Geom { namespace NL {
+
+namespace detail
+{
+
+class BaseMatrixImpl
+{
+  public:
+	virtual ~BaseMatrixImpl()
+	{
+	}
+
+	ConstVectorView row_const_view(size_t i) const
+	{
+		return ConstVectorView(gsl_matrix_const_row(m_matrix, i));
+	}
+
+	ConstVectorView column_const_view(size_t i) const
+	{
+		return ConstVectorView(gsl_matrix_const_column(m_matrix, i));
+	}
+
+	const double & operator() (size_t i, size_t j) const
+	{
+		return *gsl_matrix_const_ptr(m_matrix, i, j);
+	}
+
+	const gsl_matrix* get_gsl_matrix() const
+	{
+		return m_matrix;
+	}
+
+	bool is_zero() const
+	{
+		return gsl_matrix_isnull(m_matrix);
+	}
+
+	bool is_positive() const
+	{
+        for ( unsigned int i = 0; i < rows(); ++i )
+        {
+            for ( unsigned int j = 0; j < columns(); ++j )
+            {
+                if ( (*this)(i,j) <= 0 ) return false;
+            }
+        }
+        return true;
+	}
+
+	bool is_negative() const
+	{
+        for ( unsigned int i = 0; i < rows(); ++i )
+        {
+            for ( unsigned int j = 0; j < columns(); ++j )
+            {
+                if ( (*this)(i,j) >= 0 ) return false;
+            }
+        }
+        return true;
+	}
+
+	bool is_non_negative() const
+	{
+		for ( unsigned int i = 0; i < rows(); ++i )
+		{
+			for ( unsigned int j = 0; j < columns(); ++j )
+			{
+				if ( (*this)(i,j) < 0 ) return false;
+			}
+		}
+		return true;
+	}
+
+	double max() const
+	{
+		return gsl_matrix_max(m_matrix);
+	}
+
+	double min() const
+	{
+		return gsl_matrix_min(m_matrix);
+	}
+
+	std::pair<size_t, size_t>
+	max_index() const
+	{
+		std::pair<size_t, size_t> indices;
+		gsl_matrix_max_index(m_matrix, &(indices.first), &(indices.second));
+		return indices;
+	}
+
+	std::pair<size_t, size_t>
+	min_index() const
+	{
+		std::pair<size_t, size_t> indices;
+		gsl_matrix_min_index(m_matrix, &(indices.first), &(indices.second));
+		return indices;
+	}
+
+	size_t rows() const
+	{
+		return m_rows;
+	}
+
+	size_t columns() const
+	{
+		return m_columns;
+	}
+
+	std::string str() const;
+
+  protected:
+	size_t m_rows, m_columns;
+	gsl_matrix* m_matrix;
+
+};  // end class BaseMatrixImpl
+
+
+inline
+bool operator== (BaseMatrixImpl const& m1, BaseMatrixImpl const& m2)
+{
+	if (m1.rows() != m2.rows() || m1.columns() != m2.columns()) return false;
+
+	for (size_t i = 0; i < m1.rows(); ++i)
+		for (size_t j = 0; j < m1.columns(); ++j)
+			if (m1(i,j) != m2(i,j)) return false;
+
+	return true;
+}
+
+template< class charT >
+inline
+std::basic_ostream<charT> &
+operator<< (std::basic_ostream<charT> & os, const BaseMatrixImpl & _matrix)
+{
+	if (_matrix.rows() == 0 || _matrix.columns() == 0) return os;
+
+	os << "[[" << _matrix(0,0);
+	for (size_t j = 1; j < _matrix.columns(); ++j)
+	{
+		os << ", " << _matrix(0,j);
+	}
+	os << "]";
+
+	for (size_t i = 1; i < _matrix.rows(); ++i)
+	{
+		os << ", [" << _matrix(i,0);
+		for (size_t j = 1; j < _matrix.columns(); ++j)
+		{
+			os << ", " << _matrix(i,j);
+		}
+		os << "]";
+	}
+	os << "]";
+	return os;
+}
+
+inline
+std::string BaseMatrixImpl::str() const
+{
+	std::ostringstream oss;
+	oss << (*this);
+	return oss.str();
+}
+
+
+class MatrixImpl : public BaseMatrixImpl
+{
+  public:
+
+	typedef BaseMatrixImpl base_type;
+
+	void set_all( double x )
+	{
+		gsl_matrix_set_all(m_matrix, x);
+	}
+
+	void set_identity()
+	{
+		gsl_matrix_set_identity(m_matrix);
+	}
+
+    using base_type::operator(); // VSC legacy support
+    const double & operator() (size_t i, size_t j) const
+    {
+        return base_type::operator ()(i, j);
+    }
+
+    double & operator() (size_t i, size_t j)
+	{
+            return *gsl_matrix_ptr(m_matrix, i, j);
+	}
+
+	using base_type::get_gsl_matrix;
+
+	gsl_matrix* get_gsl_matrix()
+	{
+		return m_matrix;
+	}
+
+	VectorView row_view(size_t i)
+	{
+		return VectorView(gsl_matrix_row(m_matrix, i));
+	}
+
+	VectorView column_view(size_t i)
+	{
+		return VectorView(gsl_matrix_column(m_matrix, i));
+	}
+
+	void swap_rows(size_t i, size_t j)
+	{
+		 gsl_matrix_swap_rows(m_matrix, i, j);
+	}
+
+	void swap_columns(size_t i, size_t j)
+	{
+		gsl_matrix_swap_columns(m_matrix, i, j);
+	}
+
+	MatrixImpl & transpose()
+	{
+	    assert(columns() == rows());
+		gsl_matrix_transpose(m_matrix);
+		return (*this);
+	}
+
+	MatrixImpl & scale(double x)
+	{
+		gsl_matrix_scale(m_matrix, x);
+		return (*this);
+	}
+
+	MatrixImpl & translate(double x)
+	{
+		 gsl_matrix_add_constant(m_matrix, x);
+		 return (*this);
+	}
+
+	MatrixImpl & operator+=(base_type const& _matrix)
+	{
+		gsl_matrix_add(m_matrix, _matrix.get_gsl_matrix());
+		return (*this);
+	}
+
+	MatrixImpl & operator-=(base_type const& _matrix)
+	{
+		gsl_matrix_sub(m_matrix, _matrix.get_gsl_matrix());
+		return (*this);
+	}
+
+}; // end class MatrixImpl
+
+}  // end namespace detail
+
+
+using detail::operator==;
+using detail::operator<<;
+
+
+template <size_t N>
+class ConstBaseSymmetricMatrix;
+
+
+class Matrix: public detail::MatrixImpl
+{
+  public:
+	typedef detail::MatrixImpl base_type;
+
+  public:
+	// the matrix is not initialized
+	Matrix(size_t n1, size_t n2)
+	{
+		m_rows = n1;
+		m_columns = n2;
+		m_matrix = gsl_matrix_alloc(n1, n2);
+	}
+
+	Matrix(size_t n1, size_t n2, double x)
+	{
+		m_rows = n1;
+		m_columns = n2;
+		m_matrix = gsl_matrix_alloc(n1, n2);
+		gsl_matrix_set_all(m_matrix, x);
+	}
+
+	Matrix(Matrix const& _matrix)
+        : base_type()
+	{
+		m_rows = _matrix.rows();
+		m_columns = _matrix.columns();
+		m_matrix = gsl_matrix_alloc(rows(), columns());
+		gsl_matrix_memcpy(m_matrix, _matrix.get_gsl_matrix());
+	}
+
+	explicit
+	Matrix(base_type::base_type const& _matrix)
+	{
+		m_rows = _matrix.rows();
+		m_columns = _matrix.columns();
+		m_matrix = gsl_matrix_alloc(rows(), columns());
+		gsl_matrix_memcpy(m_matrix, _matrix.get_gsl_matrix());
+	}
+
+	template <size_t N>
+	explicit
+    Matrix(ConstBaseSymmetricMatrix<N> const& _smatrix)
+	{
+	    m_rows = N;
+	    m_columns = N;
+	    m_matrix = gsl_matrix_alloc(N, N);
+	    for (size_t i = 0; i < N; ++i)
+	        for (size_t j = 0; j < N ; ++j)
+	            (*gsl_matrix_ptr(m_matrix, i, j)) = _smatrix(i,j);
+	}
+
+	Matrix & operator=(Matrix const& _matrix)
+	{
+		assert( rows() == _matrix.rows() && columns() ==  _matrix.columns() );
+		gsl_matrix_memcpy(m_matrix, _matrix.get_gsl_matrix());
+		return *this;
+	}
+
+	Matrix & operator=(base_type::base_type const& _matrix)
+	{
+		assert( rows() == _matrix.rows() && columns() ==  _matrix.columns() );
+		gsl_matrix_memcpy(m_matrix, _matrix.get_gsl_matrix());
+		return *this;
+	}
+
+	template <size_t N>
+	Matrix & operator=(ConstBaseSymmetricMatrix<N> const& _smatrix)
+	{
+	    assert (rows() == N && columns() ==  N);
+	    for (size_t i = 0; i < N; ++i)
+	        for (size_t j = 0; j < N ; ++j)
+	            (*this)(i,j) = _smatrix(i,j);
+	    return *this;
+	}
+
+	~Matrix() override
+	{
+		gsl_matrix_free(m_matrix);
+	}
+
+	Matrix & transpose()
+	{
+		return static_cast<Matrix &>( base_type::transpose() );
+	}
+
+	Matrix & scale(double x)
+	{
+		return static_cast<Matrix &>( base_type::scale(x) );
+	}
+
+	Matrix & translate(double x)
+	{
+		return static_cast<Matrix &>( base_type::translate(x) );
+	}
+
+	Matrix & operator+=(base_type::base_type const& _matrix)
+	{
+		return static_cast<Matrix &>( base_type::operator+=(_matrix) );
+	}
+
+	Matrix & operator-=(base_type::base_type const& _matrix)
+	{
+		return static_cast<Matrix &>( base_type::operator-=(_matrix) );
+	}
+
+	friend
+	void swap(Matrix & m1, Matrix & m2);
+	friend
+	void swap_any(Matrix & m1, Matrix & m2);
+
+};  // end class Matrix
+
+
+// warning! this operation invalidates any view of the passed matrix objects
+inline
+void swap(Matrix & m1, Matrix & m2)
+{
+    assert(m1.rows() == m2.rows() && m1.columns() == m2.columns());
+    using std::swap;
+    swap(m1.m_matrix, m2.m_matrix);
+}
+
+inline void swap_any(Matrix &m1, Matrix &m2)
+{
+    using std::swap;
+    swap(m1.m_matrix, m2.m_matrix);
+    swap(m1.m_rows, m2.m_rows);
+    swap(m1.m_columns, m2.m_columns);
+}
+
+
+
+class ConstMatrixView : public detail::BaseMatrixImpl
+{
+  public:
+	typedef detail::BaseMatrixImpl base_type;
+
+  public:
+	ConstMatrixView(const base_type & _matrix, size_t k1, size_t k2, size_t n1, size_t n2)
+		: m_matrix_view( gsl_matrix_const_submatrix(_matrix.get_gsl_matrix(), k1, k2, n1, n2) )
+	{
+		m_rows = n1;
+		m_columns = n2;
+		m_matrix = const_cast<gsl_matrix*>( &(m_matrix_view.matrix) );
+	}
+
+	ConstMatrixView(const ConstMatrixView & _matrix)
+		: base_type(),
+		  m_matrix_view(_matrix.m_matrix_view)
+	{
+		m_rows = _matrix.rows();
+		m_columns = _matrix.columns();
+		m_matrix = const_cast<gsl_matrix*>( &(m_matrix_view.matrix) );
+	}
+
+	ConstMatrixView(const base_type & _matrix)
+		: m_matrix_view(gsl_matrix_const_submatrix(_matrix.get_gsl_matrix(), 0, 0, _matrix.rows(), _matrix.columns()))
+	{
+		m_rows = _matrix.rows();
+		m_columns = _matrix.columns();
+		m_matrix = const_cast<gsl_matrix*>( &(m_matrix_view.matrix) );
+	}
+
+  private:
+	gsl_matrix_const_view m_matrix_view;
+
+};  // end class ConstMatrixView
+
+
+
+
+class MatrixView : public detail::MatrixImpl
+{
+  public:
+	typedef detail::MatrixImpl base_type;
+
+  public:
+	MatrixView(base_type & _matrix, size_t k1, size_t k2, size_t n1, size_t n2)
+	{
+		m_rows = n1;
+		m_columns = n2;
+		m_matrix_view
+			= gsl_matrix_submatrix(_matrix.get_gsl_matrix(), k1, k2, n1, n2);
+		m_matrix = &(m_matrix_view.matrix);
+	}
+
+	MatrixView(const MatrixView & _matrix)
+        : base_type()
+	{
+		m_rows = _matrix.rows();
+		m_columns = _matrix.columns();
+		m_matrix_view = _matrix.m_matrix_view;
+		m_matrix = &(m_matrix_view.matrix);
+	}
+
+	MatrixView(Matrix & _matrix)
+	{
+		m_rows = _matrix.rows();
+		m_columns = _matrix.columns();
+		m_matrix_view
+			= gsl_matrix_submatrix(_matrix.get_gsl_matrix(), 0, 0, rows(), columns());
+		m_matrix = &(m_matrix_view.matrix);
+	}
+
+	MatrixView & operator=(MatrixView const& _matrix)
+	{
+		assert( rows() == _matrix.rows() && columns() ==  _matrix.columns() );
+		gsl_matrix_memcpy(m_matrix, _matrix.m_matrix);
+		return *this;
+	}
+
+	MatrixView & operator=(base_type::base_type const& _matrix)
+	{
+		assert( rows() == _matrix.rows() && columns() ==  _matrix.columns() );
+		gsl_matrix_memcpy(m_matrix, _matrix.get_gsl_matrix());
+		return *this;
+	}
+
+	MatrixView & transpose()
+	{
+		return static_cast<MatrixView &>( base_type::transpose() );
+	}
+
+	MatrixView & scale(double x)
+	{
+		return static_cast<MatrixView &>( base_type::scale(x) );
+	}
+
+	MatrixView & translate(double x)
+	{
+		return static_cast<MatrixView &>( base_type::translate(x) );
+	}
+
+	MatrixView & operator+=(base_type::base_type const& _matrix)
+	{
+		return static_cast<MatrixView &>( base_type::operator+=(_matrix) );
+	}
+
+	MatrixView & operator-=(base_type::base_type const& _matrix)
+	{
+		return static_cast<MatrixView &>( base_type::operator-=(_matrix) );
+	}
+
+	friend
+	void swap_view(MatrixView & m1, MatrixView & m2);
+
+  private:
+	gsl_matrix_view m_matrix_view;
+
+};  // end class MatrixView
+
+
+inline
+void swap_view(MatrixView & m1, MatrixView & m2)
+{
+    assert(m1.rows() == m2.rows() && m1.columns() == m2.columns());
+    using std::swap;
+    swap(m1.m_matrix_view, m2.m_matrix_view);
+}
+
+Vector operator*( detail::BaseMatrixImpl const& A,
+                  detail::BaseVectorImpl const& v );
+
+Matrix operator*( detail::BaseMatrixImpl const& A,
+                  detail::BaseMatrixImpl const& B );
+
+Matrix pseudo_inverse(detail::BaseMatrixImpl const& A);
+
+double trace (detail::BaseMatrixImpl const& A);
+
+double det (detail::BaseMatrixImpl const& A);
+
+} } // end namespaces
+
+#endif /*_NL_MATRIX_H_*/
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/numeric/symmetric-matrix-fs-operation.h b/include/2geom/numeric/symmetric-matrix-fs-operation.h
new file mode 100644
index 0000000..c5aaa72
--- /dev/null
+++ b/include/2geom/numeric/symmetric-matrix-fs-operation.h
@@ -0,0 +1,102 @@
+/*
+ * SymmetricMatrix basic operation
+ *
+ * Authors:
+ *      Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 2009  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef _NL_SYMMETRIC_MATRIX_FS_OPERATION_H_
+#define _NL_SYMMETRIC_MATRIX_FS_OPERATION_H_
+
+
+#include <2geom/numeric/symmetric-matrix-fs.h>
+#include <2geom/numeric/symmetric-matrix-fs-trace.h>
+
+
+
+
+namespace Geom { namespace NL {
+
+template <size_t N>
+SymmetricMatrix<N> adj(const ConstBaseSymmetricMatrix<N> & S);
+
+template <>
+inline
+SymmetricMatrix<2> adj(const ConstBaseSymmetricMatrix<2> & S)
+{
+    SymmetricMatrix<2> result;
+    result.get<0,0>() = S.get<1,1>();
+    result.get<1,0>() = -S.get<1,0>();
+    result.get<1,1>() = S.get<0,0>();
+    return result;
+}
+
+template <>
+inline
+SymmetricMatrix<3> adj(const ConstBaseSymmetricMatrix<3> & S)
+{
+    SymmetricMatrix<3> result;
+
+    result.get<0,0>() = S.get<1,1>() * S.get<2,2>() - S.get<1,2>() * S.get<2,1>();
+    result.get<1,0>() = S.get<0,2>() * S.get<2,1>() - S.get<0,1>() * S.get<2,2>();
+    result.get<1,1>() = S.get<0,0>() * S.get<2,2>() - S.get<0,2>() * S.get<2,0>();
+    result.get<2,0>() = S.get<0,1>() * S.get<1,2>() - S.get<0,2>() * S.get<1,1>();
+    result.get<2,1>() = S.get<0,2>() * S.get<1,0>() - S.get<0,0>() * S.get<1,2>();
+    result.get<2,2>() = S.get<0,0>() * S.get<1,1>() - S.get<0,1>() * S.get<1,0>();
+    return result;
+}
+
+template <size_t N>
+inline
+SymmetricMatrix<N> inverse(const ConstBaseSymmetricMatrix<N> & S)
+{
+    SymmetricMatrix<N> result = adj(S);
+    double d = det(S);
+    assert (d != 0);
+    result.scale (1/d);
+    return result;
+}
+
+} /* end namespace NL*/ } /* end namespace Geom*/
+
+
+#endif // _NL_SYMMETRIC_MATRIX_FS_OPERATION_H_
+
+
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/numeric/symmetric-matrix-fs-trace.h b/include/2geom/numeric/symmetric-matrix-fs-trace.h
new file mode 100644
index 0000000..0e7a28c
--- /dev/null
+++ b/include/2geom/numeric/symmetric-matrix-fs-trace.h
@@ -0,0 +1,427 @@
+/*
+ * SymmetricMatrix trace
+ *
+ * Authors:
+ *      Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 2009  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+
+#ifndef _NL_TRACE_H_
+#define _NL_TRACE_H_
+
+
+#include <2geom/numeric/matrix.h>
+#include <2geom/numeric/symmetric-matrix-fs.h>
+
+
+
+
+
+namespace Geom { namespace NL {
+
+
+namespace detail
+{
+
+/*
+ *  helper routines
+ */
+
+inline
+int sgn_prod (int x, int y)
+{
+    if (x == 0 || y == 0)  return 0;
+    if (x == y) return 1;
+    return -1;
+}
+
+inline
+bool abs_less (double x, double y)
+{
+    return (std::fabs(x) < std::fabs(y));
+}
+
+
+/*
+ * trace K-th of symmetric matrix S of order N
+ */
+template <size_t K, size_t N>
+struct trace
+{
+    static double evaluate(const ConstBaseSymmetricMatrix<N> &S);
+};
+
+template <size_t N>
+struct trace<1,N>
+{
+    static
+    double evaluate (const ConstBaseSymmetricMatrix<N> & S)
+    {
+        double t = 0;
+        for (size_t i = 0; i < N; ++i)
+        {
+            t += S(i,i);
+        }
+        return t;
+    }
+};
+
+template <size_t N>
+struct trace<N,N>
+{
+    static
+    double evaluate (const ConstBaseSymmetricMatrix<N> & S)
+    {
+        Matrix M(S);
+        return det(M);
+    }
+};
+
+/*
+ *  trace for symmetric matrix of order 2
+ */
+template <>
+struct trace<1,2>
+{
+    static
+    double evaluate (const ConstBaseSymmetricMatrix<2> & S)
+    {
+        return (S.get<0,0>() + S.get<1,1>());
+    }
+};
+
+template <>
+struct trace<2,2>
+{
+    static
+    double evaluate (const ConstBaseSymmetricMatrix<2> & S)
+    {
+        return (S.get<0,0>() * S.get<1,1>() - S.get<0,1>() * S.get<1,0>());
+    }
+};
+
+
+/*
+ *  trace for symmetric matrix of order 3
+ */
+template <>
+struct trace<1,3>
+{
+    static
+    double evaluate (const ConstBaseSymmetricMatrix<3> & S)
+    {
+        return (S.get<0,0>() + S.get<1,1>() + S.get<2,2>());
+    }
+};
+
+template <>
+struct trace<2,3>
+{
+    static
+    double evaluate (const ConstBaseSymmetricMatrix<3> & S)
+    {
+        double a00 = S.get<1,1>() * S.get<2,2>() - S.get<1,2>() * S.get<2,1>();
+        double a11 = S.get<0,0>() * S.get<2,2>() - S.get<0,2>() * S.get<2,0>();
+        double a22 = S.get<0,0>() * S.get<1,1>() - S.get<0,1>() * S.get<1,0>();
+        return (a00 + a11 + a22);
+    }
+};
+
+template <>
+struct trace<3,3>
+{
+    static
+    double evaluate (const ConstBaseSymmetricMatrix<3> & S)
+    {
+        double d = S.get<0,0>() * S.get<1,1>() * S.get<2,2>();
+        d += (2 * S.get<1,0>() * S.get<2,0>() * S.get<2,1>());
+        d -= (S.get<0,0>() * S.get<2,1>() * S.get<2,1>());
+        d -= (S.get<1,1>() * S.get<2,0>() * S.get<2,0>());
+        d -= (S.get<2,2>() * S.get<1,0>() * S.get<1,0>());
+        return d;
+    }
+};
+
+
+/*
+ *  sign of trace K-th
+ */
+template <size_t K, size_t N>
+struct trace_sgn
+{
+    static
+    int evaluate (const ConstBaseSymmetricMatrix<N> & S)
+    {
+        double d = trace<K, N>::evaluate(S);
+        return sgn(d);
+    }
+};
+
+
+/*
+ *  sign of trace for symmetric matrix of order 2
+ */
+template <>
+struct trace_sgn<2,2>
+{
+    static
+    int evaluate (const ConstBaseSymmetricMatrix<2> & S)
+    {
+        double m00 = S.get<0,0>();
+        double m10 = S.get<1,0>();
+        double m11 = S.get<1,1>();
+
+        int sm00 = sgn (m00);
+        int sm10 = sgn (m10);
+        int sm11 = sgn (m11);
+
+        if (sm10 == 0)
+        {
+            return sgn_prod (sm00, sm11);
+        }
+        else
+        {
+            int sm00m11 = sgn_prod (sm00, sm11);
+            if (sm00m11 == 1)
+            {
+                int e00, e10, e11;
+                double f00 = std::frexp (m00, &e00);
+                double f10 = std::frexp (m10, &e10);
+                double f11 = std::frexp (m11, &e11);
+
+                int e0011 = e00 + e11;
+                int e1010 = e10 << 1;
+                int ed = e0011 - e1010;
+
+                if (ed > 1)
+                {
+                    return 1;
+                }
+                else if (ed < -1)
+                {
+                    return -1;
+                }
+                else
+                {
+                    double d = std::ldexp (f00 * f11, ed) - f10 * f10;
+                    //std::cout << "trace_sgn<2,2>: det = " << d << std::endl;
+                    double eps = std::ldexp (1, -50);
+                    if (std::fabs(d) < eps)  return 0;
+                    return sgn (d);
+                }
+            }
+            return -1;
+        }
+    }
+};
+
+
+/*
+ *  sign of trace for symmetric matrix of order 3
+ */
+template <>
+struct trace_sgn<2,3>
+{
+    static
+    int evaluate (const ConstBaseSymmetricMatrix<3> & S)
+    {
+        double eps = std::ldexp (1, -50);
+        double t[6];
+
+        t[0] = S.get<1,1>() * S.get<2,2>();
+        t[1] = - S.get<1,2>() * S.get<2,1>();
+        t[2] = S.get<0,0>() * S.get<2,2>();
+        t[3] = - S.get<0,2>() * S.get<2,0>();
+        t[4] = S.get<0,0>() * S.get<1,1>();
+        t[5] = - S.get<0,1>() * S.get<1,0>();
+
+
+        double* maxp = std::max_element (t, t+6, abs_less);
+        int em;
+        std::frexp(*maxp, &em);
+        double d = 0;
+        for (double i : t)
+        {
+            d += i;
+        }
+        double r = std::fabs (std::ldexp (d, -em));  // relative error
+        //std::cout << "trace_sgn<2,3>: d = " << d << std::endl;
+        //std::cout << "trace_sgn<2,3>: r = " << r << std::endl;
+        if (r < eps) return 0;
+        if (d > 0) return 1;
+        return -1;
+    }
+};
+
+template <>
+struct trace_sgn<3,3>
+{
+    static
+    int evaluate (const ConstBaseSymmetricMatrix<3> & S)
+    {
+
+        double eps = std::ldexp (1, -48);
+        double t[5];
+
+        t[0] = S.get<0,0>() * S.get<1,1>() * S.get<2,2>();
+        t[1] = 2 * S.get<1,0>() * S.get<2,0>() * S.get<2,1>();
+        t[2] = -(S.get<0,0>() * S.get<2,1>() * S.get<2,1>());
+        t[3] = -(S.get<1,1>() * S.get<2,0>() * S.get<2,0>());
+        t[4] = -(S.get<2,2>() * S.get<1,0>() * S.get<1,0>());
+
+        double* maxp = std::max_element (t, t+5, abs_less);
+        int em;
+        std::frexp(*maxp, &em);
+        double d = 0;
+        for (double i : t)
+        {
+            d += i;
+        }
+        //std::cout << "trace_sgn<3,3>: d = " << d << std::endl;
+        double r = std::fabs (std::ldexp (d, -em));  // relative error
+        //std::cout << "trace_sgn<3,3>: r = " << r << std::endl;
+
+        if (r < eps)  return 0;
+        if (d > 0) return 1;
+        return -1;
+    }
+}; // end struct trace_sgn<3,3>
+
+} // end namespace detail
+
+
+template <size_t K, size_t N>
+inline
+double trace (const ConstBaseSymmetricMatrix<N> & _matrix)
+{
+    return detail::trace<K, N>::evaluate(_matrix);
+}
+
+template <size_t N>
+inline
+double trace (const ConstBaseSymmetricMatrix<N> & _matrix)
+{
+    return detail::trace<1, N>::evaluate(_matrix);
+}
+
+template <size_t N>
+inline
+double det (const ConstBaseSymmetricMatrix<N> & _matrix)
+{
+    return detail::trace<N, N>::evaluate(_matrix);
+}
+
+
+template <size_t K, size_t N>
+inline
+int trace_sgn (const ConstBaseSymmetricMatrix<N> & _matrix)
+{
+    return detail::trace_sgn<K, N>::evaluate(_matrix);
+}
+
+template <size_t N>
+inline
+int trace_sgn (const ConstBaseSymmetricMatrix<N> & _matrix)
+{
+    return detail::trace_sgn<1, N>::evaluate(_matrix);
+}
+
+template <size_t N>
+inline
+int det_sgn (const ConstBaseSymmetricMatrix<N> & _matrix)
+{
+    return detail::trace_sgn<N, N>::evaluate(_matrix);
+}
+
+/*
+template <size_t N>
+inline
+size_t rank (const ConstBaseSymmetricMatrix<N> & S)
+{
+    THROW_NOTIMPLEMENTED();
+    return 0;
+}
+
+template <>
+inline
+size_t rank<2> (const ConstBaseSymmetricMatrix<2> & S)
+{
+    if (S.is_zero())  return 0;
+    double d = S.get<0,0>() * S.get<1,1>() - S.get<0,1>() * S.get<1,0>();
+    if (d != 0)  return 2;
+    return 1;
+}
+
+template <>
+inline
+size_t rank<3> (const ConstBaseSymmetricMatrix<3> & S)
+{
+    if (S.is_zero())  return 0;
+
+    double a20 = S.get<0,1>() * S.get<1,2>() - S.get<0,2>() * S.get<1,1>();
+    double a21 = S.get<0,2>() * S.get<1,0>() - S.get<0,0>() * S.get<1,2>();
+    double a22 = S.get<0,0>() * S.get<1,1>() - S.get<0,1>() * S.get<1,0>();
+    double d = a20 * S.get<2,0>() + a21 * S.get<2,1>() + a22 * S.get<2,2>();
+
+    if (d != 0)  return 3;
+
+    if (a20 != 0 || a21 != 0 || a22 != 0)  return 2;
+
+    double a00 = S.get<1,1>() * S.get<2,2>() - S.get<1,2>() * S.get<2,1>();
+    if (a00 != 0)  return 2;
+
+    double a10 = S.get<0,2>() * S.get<2,1>() - S.get<0,1>() * S.get<2,2>();
+    if (a10 != 0)  return 2;
+
+    double a11 = S.get<0,0>() * S.get<2,2>() - S.get<0,2>() * S.get<2,0>();
+    if (a11 != 0)  return 2;
+
+    return 1;
+}
+*/
+
+} /* end namespace NL*/ } /* end namespace Geom*/
+
+
+
+
+#endif // _NL_TRACE_H_
+
+
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/numeric/symmetric-matrix-fs.h b/include/2geom/numeric/symmetric-matrix-fs.h
new file mode 100644
index 0000000..2fadd69
--- /dev/null
+++ b/include/2geom/numeric/symmetric-matrix-fs.h
@@ -0,0 +1,733 @@
+/*
+ * SymmetricMatrix, ConstSymmetricMatrixView, SymmetricMatrixView template
+ * classes implement fixed size symmetric matrix; "views" mimic the semantic
+ * of C++ references: any operation performed on a "view" is actually performed
+ * on the "viewed object"
+ *
+ * Authors:
+ *      Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 2009  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+
+#ifndef _NL_SYMMETRIC_MATRIX_FS_H_
+#define _NL_SYMMETRIC_MATRIX_FS_H_
+
+
+#include <2geom/numeric/vector.h>
+#include <2geom/numeric/matrix.h>
+#include <2geom/utils.h>
+#include <2geom/exception.h>
+
+#include <boost/static_assert.hpp>
+
+#include <cassert>
+#include <utility>     // for std::pair
+#include <algorithm>   // for std::swap, std::copy
+#include <sstream>
+#include <string>
+
+
+
+namespace Geom { namespace NL {
+
+
+namespace detail
+{
+
+template <size_t I, size_t J, bool B = (I < J)>
+struct index
+{
+    static const size_t K = index<J, I>::K;
+};
+
+template <size_t I, size_t J>
+struct index<I, J, false>
+{
+    static const size_t K = (((I+1) * I) >> 1) + J;
+};
+
+} // end namespace detail
+
+
+
+
+template <size_t N>
+class ConstBaseSymmetricMatrix;
+
+template <size_t N>
+class BaseSymmetricMatrix;
+
+template <size_t N>
+class SymmetricMatrix;
+
+template <size_t N>
+class ConstSymmetricMatrixView;
+
+template <size_t N>
+class SymmetricMatrixView;
+
+
+
+// declaration needed for friend clause
+template <size_t N>
+bool operator== (ConstBaseSymmetricMatrix<N> const& _smatrix1,
+                 ConstBaseSymmetricMatrix<N> const& _smatrix2);
+
+
+
+
+template <size_t N>
+class ConstBaseSymmetricMatrix
+{
+  public:
+    const static size_t DIM = N;
+    const static size_t DATA_SIZE = ((DIM+1) * DIM) / 2;
+
+  public:
+
+    ConstBaseSymmetricMatrix (VectorView const& _data)
+        : m_data(_data)
+    {
+    }
+
+    double operator() (size_t i, size_t j) const
+    {
+        return m_data[get_index(i,j)];
+    }
+
+    template <size_t I, size_t J>
+    double get() const
+    {
+        BOOST_STATIC_ASSERT ((I < N && J < N));
+        return m_data[detail::index<I, J>::K];
+    }
+
+
+    size_t rows() const
+    {
+        return DIM;
+    }
+
+    size_t columns() const
+    {
+        return DIM;
+    }
+
+    bool is_zero() const
+    {
+        return m_data.is_zero();
+    }
+
+    bool is_positive() const
+    {
+        return m_data.is_positive();
+    }
+
+    bool is_negative() const
+    {
+        return m_data.is_negative();
+    }
+
+    bool is_non_negative() const
+    {
+        return m_data.is_non_negative();
+    }
+
+    double min() const
+    {
+        return m_data.min();
+    }
+
+    double max() const
+    {
+        return m_data.max();
+    }
+
+    std::pair<size_t, size_t>
+    min_index() const
+    {
+        std::pair<size_t, size_t> indices(0,0);
+        double min_value = m_data[0];
+        for (size_t i = 1; i < DIM; ++i)
+        {
+            for (size_t j = 0; j <= i; ++j)
+            {
+                if (min_value > (*this)(i,j))
+                {
+                    min_value = (*this)(i,j);
+                    indices.first = i;
+                    indices.second = j;
+                }
+            }
+        }
+        return indices;
+    }
+
+    std::pair<size_t, size_t>
+    max_index() const
+    {
+        std::pair<size_t, size_t> indices(0,0);
+        double max_value = m_data[0];
+        for (size_t i = 1; i < DIM; ++i)
+        {
+            for (size_t j = 0; j <= i; ++j)
+            {
+                if (max_value < (*this)(i,j))
+                {
+                    max_value = (*this)(i,j);
+                    indices.first = i;
+                    indices.second = j;
+                }
+            }
+        }
+        return indices;
+    }
+
+    size_t min_on_row_index (size_t i) const
+    {
+        size_t idx = 0;
+        double min_value = (*this)(i,0);
+        for (size_t j = 1; j < DIM; ++j)
+        {
+            if (min_value > (*this)(i,j))
+            {
+                min_value = (*this)(i,j);
+                idx = j;
+            }
+        }
+        return idx;
+    }
+
+    size_t max_on_row_index (size_t i) const
+    {
+        size_t idx = 0;
+        double max_value = (*this)(i,0);
+        for (size_t j = 1; j < DIM; ++j)
+        {
+            if (max_value < (*this)(i,j))
+            {
+                max_value = (*this)(i,j);
+                idx = j;
+            }
+        }
+        return idx;
+    }
+
+    size_t min_on_column_index (size_t j) const
+    {
+        return min_on_row_index(j);
+    }
+
+    size_t max_on_column_index (size_t j) const
+    {
+        return max_on_row_index(j);
+    }
+
+    size_t min_on_diag_index () const
+    {
+        size_t idx = 0;
+        double min_value = (*this)(0,0);
+        for (size_t i = 1; i < DIM; ++i)
+        {
+            if (min_value > (*this)(i,i))
+            {
+                min_value = (*this)(i,i);
+                idx = i;
+            }
+        }
+        return idx;
+    }
+
+    size_t max_on_diag_index () const
+    {
+        size_t idx = 0;
+        double max_value = (*this)(0,0);
+        for (size_t i = 1; i < DIM; ++i)
+        {
+            if (max_value < (*this)(i,i))
+            {
+                max_value = (*this)(i,i);
+                idx = i;
+            }
+        }
+        return idx;
+    }
+
+    std::string str() const;
+
+    ConstSymmetricMatrixView<N-1> main_minor_const_view() const;
+
+    SymmetricMatrix<N> operator- () const;
+
+    Vector operator* (ConstVectorView _vector) const
+    {
+        assert (_vector.size() == DIM);
+        Vector result(DIM, 0.0);
+
+        for (size_t i = 0; i < DIM; ++i)
+        {
+            for (size_t j = 0; j < DIM; ++j)
+            {
+                result[i] += (*this)(i,j) * _vector[j];
+            }
+        }
+        return result;
+    }
+
+  protected:
+    static size_t get_index (size_t i, size_t j)
+    {
+        if (i < j) return get_index (j, i);
+        size_t k = (i+1) * i;
+        k >>= 1;
+        k += j;
+        return k;
+    }
+
+  protected:
+    ConstVectorView get_data() const
+    {
+      return m_data;
+    }
+
+    friend
+    bool operator==<N> (ConstBaseSymmetricMatrix const& _smatrix1,
+                        ConstBaseSymmetricMatrix const& _smatrix2);
+
+  protected:
+    VectorView m_data;
+
+}; //end ConstBaseSymmetricMatrix
+
+
+template <size_t N>
+class BaseSymmetricMatrix : public ConstBaseSymmetricMatrix<N>
+{
+  public:
+    typedef ConstBaseSymmetricMatrix<N> base_type;
+
+
+  public:
+
+    BaseSymmetricMatrix (VectorView const& _data)
+        : base_type(_data)
+    {
+    }
+
+    double operator() (size_t i, size_t j) const 
+    {
+        return m_data[base_type::get_index(i,j)];
+    }
+
+    double& operator() (size_t i, size_t j)
+    {
+        return m_data[base_type::get_index(i,j)];
+    }
+
+    template <size_t I, size_t J>
+    double& get()
+    {
+        BOOST_STATIC_ASSERT ((I < N && J < N));
+        return m_data[detail::index<I, J>::K];
+    }
+
+    void set_all (double x)
+    {
+        m_data.set_all(x);
+    }
+
+    SymmetricMatrixView<N-1> main_minor_view();
+
+    BaseSymmetricMatrix& transpose() const
+    {
+        return (*this);
+    }
+
+    BaseSymmetricMatrix& translate (double c)
+    {
+        m_data.translate(c);
+        return (*this);
+    }
+
+    BaseSymmetricMatrix& scale (double c)
+    {
+        m_data.scale(c);
+        return (*this);
+    }
+
+    BaseSymmetricMatrix& operator+= (base_type const& _smatrix)
+    {
+        m_data += (static_cast<const BaseSymmetricMatrix &>(_smatrix).m_data);
+        return (*this);
+    }
+
+    BaseSymmetricMatrix& operator-= (base_type const& _smatrix)
+    {
+        m_data -= (static_cast<const BaseSymmetricMatrix &>(_smatrix).m_data);
+        return (*this);
+    }
+
+    using base_type::DIM;
+    using base_type::DATA_SIZE;
+    using base_type::m_data;
+    using base_type::operator-;
+    using base_type::operator*;
+
+}; //end BaseSymmetricMatrix
+
+
+template <size_t N>
+class SymmetricMatrix : public BaseSymmetricMatrix<N>
+{
+  public:
+    typedef BaseSymmetricMatrix<N> base_type;
+    typedef typename  base_type::base_type base_base_type;
+
+    using base_type::DIM;
+    using base_type::DATA_SIZE;
+    using base_type::m_data;
+
+  public:
+    SymmetricMatrix ()
+        : base_type (VectorView(m_adata, DATA_SIZE))
+    {
+    }
+
+    explicit
+    SymmetricMatrix (ConstVectorView _data)
+        : base_type (VectorView(m_adata, DATA_SIZE))
+    {
+        assert (_data.size() == DATA_SIZE);
+        m_data = _data;
+    }
+
+    explicit
+    SymmetricMatrix (const double _data[DATA_SIZE])
+        : base_type (VectorView(m_adata, DATA_SIZE))
+    {
+        std::copy (_data, _data + DATA_SIZE, m_adata);
+    }
+
+    SymmetricMatrix (SymmetricMatrix const& _smatrix)
+        : base_type (VectorView(m_adata, DATA_SIZE))
+    {
+        m_data = _smatrix.m_data;
+    }
+
+    explicit
+    SymmetricMatrix (base_base_type const& _smatrix)
+        : base_type (VectorView(m_adata, DATA_SIZE))
+    {
+        m_data = static_cast<const ConstSymmetricMatrixView<DIM> &>(_smatrix).m_data;
+    }
+
+    explicit
+    SymmetricMatrix (ConstMatrixView const& _matrix)
+        : base_type (VectorView(m_adata, DATA_SIZE))
+    {
+        assert (_matrix.rows() == N && _matrix.columns() == N);
+        for (size_t i = 0; i < N; ++i)
+            for (size_t j = 0; j <= i ; ++j)
+                (*this)(i,j) = _matrix(i,j);
+    }
+
+    SymmetricMatrix& operator= (SymmetricMatrix const& _smatrix)
+    {
+        m_data = _smatrix.m_data;
+        return (*this);
+    }
+
+    SymmetricMatrix& operator= (base_base_type const& _smatrix)
+    {
+
+        m_data = static_cast<const ConstSymmetricMatrixView<DIM> &>(_smatrix).m_data;
+        return (*this);
+    }
+
+    SymmetricMatrix& operator= (ConstMatrixView const& _matrix)
+    {
+        assert (_matrix.rows() == N && _matrix.columns() == N);
+        for (size_t i = 0; i < N; ++i)
+            for (size_t j = 0; j <= i ; ++j)
+                (*this)(i,j) = _matrix(i,j);
+
+        return (*this);
+    }
+
+    // needed for accessing m_adata
+    friend class ConstSymmetricMatrixView<DIM>;
+    friend class SymmetricMatrixView<DIM>;
+  private:
+      double m_adata[DATA_SIZE];
+}; //end SymmetricMatrix
+
+
+template <size_t N>
+class ConstSymmetricMatrixView : public ConstBaseSymmetricMatrix<N>
+{
+  public:
+    typedef ConstBaseSymmetricMatrix<N> base_type;
+
+    using base_type::DIM;
+    using base_type::DATA_SIZE;
+    using base_type::m_data;
+
+
+  public:
+
+    explicit
+    ConstSymmetricMatrixView (ConstVectorView _data)
+        : base_type (const_vector_view_cast(_data))
+    {
+        assert (_data.size() == DATA_SIZE);
+    }
+
+    explicit
+    ConstSymmetricMatrixView (const double _data[DATA_SIZE])
+        : base_type (const_vector_view_cast (ConstVectorView (_data, DATA_SIZE)))
+    {
+    }
+
+    ConstSymmetricMatrixView (const ConstSymmetricMatrixView & _smatrix)
+        : base_type (_smatrix.m_data)
+    {
+    }
+
+    ConstSymmetricMatrixView (const base_type & _smatrix)
+        : base_type (static_cast<const ConstSymmetricMatrixView &>(_smatrix).m_data)
+    {
+    }
+
+}; //end SymmetricMatrix
+
+
+// declaration needed for friend clause
+template <size_t N>
+void swap_view(SymmetricMatrixView<N> & m1, SymmetricMatrixView<N> & m2);
+
+
+template <size_t N>
+class SymmetricMatrixView : public BaseSymmetricMatrix<N>
+{
+  public:
+    typedef BaseSymmetricMatrix<N> base_type;
+    typedef typename  base_type::base_type base_base_type;
+
+    using base_type::DIM;
+    using base_type::DATA_SIZE;
+    using base_type::m_data;
+
+  public:
+
+    explicit
+    SymmetricMatrixView (VectorView _data)
+        : base_type (_data)
+    {
+        assert (_data.size() == DATA_SIZE);
+    }
+
+    explicit
+    SymmetricMatrixView (double _data[DATA_SIZE])
+        : base_type (VectorView (_data, DATA_SIZE))
+    {
+    }
+
+    SymmetricMatrixView (const SymmetricMatrixView & _smatrix)
+        : base_type (_smatrix.m_data)
+    {
+    }
+
+    SymmetricMatrixView (SymmetricMatrix<DIM> & _smatrix)
+        : base_type (VectorView (_smatrix.m_adata, DATA_SIZE))
+    {
+    }
+
+    SymmetricMatrixView& operator= (const SymmetricMatrixView & _smatrix)
+    {
+        m_data = _smatrix.m_data;
+        return (*this);
+    }
+
+    SymmetricMatrixView& operator= (const base_base_type & _smatrix)
+    {
+        m_data = static_cast<const ConstSymmetricMatrixView<DIM> &>(_smatrix).m_data;
+        return (*this);
+    }
+
+    friend
+    void swap_view<N>(SymmetricMatrixView & m1, SymmetricMatrixView & m2);
+
+}; //end SymmetricMatrix
+
+
+
+
+/*
+ * class ConstBaseSymmetricMatrix methods
+ */
+
+template <size_t N>
+inline
+std::string ConstBaseSymmetricMatrix<N>::str() const
+{
+    std::ostringstream oss;
+    oss << (*this);
+    return oss.str();
+}
+
+template <size_t N>
+inline
+ConstSymmetricMatrixView<N-1>
+ConstBaseSymmetricMatrix<N>::main_minor_const_view() const
+{
+    ConstVectorView data(m_data.get_gsl_vector()->data, DATA_SIZE - DIM);
+    ConstSymmetricMatrixView<N-1> mm(data);
+    return mm;
+}
+
+template <size_t N>
+inline
+SymmetricMatrix<N> ConstBaseSymmetricMatrix<N>::operator- () const
+{
+    SymmetricMatrix<N> result;
+    for (size_t i = 0; i < DATA_SIZE; ++i)
+    {
+        result.m_data[i] = -m_data[i];
+    }
+    return result;
+}
+
+
+/*
+ * class ConstBaseSymmetricMatrix friend free functions
+ */
+
+template <size_t N>
+inline
+bool operator== (ConstBaseSymmetricMatrix<N> const& _smatrix1,
+                 ConstBaseSymmetricMatrix<N> const& _smatrix2)
+{
+    return (_smatrix1.m_data == _smatrix2.m_data);
+}
+
+/*
+ * class ConstBaseSymmetricMatrix related free functions
+ */
+
+template< size_t N, class charT >
+inline
+std::basic_ostream<charT> &
+operator<< (std::basic_ostream<charT> & os,
+            const ConstBaseSymmetricMatrix<N> & _matrix)
+{
+    os << "[[" << _matrix(0,0);
+    for (size_t j = 1; j < N; ++j)
+    {
+        os << ", " << _matrix(0,j);
+    }
+    os << "]";
+    for (size_t i = 1; i < N; ++i)
+    {
+        os << "\n [" << _matrix(i,0);
+        for (size_t j = 1; j < N; ++j)
+        {
+            os << ", " << _matrix(i,j);
+        }
+        os << "]";
+    }
+    os << "]";
+    return os;
+}
+
+
+/*
+ * class ConstBaseSymmetricMatrix specialized methods
+ */
+
+template<>
+inline
+size_t ConstBaseSymmetricMatrix<2>::get_index (size_t i, size_t j)
+{
+    return (i+j);
+}
+
+template<>
+inline
+size_t ConstBaseSymmetricMatrix<3>::get_index (size_t i, size_t j)
+{
+    size_t k = i + j;
+    if (i == 2 || j == 2)  ++k;
+    return k;
+}
+
+
+/*
+ * class BaseSymmetricMatrix methods
+ */
+
+template <size_t N>
+inline
+SymmetricMatrixView<N-1> BaseSymmetricMatrix<N>::main_minor_view()
+{
+    VectorView data(m_data.get_gsl_vector()->data, DATA_SIZE - DIM);
+    SymmetricMatrixView<N-1> mm(data);
+    return mm;
+}
+
+
+/*
+ * class SymmetricMatrixView friend free functions
+ */
+
+template <size_t N>
+inline
+void swap_view(SymmetricMatrixView<N> & m1, SymmetricMatrixView<N> & m2)
+{
+    swap_view(m1.m_data, m2.m_data);
+}
+
+} /* end namespace NL*/ } /* end namespace Geom*/
+
+
+
+
+#endif  // _NL_SYMMETRIC_MATRIX_FS_H_
+
+
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/numeric/vector.h b/include/2geom/numeric/vector.h
new file mode 100644
index 0000000..b66115b
--- /dev/null
+++ b/include/2geom/numeric/vector.h
@@ -0,0 +1,594 @@
+/*
+ * Vector, VectorView, ConstVectorView classes wrap the gsl vector routines;
+ * "views" mimic the semantic of C++ references: any operation performed
+ * on a "view" is actually performed on the "viewed object"
+ *
+ * Authors:
+ * 		Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 2008  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+
+
+
+#ifndef _NL_VECTOR_H_
+#define _NL_VECTOR_H_
+
+#include <cassert>
+#include <algorithm> // for std::swap
+#include <vector>
+#include <sstream>
+#include <string>
+
+
+#include <gsl/gsl_vector.h>
+#include <gsl/gsl_blas.h>
+
+
+namespace Geom { namespace NL {
+
+namespace detail
+{
+
+class BaseVectorImpl
+{
+  public:
+	double const& operator[](size_t i) const
+	{
+		return *gsl_vector_const_ptr(m_vector, i);
+	}
+
+	const gsl_vector* get_gsl_vector() const
+	{
+		return m_vector;
+	}
+	bool is_zero() const
+	{
+		return gsl_vector_isnull(m_vector);
+	}
+
+	bool is_positive() const
+	{
+	    for ( size_t i = 0; i < size(); ++i )
+	    {
+	        if ( (*this)[i] <= 0 ) return false;
+	    }
+		return true;
+	}
+
+	bool is_negative() const
+	{
+        for ( size_t i = 0; i < size(); ++i )
+        {
+            if ( (*this)[i] >= 0 ) return false;
+        }
+        return true;
+	}
+
+	bool is_non_negative() const
+	{
+		for ( size_t i = 0; i < size(); ++i )
+		{
+			if ( (*this)[i] < 0 ) return false;
+		}
+		return true;
+	}
+
+	double max() const
+	{
+		return gsl_vector_max(m_vector);
+	}
+
+	double min() const
+	{
+		return gsl_vector_min(m_vector);
+	}
+
+	size_t max_index() const
+	{
+		return gsl_vector_max_index(m_vector);
+	}
+
+	size_t min_index() const
+	{
+		return gsl_vector_min_index(m_vector);
+	}
+
+	size_t size() const
+	{
+		return m_size;
+	}
+
+	std::string str() const;
+
+	virtual ~BaseVectorImpl()
+	{
+	}
+
+  protected:
+	size_t m_size;
+	gsl_vector* m_vector;
+
+};  // end class BaseVectorImpl
+
+
+inline
+bool operator== (BaseVectorImpl const& v1, BaseVectorImpl const& v2)
+{
+	if (v1.size() != v2.size())	return false;
+
+	for (size_t i = 0; i < v1.size(); ++i)
+	{
+		if (v1[i] != v2[i])  return false;
+	}
+	return true;
+}
+
+template< class charT >
+inline
+std::basic_ostream<charT> &
+operator<< (std::basic_ostream<charT> & os, const BaseVectorImpl & _vector)
+{
+	if (_vector.size() == 0 ) return os;
+	os << "[" << _vector[0];
+	for (unsigned int i = 1; i < _vector.size(); ++i)
+	{
+		os << ", " << _vector[i];
+	}
+	os << "]";
+	return os;
+}
+
+inline
+std::string BaseVectorImpl::str() const
+{
+	std::ostringstream oss;
+	oss << (*this);
+	return oss.str();
+}
+
+inline
+double dot(BaseVectorImpl const& v1, BaseVectorImpl const& v2)
+{
+    double result;
+    gsl_blas_ddot(v1.get_gsl_vector(), v2.get_gsl_vector(), &result);
+    return result;
+}
+
+
+class VectorImpl : public BaseVectorImpl
+{
+  public:
+	typedef BaseVectorImpl base_type;
+
+  public:
+	void set_all(double x)
+	{
+		gsl_vector_set_all(m_vector, x);
+	}
+
+	void set_basis(size_t i)
+	{
+		gsl_vector_set_basis(m_vector, i);
+	}
+
+	using base_type::operator[];
+
+	double & operator[](size_t i)
+	{
+		return *gsl_vector_ptr(m_vector, i);
+	}
+
+	using base_type::get_gsl_vector;
+
+	gsl_vector* get_gsl_vector()
+	{
+		return m_vector;
+	}
+
+	void swap_elements(size_t i, size_t j)
+	{
+		gsl_vector_swap_elements(m_vector, i, j);
+	}
+
+	void reverse()
+	{
+		gsl_vector_reverse(m_vector);
+	}
+
+	VectorImpl & scale(double x)
+	{
+		gsl_vector_scale(m_vector, x);
+		return (*this);
+	}
+
+	VectorImpl & translate(double x)
+	{
+		gsl_vector_add_constant(m_vector, x);
+		return (*this);
+	}
+
+	VectorImpl & operator+=(base_type const& _vector)
+	{
+		gsl_vector_add(m_vector, _vector.get_gsl_vector());
+		return (*this);
+	}
+
+	VectorImpl & operator-=(base_type const& _vector)
+	{
+		gsl_vector_sub(m_vector, _vector.get_gsl_vector());
+		return (*this);
+	}
+
+};  // end class VectorImpl
+
+}  // end namespace detail
+
+
+using detail::operator==;
+using detail::operator<<;
+
+class Vector : public detail::VectorImpl
+{
+  public:
+	typedef detail::VectorImpl base_type;
+
+  public:
+	Vector(size_t n)
+	{
+		m_size = n;
+		m_vector = gsl_vector_alloc(n);
+	}
+
+	Vector(size_t n, double x)
+	{
+		m_size = n;
+		m_vector = gsl_vector_alloc(n);
+		gsl_vector_set_all(m_vector, x);
+	}
+
+	// create a vector with n elements all set to zero
+	// but the i-th that is set to 1
+	Vector(size_t n, size_t i)
+	{
+		m_size = n;
+		m_vector = gsl_vector_alloc(n);
+		gsl_vector_set_basis(m_vector, i);
+	}
+
+	Vector(Vector const& _vector)
+        : base_type()
+	{
+		m_size = _vector.size();
+		m_vector = gsl_vector_alloc(size());
+		gsl_vector_memcpy(m_vector, _vector.m_vector);
+	}
+
+	explicit
+	Vector(base_type::base_type const& _vector)
+	{
+		m_size = _vector.size();
+		m_vector = gsl_vector_alloc(size());
+		gsl_vector_memcpy(m_vector, _vector.get_gsl_vector());
+	}
+
+	~Vector() override
+	{
+		gsl_vector_free(m_vector);
+	}
+
+
+	Vector & operator=(Vector const& _vector)
+	{
+		assert( size() == _vector.size() );
+		gsl_vector_memcpy(m_vector, _vector.m_vector);
+		return (*this);
+	}
+
+	Vector & operator=(base_type::base_type const& _vector)
+	{
+		assert( size() == _vector.size() );
+		gsl_vector_memcpy(m_vector, _vector.get_gsl_vector());
+		return (*this);
+	}
+
+	Vector & scale(double x)
+	{
+		return static_cast<Vector&>( base_type::scale(x) );
+	}
+
+	Vector & translate(double x)
+	{
+		return static_cast<Vector&>( base_type::translate(x) );
+	}
+
+	Vector & operator+=(base_type::base_type const& _vector)
+	{
+		return static_cast<Vector&>( base_type::operator+=(_vector) );
+	}
+
+	Vector & operator-=(base_type::base_type const& _vector)
+	{
+		return static_cast<Vector&>( base_type::operator-=(_vector) );
+	}
+
+	friend
+	void swap(Vector & v1, Vector & v2);
+	friend
+	void swap_any(Vector & v1, Vector & v2);
+
+}; // end class Vector
+
+
+// warning! these operations invalidate any view of the passed vector objects
+inline
+void swap(Vector & v1, Vector & v2)
+{
+    assert(v1.size() == v2.size());
+    using std::swap;
+    swap(v1.m_vector, v2.m_vector);
+}
+
+inline
+void swap_any(Vector & v1, Vector & v2)
+{
+	using std::swap;
+    swap(v1.m_vector, v2.m_vector);
+    swap(v1.m_size, v2.m_size);
+}
+
+
+class ConstVectorView : public detail::BaseVectorImpl
+{
+  public:
+	typedef detail::BaseVectorImpl base_type;
+
+  public:
+	ConstVectorView(const base_type & _vector, size_t n, size_t offset = 0)
+		: m_vector_view( gsl_vector_const_subvector(_vector.get_gsl_vector(), offset, n) )
+	{
+		m_size = n;
+		m_vector = const_cast<gsl_vector*>( &(m_vector_view.vector) );
+	}
+
+	ConstVectorView(const base_type & _vector, size_t n, size_t offset , size_t stride)
+		: m_vector_view( gsl_vector_const_subvector_with_stride(_vector.get_gsl_vector(), offset, stride, n) )
+	{
+		m_size = n;
+		m_vector = const_cast<gsl_vector*>( &(m_vector_view.vector) );
+	}
+
+    ConstVectorView(const double* _vector, size_t n, size_t offset = 0)
+        : m_vector_view( gsl_vector_const_view_array(_vector + offset, n) )
+    {
+        m_size = n;
+        m_vector = const_cast<gsl_vector*>( &(m_vector_view.vector) );
+    }
+
+    ConstVectorView(const double* _vector, size_t n, size_t offset, size_t stride)
+        : m_vector_view( gsl_vector_const_view_array_with_stride(_vector + offset, stride, n) )
+    {
+        m_size = n;
+        m_vector = const_cast<gsl_vector*>( &(m_vector_view.vector) );
+    }
+
+	explicit
+	ConstVectorView(gsl_vector_const_view  _gsl_vector_view)
+		: m_vector_view(_gsl_vector_view)
+	{
+		m_vector = const_cast<gsl_vector*>( &(m_vector_view.vector) );
+		m_size = m_vector->size;
+	}
+
+    explicit
+    ConstVectorView(const std::vector<double>&  _vector)
+        : m_vector_view( gsl_vector_const_view_array(&(_vector[0]), _vector.size()) )
+    {
+        m_vector = const_cast<gsl_vector*>( &(m_vector_view.vector) );
+        m_size = _vector.size();
+    }
+
+	ConstVectorView(const ConstVectorView & _vector)
+		: base_type(),
+		  m_vector_view(_vector.m_vector_view)
+	{
+		m_size = _vector.size();
+		m_vector = const_cast<gsl_vector*>( &(m_vector_view.vector) );
+	}
+
+	ConstVectorView(const base_type & _vector)
+		: m_vector_view(gsl_vector_const_subvector(_vector.get_gsl_vector(), 0, _vector.size()))
+	{
+		m_size = _vector.size();
+		m_vector = const_cast<gsl_vector*>( &(m_vector_view.vector) );
+	}
+
+  private:
+	gsl_vector_const_view m_vector_view;
+
+}; // end class  ConstVectorView
+
+
+
+
+class VectorView : public detail::VectorImpl
+{
+  public:
+	typedef detail::VectorImpl base_type;
+
+  public:
+	VectorView(base_type & _vector, size_t n, size_t offset = 0, size_t stride = 1)
+	{
+		m_size = n;
+		if (stride == 1)
+		{
+			m_vector_view
+				= gsl_vector_subvector(_vector.get_gsl_vector(), offset, n);
+			m_vector = &(m_vector_view.vector);
+		}
+		else
+		{
+			m_vector_view
+				= gsl_vector_subvector_with_stride(_vector.get_gsl_vector(), offset, stride, n);
+			m_vector = &(m_vector_view.vector);
+		}
+	}
+
+    VectorView(double* _vector, size_t n, size_t offset = 0, size_t stride = 1)
+    {
+        m_size = n;
+        if (stride == 1)
+        {
+            m_vector_view
+                = gsl_vector_view_array(_vector + offset, n);
+            m_vector = &(m_vector_view.vector);
+        }
+        else
+        {
+            m_vector_view
+                = gsl_vector_view_array_with_stride(_vector + offset, stride, n);
+            m_vector = &(m_vector_view.vector);
+        }
+
+    }
+
+	VectorView(const VectorView & _vector)
+        : base_type()
+	{
+		m_size = _vector.size();
+		m_vector_view = _vector.m_vector_view;
+		m_vector = &(m_vector_view.vector);
+	}
+
+	VectorView(Vector & _vector)
+	{
+		m_size = _vector.size();
+		m_vector_view = gsl_vector_subvector(_vector.get_gsl_vector(), 0, size());
+		m_vector = &(m_vector_view.vector);
+	}
+
+	explicit
+	VectorView(gsl_vector_view _gsl_vector_view)
+		: m_vector_view(_gsl_vector_view)
+	{
+		m_vector = &(m_vector_view.vector);
+		m_size = m_vector->size;
+	}
+
+	explicit
+	VectorView(std::vector<double> & _vector)
+	{
+	    m_size = _vector.size();
+	    m_vector_view = gsl_vector_view_array(&(_vector[0]), _vector.size());
+	    m_vector = &(m_vector_view.vector);
+	}
+
+	VectorView & operator=(VectorView const& _vector)
+	{
+		assert( size() == _vector.size() );
+		gsl_vector_memcpy(m_vector, _vector.get_gsl_vector());
+		return (*this);
+	}
+
+	VectorView & operator=(base_type::base_type const& _vector)
+	{
+		assert( size() == _vector.size() );
+		gsl_vector_memcpy(m_vector, _vector.get_gsl_vector());
+		return (*this);
+	}
+
+	VectorView & scale(double x)
+	{
+		return static_cast<VectorView&>( base_type::scale(x) );
+	}
+
+	VectorView & translate(double x)
+	{
+		return static_cast<VectorView&>( base_type::translate(x) );
+	}
+
+	VectorView & operator+=(base_type::base_type const& _vector)
+	{
+		return static_cast<VectorView&>( base_type::operator+=(_vector) );
+	}
+
+	VectorView & operator-=(base_type::base_type const& _vector)
+	{
+		return static_cast<VectorView&>( base_type::operator-=(_vector) );
+	}
+
+	friend
+	void swap_view(VectorView & v1, VectorView & v2);
+
+  private:
+	gsl_vector_view m_vector_view;
+
+}; // end class VectorView
+
+
+inline
+void swap_view(VectorView & v1, VectorView & v2)
+{
+	assert( v1.size() == v2.size() );
+	using std::swap;
+	swap(v1.m_vector_view, v2.m_vector_view); // not swap m_vector too
+}
+
+inline
+const VectorView & const_vector_view_cast (const ConstVectorView & view)
+{
+    const detail::BaseVectorImpl & bvi
+        = static_cast<const detail::BaseVectorImpl &>(view);
+    const VectorView & vv = reinterpret_cast<const VectorView &>(bvi);
+    return vv;
+}
+
+inline
+VectorView & const_vector_view_cast (ConstVectorView & view)
+{
+    detail::BaseVectorImpl & bvi
+        = static_cast<detail::BaseVectorImpl &>(view);
+    VectorView & vv = reinterpret_cast<VectorView &>(bvi);
+    return vv;
+}
+
+
+} } // end namespaces
+
+
+#endif /*_NL_VECTOR_H_*/
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/ord.h b/include/2geom/ord.h
new file mode 100644
index 0000000..e190a4a
--- /dev/null
+++ b/include/2geom/ord.h
@@ -0,0 +1,80 @@
+/** @file
+ * @brief Comparator template
+ *//*
+ * Authors:
+ *      ? <?@?.?>
+ * 
+ * Copyright ?-?  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_ORD_H
+#define LIB2GEOM_SEEN_ORD_H
+
+namespace {
+
+enum Cmp {
+  LESS_THAN=-1,
+  GREATER_THAN=1,
+  EQUAL_TO=0
+};
+
+static inline Cmp operator-(Cmp x) {
+  switch(x) {
+  case LESS_THAN:
+    return GREATER_THAN;
+  case GREATER_THAN:
+    return LESS_THAN;
+  case EQUAL_TO:
+    return EQUAL_TO;
+  }
+}
+
+template <typename T1, typename T2>
+inline Cmp cmp(T1 const &a, T2 const &b) {
+  if ( a < b ) {
+    return LESS_THAN;
+  } else if ( b < a ) {
+    return GREATER_THAN;
+  } else {
+    return EQUAL_TO;
+  }
+}
+
+}
+
+#endif
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/orphan-code/arc-length.h b/include/2geom/orphan-code/arc-length.h
new file mode 100644
index 0000000..8029f04
--- /dev/null
+++ b/include/2geom/orphan-code/arc-length.h
@@ -0,0 +1,58 @@
+/**
+ * \file arc-length.h
+ * \brief Arc length computations for paths
+ *//*
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef __2GEOM_ARC_LENGTH_H
+#define __2GEOM_ARC_LENGTH_H
+
+#include <2geom/path.h>
+
+/* Routines in this group return a path that looks the same, but
+ * include extra knots for certain points of interest. */
+
+/* find_vector_extreme_points
+ * extreme points . dir.
+ */
+
+double arc_length_subdividing(Geom::Path const & p, double tol);
+double arc_length_integrating(Geom::Path const & p, double tol);
+
+#endif
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/orphan-code/chebyshev.h b/include/2geom/orphan-code/chebyshev.h
new file mode 100644
index 0000000..f729e1f
--- /dev/null
+++ b/include/2geom/orphan-code/chebyshev.h
@@ -0,0 +1,30 @@
+#ifndef _CHEBYSHEV
+#define _CHEBYSHEV
+
+#include <2geom/sbasis.h>
+#include <2geom/interval.h>
+
+/*** Conversion between Chebyshev approximation and SBasis.
+ * 
+ */
+
+namespace Geom{
+
+SBasis chebyshev_approximant (double (*f)(double,void*), int order, Interval in, void* p=0);
+SBasis chebyshev_approximant_interpolating (double (*f)(double,void*), int order, Interval in, void* p=0);
+SBasis chebyshev(unsigned n);
+
+};
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
+
+#endif
diff --git a/include/2geom/orphan-code/intersection-by-smashing.h b/include/2geom/orphan-code/intersection-by-smashing.h
new file mode 100644
index 0000000..996ec99
--- /dev/null
+++ b/include/2geom/orphan-code/intersection-by-smashing.h
@@ -0,0 +1,78 @@
+/*
+ * Diffeomorphism-based intersector: given two curves
+ *  M(t)=(x(t),y(t)) and N(u)=(X(u),Y(u))
+ * and supposing M is a graph over the x-axis, we compute y(x) and the
+ * transformation:
+ *  X <- X
+ *  Y <- Y - y(X)
+ * smashes M on the x axis. The intersections are then given by the roots of:
+ *  Y(u) - y(X(u)) = 0
+ *//*
+ * Authors:
+ * 		J.-F. Barraud    <jfbarraud at gmail.com>
+ * Copyright 2010  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef SEEN_LIB2GEOM_INTERSECTION_BY_SMASHING_H
+#define SEEN_LIB2GEOM_INTERSECTION_BY_SMASHING_H
+
+#include <2geom/d2.h>
+#include <2geom/interval.h>
+#include <2geom/sbasis.h>
+#include <2geom/sbasis-geometric.h>
+#include <cstdlib>
+#include <vector>
+#include <algorithm>
+
+
+namespace Geom{
+
+struct SmashIntersection {
+	Rect times;
+	Rect bbox;
+};
+
+std::vector<SmashIntersection> smash_intersect( D2<SBasis> const &a, D2<SBasis> const &b, double tol);
+std::vector<SmashIntersection> monotonic_smash_intersect( D2<SBasis> const &a, D2<SBasis> const &b, double tol);
+//std::vector<Intersection> monotonic_smash_intersect( Curve const &a, double a_from, double a_to,
+//			                                         Curve const &b, double b_from, double b_to, double tol);
+
+std::vector<Interval> monotonicSplit(D2<SBasis> const &p);
+
+} // end namespace Geom
+
+#endif // !SEEN_LIB2GEOM_INTERSECTION_BY_SMASHING_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/orphan-code/linear-of.h b/include/2geom/orphan-code/linear-of.h
new file mode 100644
index 0000000..9ba1fb2
--- /dev/null
+++ b/include/2geom/orphan-code/linear-of.h
@@ -0,0 +1,269 @@
+/**
+ * \file
+ * \brief  Linear fragment function class
+ *
+ *  Authors:
+ *   Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *   Michael Sloan <mgsloan@gmail.com>
+ *
+ * Copyright (C) 2006-2007 authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef SEEN_LINEAR_OF_H
+#define SEEN_LINEAR_OF_H
+#include <2geom/interval.h>
+#include <2geom/math-utils.h>
+
+namespace Geom{
+
+template <typename T>
+inline T lerp(double t, T a, T b) { return a*(1-t) + b*t; }
+
+template <typename T>
+class SBasisOf;
+
+template <typename T>
+class HatOf{
+public:
+    HatOf () {}
+    HatOf(T d) :d(d) {}
+    operator T() const { return d; }
+    T d;
+};
+
+template <typename T>
+class TriOf{
+public:
+    TriOf () {}
+    TriOf(double d) :d(d) {}
+    operator T() const { return d; }
+    T d;
+};
+
+
+//--------------------------------------------------------------------------
+#ifdef USE_SBASIS_OF
+template <typename T>
+class LinearOf;
+typedef Geom::LinearOf<double> Linear;
+#endif
+//--------------------------------------------------------------------------
+
+template <typename T>
+class LinearOf{
+public:
+    T a[2];
+    LinearOf() {}
+    LinearOf(T aa, T b) {a[0] = aa; a[1] = b;}
+    //LinearOf(double aa, double b) {a[0] = T(aa); a[1] = T(b);}
+    LinearOf(HatOf<T> h, TriOf<T> t) {
+        a[0] = T(h) - T(t)/2; 
+        a[1] = T(h) + T(t)/2;
+    }
+
+    LinearOf(HatOf<T> h) {
+        a[0] = T(h); 
+        a[1] = T(h);
+    }
+
+    unsigned input_dim(){return T::input_dim() + 1;}
+
+    T operator[](const int i) const {
+        assert(i >= 0);
+        assert(i < 2);
+        return a[i];
+    }
+    T& operator[](const int i) {
+        assert(i >= 0);
+        assert(i < 2);
+        return a[i];
+    }
+
+    //IMPL: FragmentConcept
+    typedef T output_type;
+    inline bool isZero() const { return a[0].isZero() && a[1].isZero(); }
+    inline bool isConstant() const { return a[0] == a[1]; }
+    inline bool isFinite() const { return std::isfinite(a[0]) && std::isfinite(a[1]); }
+
+    inline T at0() const { return a[0]; }
+    inline T at1() const { return a[1]; }
+
+    inline T valueAt(double t) const { return lerp(t, a[0], a[1]); }
+    inline T operator()(double t) const { return valueAt(t); }
+
+    //defined in sbasis.h
+    inline SBasisOf<T> toSBasis() const;
+
+//This is specific for T=double!!
+    inline OptInterval bounds_exact() const { return Interval(a[0], a[1]); }
+    inline OptInterval bounds_fast() const { return bounds_exact(); }
+    inline OptInterval bounds_local(double u, double v) const { return Interval(valueAt(u), valueAt(v)); }
+
+    operator TriOf<T>() const {
+        return a[1] - a[0];
+    }
+    operator HatOf<T>() const {
+        return (a[1] + a[0])/2;
+    }
+};
+
+template <>
+unsigned LinearOf<double>::input_dim(){return 1;}
+template <>
+inline OptInterval LinearOf<double>::bounds_exact() const { return Interval(a[0], a[1]); }
+template <>
+inline OptInterval LinearOf<double>::bounds_fast() const { return bounds_exact(); }
+template <>
+inline OptInterval LinearOf<double>::bounds_local(double u, double v) const { return Interval(valueAt(u), valueAt(v)); }
+template <>
+inline bool LinearOf<double>::isZero() const { return a[0]==0 && a[1]==0; }
+
+template <typename T>
+inline LinearOf<T> reverse(LinearOf<T> const &a) { return LinearOf<T>(a[1], a[0]); }
+
+//IMPL: AddableConcept
+template <typename T>
+inline LinearOf<T> operator+(LinearOf<T> const & a, LinearOf<T> const & b) {
+    return LinearOf<T>(a[0] + b[0], a[1] + b[1]);
+}
+template <typename T>
+inline LinearOf<T> operator-(LinearOf<T> const & a, LinearOf<T> const & b) {
+    return LinearOf<T>(a[0] - b[0], a[1] - b[1]);
+}
+template <typename T>
+inline LinearOf<T>& operator+=(LinearOf<T> & a, LinearOf<T> const & b) {
+    a[0] += b[0]; a[1] += b[1];
+    return a;
+}
+template <typename T>
+inline LinearOf<T>& operator-=(LinearOf<T> & a, LinearOf<T> const & b) {
+    a[0] -= b[0]; a[1] -= b[1];
+    return a;
+}
+//IMPL: OffsetableConcept
+template <typename T>
+inline LinearOf<T> operator+(LinearOf<T> const & a, double b) {
+    return LinearOf<T>(a[0] + b, a[1] + b);
+}
+template <typename T>
+inline LinearOf<T> operator-(LinearOf<T> const & a, double b) {
+    return LinearOf<T>(a[0] - b, a[1] - b);
+}
+template <typename T>
+inline LinearOf<T>& operator+=(LinearOf<T> & a, double b) {
+    a[0] += b; a[1] += b;
+    return a;
+}
+template <typename T>
+inline LinearOf<T>& operator-=(LinearOf<T> & a, double b) {
+    a[0] -= b; a[1] -= b;
+    return a;
+}
+/*
+//We can in fact offset in coeff ring T...
+template <typename T>
+inline LinearOf<T> operator+(LinearOf<T> const & a, T b) {
+    return LinearOf<T>(a[0] + b, a[1] + b);
+}
+template <typename T>
+inline LinearOf<T> operator-(LinearOf<T> const & a, T b) {
+    return LinearOf<T>(a[0] - b, a[1] - b);
+}
+template <typename T>
+inline LinearOf<T>& operator+=(LinearOf<T> & a, T b) {
+    a[0] += b; a[1] += b;
+    return a;
+}
+template <typename T>
+inline LinearOf<T>& operator-=(LinearOf<T> & a, T b) {
+    a[0] -= b; a[1] -= b;
+    return a;
+}
+*/
+
+//IMPL: boost::EqualityComparableConcept
+template <typename T>
+inline bool operator==(LinearOf<T> const & a, LinearOf<T> const & b) {
+    return a[0] == b[0] && a[1] == b[1];
+}
+template <typename T>
+inline bool operator!=(LinearOf<T> const & a, LinearOf<T> const & b) {
+    return a[0] != b[0] || a[1] != b[1];
+}
+//IMPL: ScalableConcept
+template <typename T>
+inline LinearOf<T> operator-(LinearOf<T> const &a) {
+    return LinearOf<T>(-a[0], -a[1]);
+}
+template <typename T>
+inline LinearOf<T> operator*(LinearOf<T> const & a, double b) {
+    return LinearOf<T>(a[0]*b, a[1]*b);
+}
+template <typename T>
+inline LinearOf<T> operator/(LinearOf<T> const & a, double b) {
+    return LinearOf<T>(a[0]/b, a[1]/b);
+}
+template <typename T>
+inline LinearOf<T> operator*=(LinearOf<T> & a, double b) {
+    a[0] *= b; a[1] *= b;
+    return a;
+}
+template <typename T>
+inline LinearOf<T> operator/=(LinearOf<T> & a, double b) {
+    a[0] /= b; a[1] /= b;
+    return a;
+}
+/*
+//We can in fact rescale in coeff ring T... (but not divide!)
+template <typename T>
+inline LinearOf<T> operator*(LinearOf<T> const & a, T b) {
+    return LinearOf<T>(a[0]*b, a[1]*b);
+}
+template <typename T>
+inline LinearOf<T> operator/(LinearOf<T> const & a, T b) {
+    return LinearOf<T>(a[0]/b, a[1]/b);
+}
+template <typename T>
+inline LinearOf<T> operator*=(LinearOf<T> & a, T b) {
+    a[0] *= b; a[1] *= b;
+    return a;
+}
+*/
+
+};
+
+#endif //SEEN_LINEAR_OF_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/orphan-code/linearN.h b/include/2geom/orphan-code/linearN.h
new file mode 100644
index 0000000..bb27c30
--- /dev/null
+++ b/include/2geom/orphan-code/linearN.h
@@ -0,0 +1,363 @@
+/**
+ * @file
+ * @brief  LinearN fragment function class
+ *//*
+ *  Authors:
+ *   JF Barraud <jf.barraud@gmail.com>
+ *   Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *   Michael Sloan <mgsloan@gmail.com>
+ *
+ * Copyright (C) 2006-2007 authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef SEEN_LINEARN_H
+#define SEEN_LINEARN_H
+#include <2geom/interval.h>
+#include <2geom/math-utils.h>
+#include <2geom/linear.h> //for conversion purpose ( + lerp() )
+
+#include <iostream>
+
+namespace Geom{
+
+//TODO: define this only once!! (see linear.h)
+inline double lerpppp(double t, double a, double b) { return a*(1-t) + b*t; }
+
+template<unsigned n>
+class SBasisN;
+
+template<unsigned n>
+class LinearN{
+public:
+    double a[1<<n];// 1<<n is 2^n
+    LinearN() {
+        for (unsigned i=0; i < (1<<n); i++){
+            a[i] = 0.;
+        }
+    }
+    LinearN(double aa[]) {
+        for (unsigned i=0; i < (1<<n); i++){
+            a[i] = aa[i];
+        }
+    }
+    LinearN(double c) {
+        for (unsigned i=0; i<(1<<n); i++){
+            a[i] = c;
+        }
+    }
+    LinearN(LinearN<n-1> const &aa, LinearN<n-1> const &b, unsigned var=0) {
+//         for (unsigned i=0; i<(1<<n-1); i++){
+//             a[i] = aa[i];
+//             a[i+(1<<(n-1))] = b[i];
+//         }
+        unsigned mask = (1<<var)-1;
+        for (unsigned i=0; i < (1<<(n-1)); i++){
+            unsigned low_i = i & mask, high_i = i & ~mask;
+            unsigned idx0 = (high_i<<1)|low_i;
+            unsigned idx1 = (high_i<<1)|(1<<var)|low_i;
+            a[idx0] = aa[i];
+            a[idx1] = b[i];
+        }
+
+    }
+    double operator[](const int i) const {
+        assert( i >= 0 );
+        assert( i < (1<<n) );
+        return a[i];
+    }
+    double& operator[](const int i) {
+        assert(i >= 0);
+        assert(i < (1<<n) );
+        return a[i];
+    }
+
+    //IMPL: FragmentConcept
+    typedef double output_type;
+    unsigned input_dim() const {return n;}
+    inline bool isZero() const { 
+        for (unsigned i=0; i < (1<<n); i++){
+            if (a[i] != 0) return false;
+        }
+        return true; }
+    inline bool isConstant() const { 
+        for (unsigned i=1; i < (1<<n); i++){
+            if (a[i] != a[0]) return false;
+        }
+        return true; }
+    inline bool isConstant(unsigned var) const { 
+        unsigned mask = (1<<var)-1;
+        for (unsigned i=0; i < (1<<(n-1)); i++){
+            unsigned low_i = i & mask, high_i = i & ~mask;
+            unsigned idx0 = (high_i<<1)|low_i;
+            unsigned idx1 = (high_i<<1)|(1<<var)|low_i;
+            if (a[idx0] != a[idx1]) return false;
+        }
+        return true; 
+    }
+    inline bool isFinite() const {
+        for (unsigned i=0; i < (1<<n); i++){
+            if ( !std::isfinite(a[i]) ) return false;
+        }
+        return true; }
+    //value if k-th variable is set to 0.
+    inline LinearN<n-1> at0(unsigned k=0) const {
+        LinearN<n-1> res;
+        unsigned mask = (1<<k)-1;
+        for (unsigned i=0; i < (1<<(n-1)); i++){
+            unsigned low_i = i & mask, high_i = i & ~mask;
+            unsigned idx = (high_i<<1)|low_i;
+            res[i] = a[idx];
+        }
+        return res;
+    }
+    //value if k-th variable is set to 1.
+    inline LinearN<n-1> at1(unsigned k=0) const {
+        LinearN<n-1> res;
+        for (unsigned i=0; i < (1<<(n-1)); i++){
+            unsigned mask = (1<<k)-1;
+            unsigned low_i = i & mask, high_i = i & ~mask;
+            unsigned idx = (high_i<<1)|(1<<k)|low_i;
+            res[i] = a[idx];
+        }
+        return res;
+    }
+    inline double atCorner(unsigned k) const {
+        assert( k < (1<<n) );
+        return a[k];
+    }
+    inline double atCorner(double t[]) const { 
+        unsigned k=0;
+        for(unsigned i=0; i<n; i++){
+            if (t[i] == 1.) k = k | (1<<i);
+            else assert( t[i] == 0. );
+        }
+        return atCorner(k);
+    }
+    inline LinearN<n-1> partialEval(double t, unsigned var=0 ) const {
+        LinearN<n-1> res;
+        res = at0(var)*(1-t) + at1(var)*t;
+        return res;
+    }
+    
+    //fixed and flags are used for recursion.
+    inline double valueAt(double t[], unsigned fixed=0, unsigned flags=0 ) const {
+        if (fixed == n) {
+            return a[flags]; 
+        }else{
+            double a0 = valueAt(t, fixed+1, flags);
+            double a1 = valueAt(t, fixed+1, flags|(1<<fixed));
+            return lerpppp( t[fixed], a0, a1 );
+        }
+    }
+    inline double operator()(double t[]) const { return valueAt(t); }
+
+    //defined in sbasisN.h
+    inline SBasisN<n> toSBasisN() const;
+
+    inline OptInterval bounds_exact() const {
+        double min=a[0], max=a[0];
+        for (unsigned i=1; i < (1<<n); i++){
+            if (a[i] < min) min = a[i];
+            if (a[i] > max) max = a[i];
+        }
+        return Interval(min, max); 
+    }
+    inline OptInterval bounds_fast() const { return bounds_exact(); }
+    //inline OptInterval bounds_local(double u, double v) const { return Interval(valueAt(u), valueAt(v)); }
+};
+
+//LinearN<0> are doubles. Specialize them out.
+template<>
+class LinearN<0>{
+public:
+    double d;
+    LinearN () {}
+    LinearN(double d) :d(d) {}
+    operator double() const { return d; }
+    double operator[](const int i) const {assert (i==0); return d;}
+    double& operator[](const int i) {assert (i==0); return d;}
+    typedef double output_type;
+    unsigned input_dim() const {return 0;}
+    inline bool isZero() const { return d==0; }
+    inline bool isConstant() const { return true; }
+    inline bool isFinite() const { return std::isfinite(d); }
+};
+
+//LinearN<1> are usual Linear. Allow conversion.
+Linear toLinear(LinearN<1> f){
+    return Linear(f[0],f[1]);
+}
+
+
+
+//inline Linear reverse(Linear const &a) { return Linear(a[1], a[0]); }
+
+//IMPL: AddableConcept
+template<unsigned n>
+inline LinearN<n> operator+(LinearN<n> const & a, LinearN<n> const & b) {
+    LinearN<n> res;
+    for (unsigned i=0; i < (1<<n); i++){
+        res[i] = a[i] + b[i];
+    }
+    return res;
+}
+template<unsigned n>
+inline LinearN<n> operator-(LinearN<n> const & a, LinearN<n> const & b) {
+    LinearN<n> res;
+    for (unsigned i=0; i < (1<<n); i++){
+        res[i] = a[i] - b[i];
+    }
+    return res;
+}
+template<unsigned n>
+inline LinearN<n>& operator+=(LinearN<n> & a, LinearN<n> const & b) {
+    for (unsigned i=0; i < (1<<n); i++){
+        a[i] += b[i];
+    }
+    return a;
+}
+template<unsigned n>
+inline LinearN<n>& operator-=(LinearN<n> & a, LinearN<n> const & b) {
+    for (unsigned i=0; i < (1<<n); i++){
+        a[i] -= b[i];
+    }
+    return a;
+}
+//IMPL: OffsetableConcept
+template<unsigned n>
+inline LinearN<n> operator+(LinearN<n> const & a, double b) {
+    LinearN<n> res;
+    for (unsigned i=0; i < (1<<n); i++){
+        res[i] = a[i] + b;
+    }
+    return res;
+}
+template<unsigned n>
+inline LinearN<n> operator-(LinearN<n> const & a, double b) {
+    LinearN<n> res;
+    for (unsigned i=0; i < (1<<n); i++){
+        res[i] = a[i] - b;
+    }
+    return res;
+}
+template<unsigned n>
+inline LinearN<n>& operator+=(LinearN<n> & a, double b) {
+    for (unsigned i=0; i < (1<<n); i++){
+        a[i] += b;
+    }
+    return a;
+}
+template<unsigned n>
+inline LinearN<n>& operator-=(LinearN<n> & a, double b) {
+    for (unsigned i=0; i < (1<<n); i++){
+        a[i] -= b;
+    }
+    return a;
+}
+//IMPL: boost::EqualityComparableConcept
+template<unsigned n>
+inline bool operator==(LinearN<n> const & a, LinearN<n> const & b) {
+    for (unsigned i=0; i < (1<<n); i++){
+        if (a[i] != b[i]) return false;
+    }
+    return true;
+}
+template<unsigned n>
+inline bool operator!=(LinearN<n> const & a, LinearN<n> const & b) {
+    return !(a==b);
+}
+//IMPL: ScalableConcept
+template<unsigned n>
+inline LinearN<n> operator-(LinearN<n> const &a) {
+    LinearN<n> res;
+    for (unsigned i=0; i < (1<<n); i++){
+        res[i] = -a[i];
+    }
+    return res;
+}
+template<unsigned n>
+inline LinearN<n> operator*(LinearN<n> const & a, double b) {
+    LinearN<n> res;
+    for (unsigned i=0; i < (1<<n); i++){
+        res[i] = a[i] * b;
+    }
+    return res;
+}
+template<unsigned n>
+inline LinearN<n> operator/(LinearN<n> const & a, double b) {
+    LinearN<n> res;
+    for (unsigned i=0; i < (1<<n); i++){
+        res[i] = a[i] / b;
+    }
+    return res;
+}
+template<unsigned n>
+inline LinearN<n> operator*=(LinearN<n> & a, double b) {
+    for (unsigned i=0; i < (1<<n); i++){
+        a[i] *= b;
+    }
+    return a;
+}
+template<unsigned n>
+inline LinearN<n> operator/=(LinearN<n> & a, double b) {
+    for (unsigned i=0; i < (1<<n); i++){
+        a[i] /= b;
+    }
+    return a;
+}
+
+template<unsigned n>
+void setToVariable(LinearN<n> &x, unsigned k){;
+    x = LinearN<n>(0.);
+    unsigned mask = 1<<k;
+    for (unsigned i=0; i < (1<<n); i++){
+        if ( i & mask ) x[i] = 1;
+    }
+}
+
+template<unsigned n>
+inline std::ostream &operator<< (std::ostream &out_file, const LinearN<n> &bo) {
+    out_file << "{";
+    for (unsigned i=0; i < (1<<n); i++){
+        out_file << bo[i]<<(i == (1<<n)-1 ? "}" : ",");
+    }
+    return out_file;
+}
+
+
+}
+#endif //SEEN_LINEAR_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/orphan-code/redblacktree.h b/include/2geom/orphan-code/redblacktree.h
new file mode 100644
index 0000000..9d79342
--- /dev/null
+++ b/include/2geom/orphan-code/redblacktree.h
@@ -0,0 +1,121 @@
+/**
+ * \file
+ * \brief 
+ * Implementation of Red-Black Tree as described in 
+ * Intorduction to Algorithms. Cormen et al. Mc Grow Hill. 1990. pp 263-280
+ * 
+ * The intention is to implement interval trees mentioned in the same book, after the red-black.
+ * Interval are heavily based on red-black trees (most operations are the same). So, we begin first 
+ * with implementing red-black!
+ *
+ * Authors:
+ *      ? <?@?.?>
+ * 
+ * Copyright 2009-2009 Evangelos Katsikaros
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef SEEN_LIB2GEOM_REDBLACKTREE_H
+#define SEEN_LIB2GEOM_REDBLACKTREE_H
+
+#include <vector>
+//#include <cassert>
+#include <limits>
+#include <cfloat>
+
+#include <2geom/d2.h>
+#include <2geom/interval.h>
+
+namespace Geom{
+
+class RedBlack{    
+public:
+    Interval interval;    // Key of the redblack tree will be the min of the interval
+    RedBlack *left, *right, *parent;
+    bool isRed;
+    Coord subtree_max;    // subtree_max = max( x->left->subtree_max, x->right->subtree_max, x->high )
+    int data;
+
+    RedBlack(): left(0), right(0), parent(0), isRed(false), subtree_max(0.0), data(0) {
+        Interval interval(0.0, 0.0);
+    }
+/*
+    RedBlack(Coord min, Coord max): left(0), right(0), parent(0), isRed(false), subtree_max(0.0), data(0) {
+        Interval interval( min, max );
+    }
+*/
+    inline Coord key(){ return interval.min(); };
+    inline Coord high(){ return interval.max(); };
+};
+
+
+class RedBlackTree{
+public:
+    RedBlack* root;
+
+    RedBlackTree(): root(0) {}
+
+    void insert(Rect const &r, int shape, int dimension);
+    void insert(Coord dimension_min, Coord dimension_max, int shape);
+
+    void erase(Rect const &r);
+    void erase(int shape);
+
+    RedBlack* search(Rect const &r, int dimension);
+    RedBlack* search(Interval i);
+    RedBlack* search(Coord a, Coord b);
+
+    void print_tree();
+private:
+    void inorder_tree_walk(RedBlack* x);
+    RedBlack* tree_minimum(RedBlack* x);
+    RedBlack* tree_successor(RedBlack* x);
+
+    void left_rotate(RedBlack* x);
+    void right_rotate(RedBlack* x);
+    void tree_insert(RedBlack* x);
+
+    void update_max(RedBlack* x);
+
+    RedBlack* erase(RedBlack* x); // TODO why rerutn pointer? to collect garbage ???
+    void erase_fixup(RedBlack* x);
+
+};
+
+} // end namespace Geom
+
+#endif // !SEEN_LIB2GEOM_REDBLACKTREE_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/orphan-code/rtree.h b/include/2geom/orphan-code/rtree.h
new file mode 100644
index 0000000..3ffae8e
--- /dev/null
+++ b/include/2geom/orphan-code/rtree.h
@@ -0,0 +1,241 @@
+/**
+ * \file
+ * \brief 
+ * Implementation of Red-Black Tree as described in 
+ * Intorduction to Algorithms. Cormen et al. Mc Grow Hill. 1990. pp 263-280
+ * 
+ * The intention is to implement interval trees mentioned in the same book, after the red-black.
+ * Interval are heavily based on red-black trees (most operations are the same). So, we begin first 
+ * with implementing red-black!
+ *
+ * Authors:
+ *      ? <?@?.?>
+ * 
+ * Copyright 2009-2009 Evangelos Katsikaros
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef SEEN_LIB2GEOM_RTREE_H
+#define SEEN_LIB2GEOM_RTREE_H
+
+#include <vector>
+#include <utility>
+
+
+#include <2geom/d2.h>
+#include <2geom/interval.h>
+
+namespace Geom{
+
+// used only in pick_next( )
+enum enum_add_to_group { 
+    ADD_TO_GROUP_A = 0, 
+    ADD_TO_GROUP_B
+};
+
+
+enum enum_split_strategy { 
+    QUADRATIC_SPIT = 0,
+    LINEAR_COST,
+    TOTAL_STRATEGIES // this one must be the last item
+};
+
+
+
+template <typename T>
+class pedantic_vector:public std::vector<T> {
+public:
+    pedantic_vector(size_t s=0) : std::vector<T>(s) {}
+    T& operator[](unsigned i) {
+        //assert(i >= 0);
+        assert(i < std::vector<T>::size());
+        return std::vector<T>::operator[](i);
+    }
+    T const& operator[](unsigned i) const {
+        //assert(i >= 0);
+        assert(i < std::vector<T>::size());
+        return std::vector<T>::operator[](i);
+    }
+/*
+    erase( std::vector<T>::iterator it ) {
+        //assert(i >= 0);
+        assert( it < std::vector<T>::size());
+        return std::vector<T>::erase(it);
+    }
+*/
+};
+
+class RTreeNode;
+
+class RTreeRecord_Leaf{
+public:
+    Rect bounding_box;
+    int data;
+
+    RTreeRecord_Leaf(): bounding_box(), data(0)
+    {}
+
+    RTreeRecord_Leaf(Rect bb, int d): bounding_box(bb), data(d)
+    {}
+};
+
+class RTreeRecord_NonLeaf{
+public:
+    Rect bounding_box;
+    RTreeNode* data;    
+
+    RTreeRecord_NonLeaf(): bounding_box(), data(0)
+    {}
+
+    RTreeRecord_NonLeaf(Rect bb, RTreeNode* d): bounding_box(bb), data(d)
+    {}
+};
+
+/*
+R-Tree has 2 kinds of nodes
+* Leaves which store:
+  - the actual data
+  - the bounding box of the data
+
+* Non-Leaves which store:
+  - a child node (data)
+  - the bounding box of the child node
+
+This causes some code duplication in rtree.cpp. There are 2 cases:
+- we care whether we touch a leaf/non-leaf node, since we write data in the node, so we want to 
+  write the correct thing (int or RTreeNode*)
+- we do NOT care whether we touch a leaf/non-leaf node, because we only read/write the bounding 
+  boxes which is the same in both cases.
+
+TODO:
+A better design would eliminate the duplication in the 2nd case, but we can't avoid the 1st probably.
+*/
+class RTreeNode{    
+public:
+    // first: bounding box
+    // second: "data" (leaf-node) or node (NON leaf-node)
+    //pedantic_vector< RTreeRecord_Leaf > children_leaves; // if this is empty, then node is leaf-node
+    //pedantic_vector< RTreeRecord_NonLeaf > children_nodes;  // if this is empty, then node is NON-leaf node
+
+    std::vector< RTreeRecord_Leaf > children_leaves; // if this is empty, then node is leaf-node
+    std::vector< RTreeRecord_NonLeaf > children_nodes;  // if this is empty, then node is NON-leaf node
+
+    RTreeNode(): children_leaves(0), children_nodes(0)
+    {}
+
+};
+
+
+class RTree{
+public:
+    RTreeNode* root;
+
+    // min/max records per node
+    unsigned min_records;
+    unsigned max_records; // allow +1 (used during insert)
+
+    enum_split_strategy split_strategy;
+
+
+    RTree( unsigned n, unsigned m, enum_split_strategy split_s ): 
+        root(0), min_records( n ), max_records( m ), split_strategy( split_s ),
+        tree_height(0)
+    {}
+
+    void insert( Rect const &r, unsigned shape);
+    void search( const Rect &search_area, std::vector< int >* result, const RTreeNode* subtree ) const;
+    //int erase( const RTreeRecord_Leaf & search );
+    int erase( const Rect &search_area, const int shape_to_delete );
+
+//  update
+
+    void print_tree(RTreeNode* subtree_root, int depth ) const;
+
+private:
+    unsigned tree_height; // 0 is the root level
+
+    void insert(  //Rect const &r, 
+                //int shape, 
+                const RTreeRecord_Leaf &leaf_record,
+                const bool &insert_high = false, 
+                const unsigned &stop_height = 0,
+                const RTreeRecord_NonLeaf &nonleaf_record = RTreeRecord_NonLeaf()
+                );
+    // I1
+    RTreeNode* choose_node( const Rect &r, const bool &insert_high = false, const unsigned &stop_height=0 ) const;
+    double find_waste_area( const Rect &a, const Rect &b ) const;
+    double find_enlargement( const Rect &a, const Rect &b ) const;
+
+    // I2
+    std::pair<RTreeNode*, RTreeNode*> split_node( RTreeNode *s );
+        // QUADRATIC_SPIT
+    std::pair<RTreeNode*, RTreeNode*> quadratic_split( RTreeNode* s );
+    std::pair<unsigned, unsigned> pick_seeds( RTreeNode* s ) const;
+    std::pair<unsigned, enum_add_to_group>  pick_next( RTreeNode* group_a, RTreeNode* group_b, RTreeNode* s, std::vector<bool> &assigned_v );
+        // others...
+
+    // I3
+    bool adjust_tree(   RTreeNode* position, 
+                        std::pair<RTreeNode*, RTreeNode*>  &node_division, 
+                        bool split_performed
+                        );
+    std::pair< RTreeNode*, bool > find_parent( RTreeNode* subtree_root, Rect search_area, RTreeNode* wanted ) const;
+
+    void recalculate_bounding_box( RTreeNode* parent, RTreeNode* child, unsigned &child_in_parent );
+
+    void copy_group_a_to_existing_node( RTreeNode *position, RTreeNode* group_a );
+    RTreeRecord_NonLeaf create_nonleaf_record_from_rtreenode( Rect &new_entry_bounding, RTreeNode *rtreenode );
+    RTreeRecord_Leaf create_leaf_record_from_rtreenode( Rect &new_entry_bounding, RTreeNode *rtreenode );
+
+    // erase
+//    RTreeNode* find_leaf( RTreeNode* subtree, const RTreeRecord_Leaf &search ) const;
+    RTreeNode* find_leaf( RTreeNode* subtree, const Rect &search_area, const int shape_to_delete ) const;
+
+    bool condense_tree( RTreeNode* position
+    //                  std::pair<RTreeNode*, RTreeNode*>  &node_division, // modified: it holds the last split group
+    //                  bool initial_split_performed, 
+    //                    const unsigned min_nodes
+    //                    const unsigned max_nodes 
+                        );
+    int remove_record_from_parent( RTreeNode* parent, RTreeNode* child );
+    void sanity_check(RTreeNode* subtree_root, int depth, bool used_during_insert = false ) const;
+
+};
+
+} // end namespace Geom
+
+#endif // !SEEN_LIB2GEOM_RTREE_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/orphan-code/sbasis-of.h b/include/2geom/orphan-code/sbasis-of.h
new file mode 100644
index 0000000..e5b76d6
--- /dev/null
+++ b/include/2geom/orphan-code/sbasis-of.h
@@ -0,0 +1,638 @@
+/**
+ * \file
+ * \brief Defines S-power basis function class 
+ * with coefficient in arbitrary ring
+ *
+ *  Authors:
+ *   Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *   Michael Sloan <mgsloan@gmail.com>
+ *
+ * Copyright (C) 2006-2007 authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef SEEN_SBASIS_OF_H
+#define SEEN_SBASIS_OF_H
+#include <vector>
+#include <cassert>
+#include <iostream>
+
+#include <2geom/interval.h>
+#include <2geom/utils.h>
+#include <2geom/exception.h>
+
+#include <2geom/orphan-code/linear-of.h>
+
+namespace Geom {
+
+template<typename T>
+class SBasisOf;
+
+#ifdef USE_SBASIS_OF
+typedef Geom::SBasisOf<double> SBasis;
+#endif
+
+/*** An empty SBasisOf<T> is identically 0. */
+template<typename T>
+class SBasisOf : public std::vector<LinearOf<T> >{
+public:
+    SBasisOf() {}
+    explicit SBasisOf(T a) {
+        this->push_back(LinearOf<T>(a,a));
+    }
+    SBasisOf(SBasisOf<T> const & a) :
+        std::vector<LinearOf<T> >(a)
+    {}
+    SBasisOf(LinearOf<T> const & bo) {
+        this->push_back(bo);
+    }
+    SBasisOf(LinearOf<T>* bo) {
+        this->push_back(*bo);
+    }
+    //static unsigned input_dim(){return T::input_dim()+1;}
+
+    //IMPL: FragmentConcept
+    typedef T output_type;
+    inline bool isZero() const {
+        if(this->empty()) return true;
+        for(unsigned i = 0; i < this->size(); i++) {
+            if(!(*this)[i].isZero()) return false;
+        }
+        return true;
+    }
+    inline bool isConstant() const {
+        if (this->empty()) return true;
+        for (unsigned i = 0; i < this->size(); i++) {
+            if(!(*this)[i].isConstant()) return false;
+        }
+        return true;
+    }
+
+    //TODO: code this...
+    bool isFinite() const;
+
+    inline T at0() const { 
+        if(this->empty()) return T(0); else return (*this)[0][0];
+    }
+    inline T at1() const{
+        if(this->empty()) return T(0); else return (*this)[0][1];
+    }
+
+    T valueAt(double t) const {
+        double s = t*(1-t);
+        T p0 = T(0.), p1 = T(0.);
+        for(unsigned k = this->size(); k > 0; k--) {
+            const LinearOf<T> &lin = (*this)[k-1];
+            p0 = p0*s + lin[0];
+            p1 = p1*s + lin[1];
+        }
+        return p0*(1-t) + p1*t;
+    }
+
+    T operator()(double t) const {
+        return valueAt(t);
+    }
+
+    /**
+    *  The size of the returned vector equals n+1.
+    */
+    std::vector<T> valueAndDerivatives(double t, unsigned n) const{
+        std::vector<T> ret(n+1);
+        ret[0] = valueAt(t);
+        SBasisOf<T> tmp = *this;
+        for(unsigned i = 0; i < n; i++) {
+            tmp.derive();
+            ret[i+1] = tmp.valueAt(t);
+        }
+        return ret;
+    }
+
+    //The following lines only makes sens if T=double!
+    SBasisOf<T> toSBasis() const { return SBasisOf<T>(*this); }
+    double tailError(unsigned tail) const{
+        Interval bs = *bounds_fast(*this, tail);
+        return std::max(fabs(bs.min()),fabs(bs.max()));
+    }
+
+// compute f(g)
+    SBasisOf<T> operator()(SBasisOf<T> const & g) const;
+
+    LinearOf<T> operator[](unsigned i) const {
+        assert(i < this->size());
+        return std::vector<LinearOf<T> >::operator[](i);
+    }
+
+//MUTATOR PRISON
+    LinearOf<T>& operator[](unsigned i) { return this->at(i); }
+
+    //remove extra zeros
+    void normalize() {
+        while(!this->empty() && this->back().isZero())
+            this->pop_back();
+    }
+
+    void truncate(unsigned k) { if(k < this->size()) this->resize(k); }
+private:
+    void derive(); // in place version
+    unsigned dim;
+};
+
+//template<>
+//inline unsigned SBasisOf<double>::input_dim() { return 1; }
+
+//--------------------------------------------------------------------------
+#ifdef USE_SBASIS_OF
+
+//implemented in sbasis-roots.cpp
+OptInterval bounds_exact(SBasis const &a);
+OptInterval bounds_fast(SBasis const &a, int order = 0);
+OptInterval bounds_local(SBasis const &a, const OptInterval &t, int order = 0);
+
+std::vector<double> roots(SBasis const & s);
+std::vector<std::vector<double> > multi_roots(SBasis const &f,
+                                 std::vector<double> const &levels,
+                                 double htol=1e-7,
+                                 double vtol=1e-7,
+                                 double a=0,
+                                 double b=1);
+#endif
+//--------------------------------------------------------------------------
+
+
+//TODO: figure out how to stick this in linear, while not adding an sbasis dep
+template<typename T>
+inline SBasisOf<T> LinearOf<T>::toSBasis() const { return SBasisOf<T>(*this); }
+
+template<typename T>
+inline SBasisOf<T> reverse(SBasisOf<T> const &a) {
+    SBasisOf<T> result;
+    result.reserve(a.size());
+    for(unsigned k = 0; k < a.size(); k++)
+       result.push_back(reverse(a[k]));
+    return result;
+}
+
+//IMPL: ScalableConcept
+template<typename T>
+inline SBasisOf<T> operator-(const SBasisOf<T>& p) {
+    if(p.isZero()) return SBasisOf<T>();
+    SBasisOf<T> result;
+    result.reserve(p.size());
+        
+    for(unsigned i = 0; i < p.size(); i++) {
+        result.push_back(-p[i]);
+    }
+    return result;
+}
+
+template<typename T>
+SBasisOf<T> operator*(SBasisOf<T> const &a, double k){
+    SBasisOf<T> c;
+    //TODO: what does this mean for vectors of vectors??
+    //c.reserve(a.size());
+    for(unsigned i = 0; i < a.size(); i++)
+        c.push_back(a[i] * k);
+    return c;
+}
+
+template<typename T>
+inline SBasisOf<T> operator*(double k, SBasisOf<T> const &a) { return a*k; }
+template<typename T>
+inline SBasisOf<T> operator/(SBasisOf<T> const &a, double k) { return a*(1./k); }
+template<typename T>
+SBasisOf<T>& operator*=(SBasisOf<T>& a, double b){
+    if (a.isZero()) return a;
+    if (b == 0)
+        a.clear();
+    else
+        for(unsigned i = 0; i < a.size(); i++)
+            a[i] *= b;
+    return a;
+}
+
+template<typename T>
+inline SBasisOf<T>& operator/=(SBasisOf<T>& a, double b) { return (a*=(1./b)); }
+
+/*
+//We can also multiply by element of ring coeff T: 
+template<typename T>
+SBasisOf<T> operator*(SBasisOf<T> const &a, T k){
+    SBasisOf<T> c;
+    //TODO: what does this mean for vectors of vectors??
+    //c.reserve(a.size());
+    for(unsigned i = 0; i < a.size(); i++)
+        c.push_back(a[i] * k);
+    return c;
+}
+
+template<typename T>
+inline SBasisOf<T> operator*(T k, SBasisOf<T> const &a) { return a*k; }
+template<typename T>
+SBasisOf<T>& operator*=(SBasisOf<T>& a, T b){
+    if (a.isZero()) return a;
+    if (b == 0)
+        a.clear();
+    else
+        for(unsigned i = 0; i < a.size(); i++)
+            a[i] *= b;
+    return a;
+}
+*/
+
+//IMPL: AddableConcept
+template<typename T>
+inline SBasisOf<T> operator+(const SBasisOf<T>& a, const SBasisOf<T>& b){
+    SBasisOf<T> result;
+    const unsigned out_size = std::max(a.size(), b.size());
+    const unsigned min_size = std::min(a.size(), b.size());
+    //TODO: what does this mean for vector<vector>;
+    //result.reserve(out_size);
+
+    for(unsigned i = 0; i < min_size; i++) {
+        result.push_back(a[i] + b[i]);
+    }
+    for(unsigned i = min_size; i < a.size(); i++)
+        result.push_back(a[i]);
+    for(unsigned i = min_size; i < b.size(); i++)
+        result.push_back(b[i]);
+
+    assert(result.size() == out_size);
+    return result;
+}
+
+template<typename T>
+SBasisOf<T> operator-(const SBasisOf<T>& a, const SBasisOf<T>& b){
+    SBasisOf<T> result;
+    const unsigned out_size = std::max(a.size(), b.size());
+    const unsigned min_size = std::min(a.size(), b.size());
+    //TODO: what does this mean for vector<vector>;
+    //result.reserve(out_size);
+
+    for(unsigned i = 0; i < min_size; i++) {
+        result.push_back(a[i] - b[i]);
+    }
+    for(unsigned i = min_size; i < a.size(); i++)
+        result.push_back(a[i]);
+    for(unsigned i = min_size; i < b.size(); i++)
+        result.push_back(-b[i]);
+
+    assert(result.size() == out_size);
+    return result;
+}
+
+template<typename T>
+SBasisOf<T>& operator+=(SBasisOf<T>& a, const SBasisOf<T>& b){
+    const unsigned out_size = std::max(a.size(), b.size());
+    const unsigned min_size = std::min(a.size(), b.size());
+    //TODO: what does this mean for vectors of vectors
+    //a.reserve(out_size);
+    for(unsigned i = 0; i < min_size; i++)
+        a[i] += b[i];
+    for(unsigned i = min_size; i < b.size(); i++)
+        a.push_back(b[i]);
+
+    assert(a.size() == out_size);
+    return a;
+}
+
+template<typename T>
+SBasisOf<T>& operator-=(SBasisOf<T>& a, const SBasisOf<T>& b){
+    const unsigned out_size = std::max(a.size(), b.size());
+    const unsigned min_size = std::min(a.size(), b.size());
+    //TODO: what does this mean for vectors of vectors
+    //a.reserve(out_size);
+    for(unsigned i = 0; i < min_size; i++)
+        a[i] -= b[i];
+    for(unsigned i = min_size; i < b.size(); i++)
+        a.push_back(-b[i]);
+
+    assert(a.size() == out_size);
+    return a;
+}
+
+//TODO: remove?
+template<typename T>
+inline SBasisOf<T> operator+(const SBasisOf<T> & a, LinearOf<T> const & b) {
+    if(b.isZero()) return a;
+    if(a.isZero()) return b;
+    SBasisOf<T> result(a);
+    result[0] += b;
+    return result;
+}
+template<typename T>
+inline SBasisOf<T> operator-(const SBasisOf<T> & a, LinearOf<T> const & b) {
+    if(b.isZero()) return a;
+    SBasisOf<T> result(a);
+    result[0] -= b;
+    return result;
+}
+template<typename T>
+inline SBasisOf<T>& operator+=(SBasisOf<T>& a, const LinearOf<T>& b) {
+    if(a.isZero())
+        a.push_back(b);
+    else
+        a[0] += b;
+    return a;
+}
+template<typename T>
+inline SBasisOf<T>& operator-=(SBasisOf<T>& a, const LinearOf<T>& b) {
+    if(a.isZero())
+        a.push_back(-b);
+    else
+        a[0] -= b;
+    return a;
+}
+
+//IMPL: OffsetableConcept
+/*
+template<typename T>
+inline SBasisOf<T> operator+(const SBasisOf<T> & a, double b) {
+    if(a.isZero()) return LinearOf<T>(b, b);
+    SBasisOf<T> result(a);
+    result[0] += b;
+    return result;
+}
+template<typename T>
+inline SBasisOf<T> operator-(const SBasisOf<T> & a, double b) {
+    if(a.isZero()) return LinearOf<T>(-b, -b);
+    SBasisOf<T> result(a);
+    result[0] -= b;
+    return result;
+}
+template<typename T>
+inline SBasisOf<T>& operator+=(SBasisOf<T>& a, double b) {
+    if(a.isZero())
+        a.push_back(LinearOf<T>(b,b));
+    else
+        a[0] += b;
+    return a;
+}
+template<typename T>
+inline SBasisOf<T>& operator-=(SBasisOf<T>& a, double b) {
+    if(a.isZero())
+        a.push_back(LinearOf<T>(-b,-b));
+    else
+        a[0] -= b;
+    return a;
+}
+*/
+//We can also offset by elements of coeff ring T
+template<typename T>
+inline SBasisOf<T> operator+(const SBasisOf<T> & a, T b) {
+    if(a.isZero()) return LinearOf<T>(b, b);
+    SBasisOf<T> result(a);
+    result[0] += b;
+    return result;
+}
+template<typename T>
+inline SBasisOf<T> operator-(const SBasisOf<T> & a, T b) {
+    if(a.isZero()) return LinearOf<T>(-b, -b);
+    SBasisOf<T> result(a);
+    result[0] -= b;
+    return result;
+}
+template<typename T>
+inline SBasisOf<T>& operator+=(SBasisOf<T>& a, T b) {
+    if(a.isZero())
+        a.push_back(LinearOf<T>(b,b));
+    else
+        a[0] += b;
+    return a;
+}
+template<typename T>
+inline SBasisOf<T>& operator-=(SBasisOf<T>& a, T b) {
+    if(a.isZero())
+        a.push_back(LinearOf<T>(-b,-b));
+    else
+        a[0] -= b;
+    return a;
+}
+
+
+template<typename T>
+SBasisOf<T> shift(SBasisOf<T> const &a, int sh){
+    SBasisOf<T> c = a;
+    if(sh > 0) {
+        c.insert(c.begin(), sh, LinearOf<T>(0,0));
+    } else {
+        //TODO: truncate
+    }
+    return c;
+}
+
+template<typename T>
+SBasisOf<T> shift(LinearOf<T> const &a, int sh) {
+    SBasisOf<T> c;
+    if(sh > 0) {
+        c.insert(c.begin(), sh, LinearOf<T>(0,0));
+        c.push_back(a);
+    }
+    return c;
+}
+
+template<typename T>
+inline SBasisOf<T> truncate(SBasisOf<T> const &a, unsigned terms) {
+    SBasisOf<T> c;
+    c.insert(c.begin(), a.begin(), a.begin() + std::min(terms, (unsigned)a.size()));
+    return c;
+}
+
+template<typename T>
+SBasisOf<T> multiply_add(SBasisOf<T> const &a, SBasisOf<T> const &b, SBasisOf<T> c) {
+    if(a.isZero() || b.isZero())
+        return c;
+    c.resize(a.size() + b.size(), LinearOf<T>(T(0.),T(0.)));
+    for(unsigned j = 0; j < b.size(); j++) {
+        for(unsigned i = j; i < a.size()+j; i++) {
+            T tri = (b[j][1]-b[j][0])*(a[i-j][1]-a[i-j][0]);
+            c[i+1/*shift*/] += LinearOf<T>(-tri);
+        }
+    }
+    for(unsigned j = 0; j < b.size(); j++) {
+        for(unsigned i = j; i < a.size()+j; i++) {
+            for(unsigned dim = 0; dim < 2; dim++)
+                c[i][dim] += b[j][dim]*a[i-j][dim];
+        }
+    }
+    c.normalize();
+    //assert(!(0 == c.back()[0] && 0 == c.back()[1]));
+    return c;
+}
+
+template<typename T>
+SBasisOf<T> multiply(SBasisOf<T> const &a, SBasisOf<T> const &b) {
+    SBasisOf<T> c;
+    if(a.isZero() || b.isZero())
+        return c;
+    return multiply_add(a, b, c);
+}
+
+template<typename T>
+SBasisOf<T> integral(SBasisOf<T> const &c){
+    SBasisOf<T> a;
+    T aTri = T(0.);
+    for(int k = c.size()-1; k >= 0; k--) {
+        aTri = (HatOf<T>(c[k]).d + (k+1)*aTri/2)/(2*k+1);
+        a[k][0] -= aTri/2;
+        a[k][1] += aTri/2;
+    }
+    a.normalize();
+    return a;
+}
+
+template<typename T>
+SBasisOf<T> derivative(SBasisOf<T> const &a){
+    SBasisOf<T> c;
+    c.resize(a.size(), LinearOf<T>());
+    if(a.isZero())
+        return c;
+
+    for(unsigned k = 0; k < a.size()-1; k++) {
+        T d = (2*k+1)*(a[k][1] - a[k][0]);
+        
+        c[k][0] = d + (k+1)*a[k+1][0];
+        c[k][1] = d - (k+1)*a[k+1][1];
+    }
+    int k = a.size()-1;
+    T d = (2*k+1)*(a[k][1] - a[k][0]);
+    //TODO: do a real test to know if d==0!
+    if(d == T(0.0))
+        c.pop_back();
+    else {
+        c[k][0] = d;
+        c[k][1] = d;
+    }
+
+    return c;
+}
+
+template<typename T>
+void SBasisOf<T>::derive() { // in place version
+    if(isZero()) return;
+    for(unsigned k = 0; k < this->size()-1; k++) {
+        T d = (2*k+1)*((*this)[k][1] - (*this)[k][0]);
+        
+        (*this)[k][0] = d + (k+1)*(*this)[k+1][0];
+        (*this)[k][1] = d - (k+1)*(*this)[k+1][1];
+    }
+    int k = this->size()-1;
+    T d = (2*k+1)*((*this)[k][1] - (*this)[k][0]);
+    if(d == 0)//TODO: give this a meaning for general coeff ring.
+        this->pop_back();
+    else {
+        (*this)[k][0] = d;
+        (*this)[k][1] = d;
+    }
+}
+
+
+template<typename T>
+inline SBasisOf<T> operator*(SBasisOf<T> const & a, SBasisOf<T> const & b) {
+    return multiply(a, b);
+}
+
+template<typename T>
+inline SBasisOf<T>& operator*=(SBasisOf<T>& a, SBasisOf<T> const & b) {
+    a = multiply(a, b);
+    return a;
+}
+
+// a(b(t))
+//TODO: compose all compatibles types!
+template<typename T>
+SBasisOf<T> compose(SBasisOf<T> const &a, SBasisOf<T> const &b){
+    SBasisOf<double> s = multiply((SBasisOf<T>(LinearOf<T>(1,1))-b), b);
+    SBasisOf<T> r;
+
+    for(int i = a.size()-1; i >= 0; i--) {
+        r = multiply_add(r, s, SBasisOf<T>(LinearOf<T>(HatOf<T>(a[i][0]))) - b*a[i][0] + b*a[i][1]);
+    }
+    return r;
+}
+
+template<typename T>
+SBasisOf<T> compose(SBasisOf<T> const &a, SBasisOf<T> const &b, unsigned k){
+    SBasisOf<T> s = multiply((SBasisOf<T>(LinearOf<T>(1,1))-b), b);
+    SBasisOf<T> r;
+
+    for(int i = a.size()-1; i >= 0; i--) {
+        r = multiply_add(r, s, SBasisOf<T>(LinearOf<T>(HatOf<T>(a[i][0]))) - b*a[i][0] + b*a[i][1]);
+    }
+    r.truncate(k);
+    return r;
+}
+template<typename T>
+SBasisOf<T> compose(LinearOf<T> const &a, SBasisOf<T> const &b){
+    return compose(SBasisOf<T>(a),b);
+}
+template<typename T>
+SBasisOf<T> compose(SBasisOf<T> const &a, LinearOf<T> const &b){
+    return compose(a,SBasisOf<T>(b));
+}
+template<typename T>//TODO: don't be so lazy!!
+SBasisOf<T> compose(LinearOf<T> const &a, LinearOf<T> const &b){
+    return compose(SBasisOf<T>(a),SBasisOf<T>(b));
+}
+
+
+
+template<typename T>
+inline SBasisOf<T> portion(const SBasisOf<T> &t, double from, double to) { return compose(t, LinearOf<T>(from, to)); }
+
+// compute f(g)
+template<typename T>
+inline SBasisOf<T>
+SBasisOf<T>::operator()(SBasisOf<T> const & g) const {
+    return compose(*this, g);
+}
+ 
+template<typename T>
+inline std::ostream &operator<< (std::ostream &out_file, const LinearOf<T> &bo) {
+    out_file << "{" << bo[0] << ", " << bo[1] << "}";
+    return out_file;
+}
+
+template<typename T>
+inline std::ostream &operator<< (std::ostream &out_file, const SBasisOf<T> & p) {
+    for(unsigned i = 0; i < p.size(); i++) {
+        out_file << p[i] << "s^" << i << " + ";
+    }
+    return out_file;
+}
+
+};
+#endif
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/orphan-code/sbasisN.h b/include/2geom/orphan-code/sbasisN.h
new file mode 100644
index 0000000..0b5a48f
--- /dev/null
+++ b/include/2geom/orphan-code/sbasisN.h
@@ -0,0 +1,1123 @@
+/**
+ * \file
+ * \brief Multi-dimensional symmetric power basis function.
+ * A SBasisN<n> is a polynomial f of n variables (t0,...,tn-1), 
+ * written in a particular form. Let si = ti(1-t_i). f is written as
+ *
+ *  f = sum_p  s^p a_{p}(t0,...,t_{n-1})
+ *
+ * where p=(p0,...,p_{n-1}) is a multi index (called MultiDegree<n> in the code)
+ *  s^p = prod_i si^pi, and a_p is a LinearN<n>.
+ * Recall a LinearN<n> is sum over all choices xi = ti or (1-ti) of terms of form
+ *   a * x0*...*x_{n-1}
+ *
+ * Caution: degrees are expressed as degrees of s=t*(1-t). The real degree
+ * (with respect to t) of the polynomial is twice that + 0 or 1 depending 
+ * whether the relevant LinearN<n> coeff is constant or not.
+ *//*
+ *
+ *  Authors:
+ *   JF Barraud <jf.barraud@gmail.com>
+ *   Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *   Michael Sloan <mgsloan@gmail.com>
+ *
+ * Copyright (C) 2006-2007 authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef SEEN_SBASISN_H
+#define SEEN_SBASISN_H
+#include <vector>
+#include <cassert>
+#include <iostream>
+
+#include <2geom/orphan-code/linearN.h>
+#include <2geom/linear.h>//for conversion purpose
+#include <2geom/sbasis.h>//for conversion purpose
+#include <2geom/interval.h>
+#include <2geom/utils.h>
+#include <2geom/exception.h>
+
+
+namespace Geom{
+
+/** MultiDegree:
+ *  \brief multi-degree (p0,...,p^{n-1}) of a s0^p0...s_{n-1}p^{n-1} monomial.
+ *
+ *  a "Multi_deg" is a sequence p={p0,...,p_n-1} representing the monomial
+ *  s^p = s_0^{p_0}*...*s_{n-1}^{p_{n-1}}.
+ *  Caution: the degrees are expressed with respect to si! ( in SBasis code
+ *  below, si = ti*(1-ti) ).
+ */
+
+template<unsigned n>
+class MultiDegree{
+public:
+    unsigned p[n];
+    MultiDegree(){
+        for (unsigned i = 0; i <n; i++) {
+            p[i]=0;
+        }
+    }
+    MultiDegree( unsigned const other_p[] ){
+        p = other_p;
+    }
+    MultiDegree(unsigned const idx, unsigned const sizes[]){
+        unsigned q = idx;
+        for (unsigned i = n-1; i >0; i--) {
+            div_t d = std::div(int(q), int(sizes[i]));
+            p[i] = d.rem;
+            q = d.quot;
+        }
+        p[0] = q;
+    }
+    unsigned operator[](const unsigned i) const {
+        assert(i < n); return p[i];
+    }
+    unsigned& operator[](const unsigned i) {
+        assert(i < n); return p[i]; 
+    }
+
+    unsigned asIdx(unsigned const sizes[]) const{
+        unsigned ret = p[0];
+        bool in_range = (p[0]<sizes[0]);
+        for (unsigned i = 1; i < n; i++) {
+            in_range = in_range && (p[i]<sizes[i]);
+            ret = ret*sizes[i] + p[i];
+        }
+        if (in_range) return ret;
+        //TODO: find a better warning than returning out of range idx!
+        ret =1;
+        for (unsigned i = 0; i < n; i++) {
+            ret *= sizes[i];
+        }
+        return ret;
+    }
+    bool stepUp(unsigned const sizes[], unsigned frozen_mask = 0){
+        unsigned i = 0;
+        while ( i < n && ( (1<<i) & frozen_mask ) ) i++;
+        while ( i <n && p[i] == sizes[i]-1 ) {
+            i++;
+            while (i<n && ( (1<<i) & frozen_mask ) ) i++;
+        }
+        if (i<n){
+            p[i]+=1; 
+            for (unsigned j = 0; j <  i; j++) {
+                if ( !( (1<<j) & frozen_mask ) ) p[j] = 0;
+            }
+            return true; 
+        }else{
+            return false;
+        }
+    }
+    bool stepDown(unsigned const sizes[], unsigned frozen_mask = 0){
+        int i = n-1;
+        while (i>=0 && ( (1<<i) & frozen_mask ) ) i--;
+        while ( i >= 0 && p[i] == 0 ) {
+            i--;
+            while (i>=0 && ( (1<<i) & frozen_mask ) ) i--;
+        }
+        if ( i >= 0 ){
+            p[i]-=1; 
+            for (unsigned j = i+1; j <  n; j++) {
+                if ( !( (1<<j) & frozen_mask ) ) p[j] = sizes[j]-1;
+            }
+            return true; 
+        }else{
+            return false;
+        }
+    }
+};
+
+/**
+ * Returns the maximal degree appearing in the two arguments for each variables.
+ */
+template <unsigned n>
+MultiDegree<n> max(MultiDegree<n> const &p, MultiDegree<n> const &q){
+    MultiDegree<n> ret;
+    for (unsigned i = 0; i <n; i++) {
+        ret.p[i] = (p[i]>q[i] ? p[i] : q[i]);
+    }
+    return ret;
+}
+
+template <unsigned n>
+MultiDegree<n> operator + (MultiDegree<n> const &p, MultiDegree<n> const &q){
+    MultiDegree<n> ret;
+    for (unsigned i = 0; i <n; i++) {
+        ret.p[i] = p[i] + q[i];
+    }
+    return ret;
+}
+template <unsigned n>
+MultiDegree<n> operator += (MultiDegree<n> const &p, MultiDegree<n> const &q){
+    for (unsigned i = 0; i <n; i++) {
+        p[i] += q[i];
+    }
+    return p;
+}
+
+/**
+ *  \brief MultiDegree comparison.
+ * A MultiDegree \param p is smaller than another \param q
+ * if all it's smaller for all variables.
+ *
+ * In particular, p<=q and q<=p can both be false! 
+ */
+template<unsigned n>
+bool operator<=(MultiDegree<n> const &p, MultiDegree<n> const &q){
+    for (unsigned i = 0; i <n; i++) {
+        if (p[i]>q[i]) return false;
+    }
+    return true;
+}
+
+
+/**
+ *  \brief Polynomials of n variables, written in SBasis form.
+ * An SBasisN<n> f is internaly represented as a vector of LinearN<n>.
+ * It should be thought of as an n-dimensional vector: the coef of s0^p0...s_{n-1}p^{n-1}
+ * is soterd in f[p0,...,p_{n-1}]. The sizes of each dimension is stored in "sizes". 
+ * Note: an empty SBasis is identically 0.
+ */
+template<unsigned n>
+class SBasisN : public std::vector<LinearN<n> >{
+public:
+    unsigned sizes[n];
+    SBasisN() {
+        for (unsigned i = 0; i < n; i++) {
+            sizes[i] = 0;
+        }
+    }
+    explicit SBasisN(double a) {
+        for (unsigned i = 0; i < n; i++) {
+            sizes[i] = 1;
+        }
+        this->push_back(LinearN<n>(a));
+    }
+    SBasisN(SBasisN<n> const & a) : std::vector<LinearN<n> >(a){
+        //TODO: efficient array copy??
+        for (unsigned i = 0; i < n; i++) {
+            sizes[i] = a.sizes[i];
+        }
+    }
+    SBasisN(LinearN<n> const & bo) {
+        for (unsigned i = 0; i < n; i++) {
+            sizes[i] = 1;
+        }
+        this->push_back(bo);
+    }
+    SBasisN(LinearN<n>* bo) {
+        for (unsigned i = 0; i < n; i++) {
+            sizes[i] = 1;
+        }
+        this->push_back(*bo); 
+    }
+
+//----------------------------------------------
+//-- Degree/Sizing facilities ------------------
+//----------------------------------------------
+/**
+ * Internal recursive function used to compute partial degrees.
+ */
+    bool find_non_empty_level(unsigned var, MultiDegree<n> &fixed_degrees)const{
+        if (this->size()==0){
+            for (unsigned i = 0; i < n; i++) {
+                fixed_degrees[i] = 0;//FIXME this should be -infty!!
+            }
+            return false;
+        }
+        if ( !((*this)[fixed_degrees.asIdx(sizes)].isZero()) ) return true;
+
+        unsigned frozen = (1<<var);
+        if ( fixed_degrees.stepDown(sizes, frozen) ){
+            if ( find_non_empty_level(var, fixed_degrees) ) return true;
+        }
+        if ( fixed_degrees[var] > 0 ){
+            fixed_degrees[var] -= 1;
+            for (unsigned i = 0; i < n; i++) {
+                if (i!=var) fixed_degrees[i] = sizes[i]-1;
+            }
+            if (find_non_empty_level(var, fixed_degrees)) return true;
+        }
+        return false;//FIXME: this should return -infty in all variables!
+    }
+
+/**
+ * Returns the degree of an SBasisN<n> with respect to a given variable form its sizes.
+ * All terms are taken into account, even eventual trailing zeros.
+ * Note: degree is expressed with respect to s = t*(1-t), not t itself.
+ */
+    unsigned quick_degree(unsigned var) const{
+        return ( sizes[var] > 0 ? sizes[var]-1 : 0 );//this should be -infty.
+     }
+/**
+ * Computes the multi degree of the SBasis from it's sizes.
+ * All terms are taken into account, even eventual trailing zeros.
+ * Note: degrees are expressed with respect to s = t*(1-t), not t itself.
+ */
+    MultiDegree<n> quick_multi_degree() const{
+        MultiDegree<n> ret;
+        if (this->size()==0) return ret;//should be -infty for all vars.
+        for (unsigned i = 0; i < n; i++) {
+            assert( sizes[i]>0 );
+            ret.p[i] = sizes[i]-1;
+       }
+        return ret;
+    }
+/**
+ * Returns the degree of an SBasisN<n> with respect to a given variable.
+ * Trailing zeros are not taken into account.
+ * Note: degree is expressed with respect to s = t*(1-t), not t itself.
+ */
+    unsigned degree(unsigned var)const{
+        MultiDegree<n> degrees;
+        for(unsigned i = 0; i < n; i++) {
+            degrees[i] = sizes[i]-1;
+        }
+        if ( find_non_empty_level(var, degrees) ) return degrees[var];
+        else return 0;//should be -infty.
+     }
+/**
+ * Returns the *real* degree of an SBasisN<n> with respect to a given variable.
+ * Trailing zeros are not taken into account.
+ * Note: degree is expressed with respect to t itself, not s = t*(1-t).
+ * In particular: real_t_degree() = 2*degree() + 0 or 1.
+ */
+    unsigned real_t_degree(unsigned var)const{
+        unsigned deg = 0;
+        bool even = true;
+        bool notfound = true;
+        unsigned frozen = (1<<var);
+        MultiDegree<n> degrees;
+        for(unsigned i = 0; i < n; i++) {
+            degrees[i] = sizes[i]-1;
+        }
+        while( notfound ){
+            if ( find_non_empty_level(var, degrees) && degrees[var]>= deg ){
+                deg = degrees[var];
+                even = (*this)[degrees.asIdx(sizes)].isConstant(var);
+            }
+            notfound =  even && degrees.stepDown(sizes, frozen);
+        }
+        return 2*deg + ( even ? 0 : 1 );
+     }
+/**
+ * Returns the *real* degrees of an SBasisN<n>.
+ * Trailing zeros are not taken into account.
+ * Note: degree is expressed with respect to t itself, not s = t*(1-t).
+ * In particular: real_t_degree() = 2*degree() + 0 or 1.
+ */
+    MultiDegree<n> real_t_degrees()const{
+        MultiDegree<n>res;
+        for(unsigned i = 0; i < n; i++) {
+            res[i] = real_t_degree(i);
+        }
+        return res;
+     }
+/**
+ * Computes the multi degree of the SBasis.
+ * Trailing zeros are not taken into account.
+ * Note: degree is expressed with respect to s = t*(1-t), not t itself.
+ */
+    MultiDegree<n> multi_degree() const{
+        MultiDegree<n> ret;
+        if (this->size()==0) return ret;//should be -infty for all vars.
+        for (unsigned i = 0; i < n; i++) {
+            ret[i] = this->degree(i);
+        }
+        return ret;
+    }
+/**
+ * Returns the highest degree over all variables.
+ * Note: degree is expressed with respect to s = t*(1-t), not t itself.
+ */
+    unsigned max_degree() const {
+        if (this->size()==0) return 0;//should be -infty!
+        unsigned d=0;
+        for (unsigned i = 0; i < n; i++) {
+            assert( sizes[i]>0 );
+            if (d < sizes[i]-1) d = sizes[i]-1;
+        }
+        return d;
+    }
+
+/**
+ * Resize an SBasisN<n> to match new sizes.
+ *
+ * Caution: if a new size is smaller, the corresponding coefficients are discarded.
+ */
+    void multi_resize(unsigned new_sizes[], LinearN<n> def_value = LinearN<n>(0.)){
+        SBasisN<n> result;
+        bool nothing_todo = true;
+        unsigned tot_size = 1;
+        for(unsigned i = 0; i < n; i++) {
+            nothing_todo = nothing_todo && (sizes[i] == new_sizes[i]);
+            result.sizes[i] = new_sizes[i];
+            tot_size *= new_sizes[i];
+        }
+        if (nothing_todo) return;
+        result.resize(tot_size, def_value);
+        for(unsigned i = 0; i < tot_size; i++) {
+            MultiDegree<n> d( i, result.sizes );
+            unsigned j = d.asIdx(sizes);
+            if ( j < this->size() ){
+                result[i] = (*this)[j];
+            }
+        }
+        *this = result;
+    }
+
+    //remove extra zeros
+    void normalize() {
+        MultiDegree<n> max_p = multi_degree();
+        unsigned new_sizes[n];
+        for (unsigned i=0; i<n; i++){
+            new_sizes[i] = max_p[i]+1;
+        }
+        multi_resize(new_sizes);
+    }
+
+//-----------------------------
+//-- Misc. --------------------
+//-----------------------------
+
+/**
+ * Returns the number of variables this function takes as input: n.
+ */
+    unsigned input_dim(){return n;};
+
+    //IMPL: FragmentConcept
+    typedef double output_type;
+
+    inline bool isZero() const {
+        if(this->size()==0) return true;
+        for(unsigned i = 0; i < this->size(); i++) {
+            if(!(*this)[i].isZero()) return false;
+        }
+        return true;
+    }
+    inline bool isConstant() const {
+        if (this->size()==0) return true;
+        if(!(*this)[0].isConstant()) return false;
+        for (unsigned i = 1; i < this->size(); i++) {
+            if(!(*this)[i].isZero()) return false;
+        }
+        return true;
+    }
+
+    bool isFinite() const{
+        for (unsigned i = 0; i < this->size(); i++) {
+            if(!(*this)[i].isFinite()) return false;
+        }
+        return true;
+    }
+
+
+//------------------------------------------
+//-- Evaluation methods --------------------
+//------------------------------------------
+/**
+ * Returns the value of the SBasis at a given corner of [0,1]^n.
+ * \param k describes the corner: if i-th bit is 0, ti=0, otherwise ti=1. 
+ */
+    inline double atCorner(unsigned k) const { 
+        if(this->size()==0) return 0.;
+        return (*this)[0].atCorner(k);
+    }
+/**
+ * Returns the value of the SBasis at a given corner of [0,1]^n.
+ * \param t[n] describes the corner: the values should be 0's and 1's. 
+ */
+    inline double atCorner(double t[]) const { 
+        if(this->size()==0) return 0.;
+        return (*this)[0].atCorner(t);
+    }
+/**
+ * Returns a "slice" of the array.
+ * Returns an SBasis containing all the coeff of (s-)degree \param deg in variable \param var
+ */
+    //TODO: move by bigger blocks (?) but they are broken into pieces in general...
+    SBasisN<n> slice(unsigned const var, unsigned const deg) const{
+        if (deg >= sizes[var] ) return SBasisN<n>();
+        SBasisN<n> res;
+        unsigned tot_size = 1;
+        for (unsigned i = 0; i < n; i++) {
+            res.sizes[i] = (i==var ? 1 : sizes[i]);
+            tot_size *= res.sizes[i];
+        }
+        res.resize( tot_size, LinearN<n>(0.));
+        for (unsigned i = 0; i < tot_size; i++) {
+            MultiDegree<n> d(i,res.sizes);
+            d.p[var] = deg;
+            res[i] = (*this)[d.asIdx(sizes)];
+        }
+        return res;
+    }
+/**
+ * Returns a the SBasisN<n-1> obtained by setting variable \param var to 0.
+ */
+    inline SBasisN<n-1> at0(unsigned var=0, unsigned deg=0) const {
+        SBasisN<n> sl = slice(var,deg);
+        SBasisN<n-1> res;
+        res.reserve(sl.size());
+        for (unsigned i = 0; i < n-1; i++) {
+            res.sizes[i] = sizes[ ( i<var ? i : i+1 ) ];
+        }
+        for (unsigned i = 0; i < sl.size(); i++) {
+            res.push_back( sl[i].at0(var) );
+        }
+        return res;
+    }
+/**
+ * Returns a the SBasisN<n-1> obtained by setting variable \param var to 1.
+ */
+    inline SBasisN<n-1> at1(unsigned var=0, unsigned deg=0) const {
+        SBasisN<n> sl = slice(var,deg);
+        SBasisN<n-1> res;
+        res.reserve(sl.size());
+        for (unsigned i = 0; i < n-1; i++) {
+            res.sizes[i] = sizes[ ( i<var ? i : i+1 ) ];
+        }
+        for (unsigned i = 0; i < sl.size(); i++) {
+            res.push_back( sl[i].at1(var) );
+        }
+        return res;
+    }
+/**
+ * Returns a the SBasisN<n-1> obtained by setting variable \param var to \param t.
+ */
+    inline SBasisN<n-1> partialEval(double t, unsigned var=0 ) const {
+        SBasisN<n> sl;
+        double s = t*(1-t);
+        double si = 1;
+        for (unsigned i = 0; i <sizes[var]; i++) {
+            sl = sl + slice(var, i)*si;
+            si *= s;
+        }
+        SBasisN<n-1> res;
+        res.resize(sl.size(), LinearN<n-1>(0.));
+        for (unsigned i = 0; i < n-1; i++) {
+            res.sizes[i] = sizes[ ( i<var ? i : i+1 ) ];
+        }
+        for (unsigned i = 0; i < sl.size(); i++) {
+            res[i] = sl[i].partialEval(t,var);
+        }
+        return res;
+    }
+
+/**
+ * \brief Internal recursive function.
+ * Replace each variable by it's value in the 's=t*(1-t)' factor 
+ * but not in the LinearN<n> coeffs. Then sum up all coefficients.
+ * \param t[n]: values of the variables.
+ */
+    LinearN<n> sumCoefs( double t[], unsigned const k, unsigned const idx) const{
+        LinearN<n> a;
+        if (k == n){
+            a = (*this)[idx];
+            return (*this)[idx];
+        }
+        double s = t[k]*(1-t[k]);
+        double si=1;
+        for (unsigned i=0; i<sizes[k]; i++){
+            a += sumCoefs(t,k+1,idx*sizes[k]+i)*si;;
+            si *= s;
+        }
+        return a;
+    }
+/**
+ * Evaluate at given n-dimensional point.
+ * \param t[n]: values of the variables.
+ */
+    double valueAt(double t[]) const {
+        LinearN<n> a = sumCoefs(t,0,0);
+        return a.valueAt(t);
+    }
+
+    double operator()(double t[]) const {
+        return valueAt(t);
+    }
+
+    //double valueAndDerivative(double t, double &der) const;
+    //std::vector<double> valueAndDerivatives(double t, unsigned n) const;
+    //SBasisN toSBasisN() const { return SBasisN(*this); }
+    //double tailError(unsigned tail) const;
+
+
+//--------------------------------------------------
+//-- Coeff. manipulation ---------------------------
+//--------------------------------------------------
+
+/**
+ * Accessing the SBasisN<n> coefficients.
+ */
+    LinearN<n> operator[](unsigned i) const {
+        assert(i < this->size());
+        return std::vector<LinearN<n> >::operator[](i);
+    }
+    LinearN<n> operator[](MultiDegree<n> const &p) const {
+        unsigned i = p.asIdx(sizes);
+        assert(i < this->size());        
+        return std::vector<LinearN<n> >::operator[](i);
+    }
+
+//MUTATOR PRISON
+    LinearN<n>& operator[](unsigned i) { return this->at(i); }
+//    LinearN<n>& operator[](MultiDegree const &p) { 
+//        unsigned i = p.asIdx(sizes);
+//        return this->at(i); 
+//    }
+
+    void appendCoef(const SBasisN<n-1> &a, const SBasisN<n-1> &b, unsigned var=0){
+        unsigned new_sizes[n];
+        MultiDegree<n-1> deg_a = a.multi_degree(), deg_b = b.multi_degree();
+        MultiDegree<n-1> dcoef = max( deg_a, deg_b );
+        for (unsigned i=0; i<n; i++){
+            if ( i == var ){
+                new_sizes[var] = sizes[var] + 1;
+            }else{
+                unsigned coef_size = dcoef[(i<var?i:i-1)] + 1;
+                new_sizes[i] = ( sizes[i]>coef_size ? sizes[i] : coef_size );
+            }
+        }
+        multi_resize(new_sizes);
+        
+        MultiDegree<n> d;
+        d[var] = sizes[var]-1;
+        unsigned frozen_mask = (1<<var);
+        do{
+            for (unsigned i=0; i<n-1; i++){
+                dcoef.p[i] = d.p[ ( i<var ? i : i+1) ];
+            }
+            LinearN<n-1> a_d,b_d;
+            unsigned ia = dcoef.asIdx(a.sizes);
+            if ( ia < a.size() ) a_d = a[ia];
+            unsigned ib = dcoef.asIdx(b.sizes);
+            if ( ib < b.size() ) b_d = b[ib];
+            (*this)[d.asIdx(sizes)] = LinearN<n>(a_d,b_d);
+        }while (d.stepUp(sizes,frozen_mask));
+    }
+
+//private:
+    //void derive(); // in place version
+};
+
+//SBasisN<0> is a double. Specialize it out.
+template<>
+class SBasisN<0>{
+public:
+    double d;
+    SBasisN () {}
+    SBasisN(double d) :d(d) {}
+    operator double() const { return d; }
+};
+
+
+//SBasisN<1> are usual SBasis. Allow conversion.
+SBasis toSBasis(SBasisN<1> f){
+    SBasis res(f.size(), Linear());
+    for (unsigned i = 0; i < f.size(); i++) {
+        res[i] = toLinear(f[i]);
+    }
+    return res;
+}
+
+//TODO: figure out how to stick this in linear, while not adding an sbasis dep
+template<unsigned n>
+inline SBasisN<n> LinearN<n>::toSBasisN() const { return SBasisN<n>(*this); }
+
+
+
+
+//implemented in sbasis-roots.cpp
+//OptInterval bounds_exact(SBasisN const &a);
+//OptInterval bounds_fast(SBasisN const &a, int order = 0);
+//OptInterval bounds_local(SBasisN const &a, const OptInterval &t, int order = 0);
+
+/** Returns a function which reverses the domain of a.
+ \param a sbasis function
+
+useful for reversing a parameteric curve.
+*/
+//template<unsigned n>
+//inline SBasisN<n> reverse(SBasisN<n> const &a);
+
+//IMPL: ScalableConcept
+template<unsigned n>
+inline SBasisN<n> operator-(const SBasisN<n>& p) {
+    if(p.isZero()) return SBasisN<n>();
+    SBasisN<n> result;
+    for(unsigned i = 0; i < n; i++) {
+        result.sizes[i] = p.sizes[i];
+    }
+    result.reserve(p.size());
+    for(unsigned i = 0; i < p.size(); i++) {
+        result.push_back(-p[i]);
+    }
+    return result;
+}
+template<unsigned n>
+SBasisN<n> operator*(SBasisN<n> const &a, double c){
+    if(a.isZero()) return SBasisN<n>();
+    SBasisN<n> result;
+    for(unsigned i = 0; i < n; i++) {
+        result.sizes[i] = a.sizes[i];
+    }
+    result.reserve(a.size());
+    for(unsigned i = 0; i < a.size(); i++) {
+        result.push_back(a[i] * c);
+    }
+    return result;
+}
+template<unsigned n>
+inline SBasisN<n> operator*(double k, SBasisN<n> const &a) { return a*k; }
+template<unsigned n>
+inline SBasisN<n> operator/(SBasisN<n> const &a, double k) { return a*(1./k); }
+template<unsigned n>
+SBasisN<n>& operator*=(SBasisN<n>& a, double c){
+    for(unsigned i = 0; i < a.size(); i++) a[i] *= c;
+    return a;
+}
+template<unsigned n>
+inline SBasisN<n>& operator/=(SBasisN<n>& a, double b) { return (a*=(1./b)); }
+
+//IMPL: AddableConcept
+template<unsigned n>
+SBasisN<n> operator + (const SBasisN<n>& a, const SBasisN<n>& b){
+    if( a.isZero() ) return b;
+    if( b.isZero() ) return a;
+    SBasisN<n> result;
+    MultiDegree<n> deg = max(a.quick_multi_degree(),b.quick_multi_degree());
+    unsigned max_size = 1;
+    for(unsigned i = 0; i < n; i++) {
+        result.sizes[i] = deg[i]+1;
+        max_size *= result.sizes[i];
+    }
+    result.resize( max_size, LinearN<n>(0.) );
+    for(unsigned i = 0; i < result.size(); i++) {
+        MultiDegree<n> p(i,result.sizes);
+        unsigned ia = p.asIdx(a.sizes);
+        unsigned ib = p.asIdx(b.sizes);
+        if (ia<a.size()) {
+            result[i] += a[ia];
+        }
+        if (ib<b.size()) {
+            result[i] += b[ib];
+        }
+    }
+    return result;
+}
+template<unsigned n>
+SBasisN<n> operator-(const SBasisN<n>& a, const SBasisN<n>& b){return a+(-b);}
+template<unsigned n>
+SBasisN<n>& operator+=(SBasisN<n>& a, const SBasisN<n>& b){
+    if(b.isZero()) return a;
+    a = a + b;
+    return a;
+}
+template<unsigned n>
+SBasisN<n>& operator-=(SBasisN<n>& a, const SBasisN<n>& b){
+    a += -b;
+    return a;
+}
+
+//TODO: remove?
+template<unsigned n>
+inline SBasisN<n> operator+(const SBasisN<n> & a, LinearN<n> const & b) {
+    if(b.isZero()) return a;
+    if(a.isZero()) return b;
+    SBasisN<n> result(a);
+    result[0] += b;
+    return result;
+}
+template<unsigned n>
+
+inline SBasisN<n> operator-(const SBasisN<n> & a, LinearN<n> const & b) {
+    if(b.isZero()) return a;
+    if(a.isZero()) return -b;
+    SBasisN<n> result(a);
+    result[0] -= b;
+    return result;
+}
+template<unsigned n>
+inline SBasisN<n>& operator+=(SBasisN<n>& a, const LinearN<n>& b) {
+    if(a.size()==0)
+        a.push_back(b);
+    else
+        a[0] += b;
+    return a;
+}
+template<unsigned n>
+inline SBasisN<n>& operator-=(SBasisN<n>& a, const LinearN<n>& b) {
+    if(a.size()==0)
+        a.push_back(-b);
+    else
+        a[0] -= b;
+    return a;
+}
+
+//IMPL: OffsetableConcept
+template<unsigned n>
+inline SBasisN<n> operator+(const SBasisN<n> & a, double b) {
+    if(a.isZero()) return LinearN<n>(b);
+    SBasisN<n> result(a);
+    result[0] += b;
+    return result;
+}
+template<unsigned n>
+inline SBasisN<n> operator-(const SBasisN<n> & a, double b) {
+    if(a.isZero()) return LinearN<n>(-b);
+    SBasisN<n> result(a);
+    result[0] -= b;
+    return result;
+}
+template<unsigned n>
+inline SBasisN<n>& operator+=(SBasisN<n>& a, double b) {
+    if(a.size()==0)
+        a.push_back(LinearN<n>(b));
+    else
+        a[0] += b;
+    return a;
+}
+template<unsigned n>
+inline SBasisN<n>& operator-=(SBasisN<n>& a, double b) {
+    if(a.size()==0)
+        a.push_back(LinearN<n>(-b));
+    else
+        a[0] -= b;
+    return a;
+}
+
+template<unsigned n>
+SBasisN<n> shift(SBasisN<n> const &a, MultiDegree<n> sh){
+    SBasisN<n> result;
+    MultiDegree<n> deg = a.quick_multi_degree() + sh;
+    for(unsigned i = 0; i < n; i++) {
+        result.sizes[i] = deg[i]+1;
+    }
+    unsigned max_size = deg.asIdx(result.sizes);
+    result.resize( max_size, LinearN<n>(0.) );
+    for(unsigned i = 0; i < a.size(); i++) {
+        MultiDegree<n> p(i,a.sizes);
+        p+=sh;
+        result[p.asIdx(result.sizes)]=a[i];
+    }
+    return result;
+}
+template<unsigned n>
+SBasisN<n> shift(LinearN<n> const &a, MultiDegree<n> sh){
+    SBasisN<n> result;
+    for(unsigned i = 0; i < n; i++) {
+        result.sizes[i] = sh[i]+1;
+    }
+    unsigned max_size = sh.asIdx(result.sizes);
+    result.resize( max_size, LinearN<n>(0.) );
+    result[max_size-1]=a;
+    return result;
+}
+//shift only in one variable
+template<unsigned n>
+SBasisN<n> shift(LinearN<n> const &a, unsigned sh, unsigned var){
+    assert( var < n );
+    SBasisN<n> result;
+    for(unsigned i = 0; i < n; i++) {
+        result.sizes[i] = 1;
+    }
+    result.sizes[var] = sh+1;
+    result.resize( sh+1, LinearN<n>(0.) );
+    result[sh]=a;
+    return result;
+}
+
+//truncate only in first variable
+template<unsigned n>
+inline SBasisN<n> truncate(SBasisN<n> const &a, unsigned first_size) {
+    if ( first_size <= a.sizes[0] ) return a;
+    SBasisN<n> c;
+    for (unsigned i = 0; i < n; i++) {
+        c.sizes[i] = a.sizes[i];
+    }
+    c.sizes[0] = first_size;
+    unsigned tot_size = 1;
+    for(unsigned i = 0; i < n; i++) {
+        tot_size*=c.sizes[i];
+    }
+    c.insert(c.begin(), a.begin(), a.begin() + tot_size);
+    return c;
+}
+
+template<unsigned n>
+SBasisN<n> multiply(SBasisN<n> const &a, SBasisN<n> const &b){
+    SBasisN<n> c;
+    MultiDegree<n> d;
+    MultiDegree<n> t_deg = a.real_t_degrees() + b.real_t_degrees();
+    for(unsigned i = 0; i < n; i++) {
+        d[i] = ( t_deg[i]%2 == 0 ? t_deg[i]/2 : (t_deg[i]-1)/2 ) ;
+    }
+    unsigned new_sizes[n], tot_size = 1;
+    for(unsigned i = 0; i < n; i++) {
+        //c.sizes[i] = d[i] + 1+1;//product of linears might give 1 more s in each dir!!
+        new_sizes[i] = d[i] + 1;
+        tot_size*=new_sizes[i];
+    }
+    c.resize( tot_size, LinearN<n>(0.) );
+    for(unsigned i = 0; i < n; i++) {
+        c.sizes[i] = new_sizes[i];
+    }
+
+    for(unsigned ai = 0; ai < a.size(); ai++) {
+        for(unsigned bj = 0; bj < b.size(); bj++) {
+            MultiDegree<n> di( ai, a.sizes ); 
+            MultiDegree<n> dj( bj, b.sizes ); 
+            //compute a[ai]*b[bj]:
+            for(unsigned p = 0; p < (1<<n); p++) {
+                for(unsigned q = 0; q < (1<<n); q++) {
+
+                    //compute a[ai][p]*b[bj][q]:
+                    unsigned m = p^q;//m has ones for factors s, 0 for (t-s) or ((1-t)-s).
+                    for(unsigned r = 0; r < (1<<n); r++) {
+                        if (!(r&m)) {// a 1 in r means take t (or (1-t)), otherwise take -s.
+                            int sign = 1;
+                            MultiDegree<n> dr; 
+                            unsigned t0 = 0, t1 = 0;
+                            for (unsigned var = 0; var < n; var++) {
+                                //if var is in mask m, no choice, take s
+                                if ( m & (1<<var) ){
+                                    dr.p[var] = 1;
+                                }//if var is in mask r, take t or (1-t)
+                                else  if ( r & (1<<var) ){
+                                    dr.p[var] = 0;
+                                    if ( p&(1<<var) ) {
+                                        t0 = t0 | (1<<var);
+                                    }else{
+                                        t1 = t1 | (1<<var);
+                                    }
+                                }//ohterwise take -s
+                                else{
+                                    dr.p[var] = 1;
+                                    sign *= -1;
+                                }
+                            }
+                            unsigned idx = (di+dj+dr).asIdx(c.sizes);
+                            if (idx < c.size()){
+                                for(unsigned s = 0; s < (1<<n); s++) {
+                                    if ( (t0 & ~s) || (t1 & s) ){
+                                        c[idx][s] += 0;
+                                    }else{
+                                        c[idx][s] += sign * a[ai][p] * b[bj][q];
+                                    }
+                                }
+                            }
+                        }
+                    }//r loop: all choices have been expanded in the product a[ai][p]*b[bj][q]
+                }//q loop
+            }//p loop: all products a[ai][p]*b[bj][q] have been computed.
+        }//bj loop
+    }//ai loop: all a[ai]b[bj] have been computed.
+
+    //TODO: normalize c, or even better, compute with the right size from scratch
+    return c;
+}
+
+
+template<unsigned n>
+inline SBasisN<n> operator*(SBasisN<n> const & a, SBasisN<n> const & b) {
+    return multiply(a, b);
+}
+
+template<unsigned n>
+inline SBasisN<n>& operator*=(SBasisN<n>& a, SBasisN<n> const & b) {
+    a = multiply(a, b);
+    return a;
+}
+
+template<unsigned m,unsigned n>
+SBasisN<m> compose(LinearN<n> const &f, std::vector<SBasisN<m> > const &t, unsigned fixed=0, unsigned flags=0 ){
+    assert (t.size() == n );
+    if (fixed == n) {
+        return SBasisN<m>(1.) * f[flags]; 
+    }else{
+        SBasisN<m> a0 = compose(f, t, fixed+1, flags);
+        SBasisN<m> a1 = compose(f, t, fixed+1, flags|(1<<fixed));
+        return (-t[fixed]+1) * a0 + t[fixed] * a1;
+    }
+}
+
+template<unsigned m,unsigned n>
+SBasisN<m> compose(SBasisN<n> const &f, std::vector<SBasisN<m> > const &t, unsigned const k=0, unsigned const idx = 0){
+    assert (t.size() == n );
+    if (k == n){
+        return compose( f[idx], t);
+    }
+    SBasisN<m> a;
+    SBasisN<m> s = multiply( t[k], (-t[k]+1.) );
+    SBasisN<m> si= SBasisN<m>(1.);
+    for (unsigned i=0; i<f.sizes[k]; i++){
+        a += compose(f, t,k+1,idx*f.sizes[k]+i)*si;;
+        si *= s;
+    }
+    return a;
+}
+
+template <unsigned n>
+inline std::ostream &operator<< (std::ostream &out_file, const MultiDegree<n> & d) {
+    out_file << "s^{";
+    for(unsigned i = 0; i < n; i++) {
+        out_file << d[i] << (i == n-1 ? "}" : ",");        
+    }
+    return out_file;
+}
+template <unsigned n>
+inline std::ostream &operator<< (std::ostream &out_file, const SBasisN<n> & p) {
+    for(unsigned i = 0; i < p.size(); i++) {
+        MultiDegree<n> d(i, p.sizes);
+        out_file << d << " " << p[i] << " + ";
+    }
+    return out_file;
+}
+
+
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+
+#if 0
+
+
+// This performs a multiply and accumulate operation in about the same time as multiply.  return a*b + c
+template<unsigned n>
+SBasisN<n> multiply_add(SBasisN<n> const &a, SBasisN<n> const &b, SBasisN<n> c);
+
+template<unsigned n>
+SBasisN<n> integral(SBasisN<n> const &c);
+template<unsigned n>
+SBasisN<n> derivative(SBasisN<n> const &a);
+
+template<unsigned n>
+SBasisN<n> sqrt(SBasisN<n> const &a, int k);
+
+// return a kth order approx to 1/a)
+template<unsigned n>
+SBasisN<n> reciprocal(LinearN<n> const &a, int k);
+template<unsigned n>
+SBasisN<n> divide(SBasisN<n> const &a, SBasisN<n> const &b, int k);
+
+
+/** Returns the degree of the first non zero coefficient.
+ \param a sbasis function
+ \param tol largest abs val considered 0
+ \returns first non zero coefficient
+*/
+template<unsigned n>
+inline unsigned 
+valuation(SBasisN<n> const &a, double tol=0){
+    unsigned val=0;
+    while( val<a.size() &&
+           fabs(a[val][0])<tol &&
+           fabs(a[val][1])<tol ) 
+        val++;
+    return val;
+}
+
+// a(b(t))
+template<unsigned n>
+SBasisN<n> compose(SBasisN<n> const &a, SBasisN<n> const &b);
+template<unsigned n>
+SBasisN<n> compose(SBasisN<n> const &a, SBasisN<n> const &b, unsigned k);
+template<unsigned n>
+SBasisN<n> inverse(SBasisN<n> a, int k);
+//compose_inverse(f,g)=compose(f,inverse(g)), but is numerically more stable in some good cases...
+//TODO: requires g(0)=0 & g(1)=1 atm. generalization should be obvious.
+template<unsigned n>
+SBasisN<n> compose_inverse(SBasisN<n> const &f, SBasisN<n> const &g, unsigned order=2, double tol=1e-3);
+
+/** Returns the sbasis on domain [0,1] that was t on [from, to]
+ \param a sbasis function
+ \param from,to interval
+ \returns sbasis
+
+*/
+template<unsigned n>
+inline SBasisN<n> portion(const SBasisN<n> &t, double from, double to) { return compose(t, LinearN<n>(from, to)); }
+
+// compute f(g)
+template<unsigned n>
+inline SBasisN<n>
+SBasisN<n>::operator()(SBasisN<n> const & g) const {
+    return compose(*this, g);
+}
+ 
+template<unsigned n>
+inline std::ostream &operator<< (std::ostream &out_file, const LinearN<n> &bo) {
+    out_file << "{" << bo[0] << ", " << bo[1] << "}";
+    return out_file;
+}
+
+template<unsigned n>
+inline std::ostream &operator<< (std::ostream &out_file, const SBasisN<n> & p) {
+    for(unsigned i = 0; i < p.size(); i++) {
+        out_file << p[i] << "s^" << i << " + ";
+    }
+    return out_file;
+}
+
+// These are deprecated, use sbasis-math.h versions if possible
+template<unsigned n>
+SBasisN<n> sin(LinearN<n> bo, int k);
+template<unsigned n>
+SBasisN<n> cos(LinearN<n> bo, int k);
+
+template<unsigned n>
+std::vector<double> roots(SBasisN<n> const & s);
+template<unsigned n>
+std::vector<std::vector<double> > multi_roots(SBasisN<n> const &f,
+                                 std::vector<double> const &levels,
+                                 double htol=1e-7,
+                                 double vtol=1e-7,
+                                 double a=0,
+                                 double b=1);
+    
+#endif
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+//--------------------------------------------------
+}
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
+#endif
diff --git a/include/2geom/parallelogram.h b/include/2geom/parallelogram.h
new file mode 100644
index 0000000..0c7134f
--- /dev/null
+++ b/include/2geom/parallelogram.h
@@ -0,0 +1,83 @@
+/*
+ * Authors:
+ *   Thomas Holder
+ *   Sergei Izmailov
+ *
+ * Copyright 2020 Authors
+ *
+ * SPDX-License-Identifier: LGPL-2.1 or MPL-1.1
+ */
+
+#ifndef LIB2GEOM_SEEN_PARALLELOGRAM_H
+#define LIB2GEOM_SEEN_PARALLELOGRAM_H
+
+#include <2geom/affine.h>
+#include <2geom/rect.h>
+
+namespace Geom {
+
+/**
+ * Paralellogram, representing a linear transformation of a rectangle.
+ *
+ * Implements efficient "contains" and "intersects" operations.
+ */
+class Parallelogram {
+    Affine m_affine;
+
+    /// Transformed unit rectangle
+    Parallelogram(Affine const &affine)
+        : m_affine(affine)
+    {
+    }
+
+  public:
+    explicit Parallelogram(Rect const &rect)
+        : m_affine(rect.width(), 0, 0, rect.height(), rect.left(), rect.top())
+    {
+    }
+
+    Point corner(unsigned i) const;
+
+    Point midpoint() const { return Point(0.5, 0.5) * m_affine; }
+
+    /// Area (non-negative)
+    Coord area() const { return m_affine.descrim2(); }
+
+    /// Axis-aligned bounding box
+    Rect bounds() const;
+
+    bool intersects(Parallelogram const &) const;
+    bool intersects(Rect const &rect) const { return intersects(Parallelogram(rect)); }
+
+    bool contains(Point const &) const;
+    bool contains(Parallelogram const &) const;
+    bool contains(Rect const &rect) const { return contains(Parallelogram(rect)); }
+
+    /// Returns shorter side length
+    Coord minExtent() const;
+
+    /// Returns longer side length
+    Coord maxExtent() const;
+
+    /// Return a new transformed parallelogram
+    Parallelogram operator*(Affine const &affine) const { return m_affine * affine; }
+    Parallelogram &operator*=(Affine const &affine) { m_affine *= affine; return *this; }
+
+    /// True if this parallelogram does not have right angles
+    bool isSheared(Coord eps = EPSILON) const;
+};
+
+} // namespace Geom
+
+#endif
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/path-intersection.h b/include/2geom/path-intersection.h
new file mode 100644
index 0000000..7d7cec9
--- /dev/null
+++ b/include/2geom/path-intersection.h
@@ -0,0 +1,118 @@
+/**
+ * \file
+ * \brief Path intersection
+ *//*
+ * Authors:
+ *      ? <?@?.?>
+ * 
+ * Copyright ?-?  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_PATH_INTERSECTION_H
+#define LIB2GEOM_SEEN_PATH_INTERSECTION_H
+
+#include <2geom/crossing.h>
+#include <2geom/path.h>
+#include <2geom/sweep-bounds.h>
+
+namespace Geom {
+
+int winding(Path const &path, Point const &p);
+bool path_direction(Path const &p);
+
+inline bool contains(Path const & p, Point const &i, bool evenodd = true) {
+    return (evenodd ? winding(p, i) % 2 : winding(p, i)) != 0;
+}
+
+template<typename T>
+Crossings curve_sweep(Path const &a, Path const &b) {
+    T t;
+    Crossings ret;
+    std::vector<Rect> bounds_a = bounds(a), bounds_b = bounds(b);
+    std::vector<std::vector<unsigned> > ixs = sweep_bounds(bounds_a, bounds_b);
+    for(unsigned i = 0; i < a.size(); i++) {
+        for(std::vector<unsigned>::iterator jp = ixs[i].begin(); jp != ixs[i].end(); ++jp) {
+            Crossings cc = t.crossings(a[i], b[*jp]);
+            offset_crossings(cc, i, *jp);
+            ret.insert(ret.end(), cc.begin(), cc.end());
+        }
+    }
+    return ret;
+}
+
+Crossings pair_intersect(Curve const & A, Interval const &Ad,
+                    Curve const & B, Interval const &Bd);
+Crossings mono_intersect(Curve const & A, Interval const &Ad,
+                    Curve const & B, Interval const &Bd);
+                    
+struct SimpleCrosser : public Crosser<Path> {
+    Crossings crossings(Curve const &a, Curve const &b);
+    Crossings crossings(Path const &a, Path const &b) override { return curve_sweep<SimpleCrosser>(a, b); }
+    CrossingSet crossings(PathVector const &a, PathVector const &b) override { return Crosser<Path>::crossings(a, b); }
+};
+
+struct MonoCrosser : public Crosser<Path> {
+    Crossings crossings(Path const &a, Path const &b) override { return crossings(PathVector(a), PathVector(b))[0]; }
+    CrossingSet crossings(PathVector const &a, PathVector const &b) override;
+};
+
+typedef SimpleCrosser DefaultCrosser;
+
+std::vector<double> path_mono_splits(Path const &p);
+
+CrossingSet crossings_among(PathVector const & p);
+Crossings self_crossings(Path const & a);
+
+inline Crossings crossings(Curve const & a, Curve const & b) {
+    DefaultCrosser c = DefaultCrosser();
+    return c.crossings(a, b);
+}
+
+inline Crossings crossings(Path const & a, Path const & b) {
+    DefaultCrosser c = DefaultCrosser();
+    return c.crossings(a, b);
+}
+
+inline CrossingSet crossings(PathVector const & a, PathVector const & b) {
+    DefaultCrosser c = DefaultCrosser();
+    return c.crossings(a, b);
+}
+
+}
+
+#endif
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/path-sink.h b/include/2geom/path-sink.h
new file mode 100644
index 0000000..35bd1cd
--- /dev/null
+++ b/include/2geom/path-sink.h
@@ -0,0 +1,253 @@
+/**
+ * \file
+ * \brief  callback interface for SVG path data
+ *//*
+ * Copyright 2007 MenTaLguY <mental@rydia.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_PATH_SINK_H
+#define LIB2GEOM_SEEN_PATH_SINK_H
+
+#include <2geom/forward.h>
+#include <2geom/pathvector.h>
+#include <2geom/curves.h>
+#include <iterator>
+
+namespace Geom {
+
+
+/** @brief Callback interface for processing path data.
+ *
+ * PathSink provides an interface that allows one to easily write
+ * code which processes path data, for instance when converting
+ * between path formats used by different graphics libraries.
+ * It is also useful for writing algorithms which must do something
+ * for each curve in the path.
+ *
+ * To store a path in a new format, implement the virtual methods
+ * for segments in a derived class and call feed().
+ *
+ * @ingroup Paths
+ */
+class PathSink {
+public:
+    /** @brief Move to a different point without creating a segment.
+     * Usually starts a new subpath. */
+    virtual void moveTo(Point const &p) = 0;
+    /// Output a line segment.
+    virtual void lineTo(Point const &p) = 0;
+    /// Output a quadratic Bezier segment.
+    virtual void curveTo(Point const &c0, Point const &c1, Point const &p) = 0;
+    /// Output a cubic Bezier segment.
+    virtual void quadTo(Point const &c, Point const &p) = 0;
+    /** @brief Output an elliptical arc segment.
+     * See the EllipticalArc class for the documentation of parameters. */
+    virtual void arcTo(Coord rx, Coord ry, Coord angle,
+                       bool large_arc, bool sweep, Point const &p) = 0;
+
+    /// Close the current path with a line segment.
+    virtual void closePath() = 0;
+    /** @brief Flush any internal state of the generator.
+     * This call should implicitly finish the current subpath.
+     * Calling this method should be idempotent, because the default
+     * implementations of path() and pathvector() will call it
+     * multiple times in a row. */
+    virtual void flush() = 0;
+    // Get the current point, e.g. where the initial point of the next segment will be.
+    //virtual Point currentPoint() const = 0;
+
+    /** @brief Undo the last segment.
+     * This method is optional.
+     * @return true true if a segment was erased, false otherwise. */
+    virtual bool backspace() { return false; }
+
+    // these have a default implementation
+    virtual void feed(Curve const &c, bool moveto_initial = true);
+    /** @brief Output a subpath.
+     * Calls the appropriate segment methods according to the contents
+     * of the passed subpath. You can override this function.
+     * NOTE: if you override only some of the feed() functions,
+     * always write this in the derived class:
+     * @code
+       using PathSink::feed;
+       @endcode
+     * Otherwise the remaining methods will be hidden. */
+    virtual void feed(Path const &p);
+    /** @brief Output a path.
+     * Calls feed() on each path in the vector. You can override this function. */
+    virtual void feed(PathVector const &v);
+    /// Output an axis-aligned rectangle, using moveTo, lineTo and closePath.
+    virtual void feed(Rect const &);
+    /// Output a circle as two elliptical arcs.
+    virtual void feed(Circle const &e);
+    /// Output an ellipse as two elliptical arcs.
+    virtual void feed(Ellipse const &e);
+
+    virtual ~PathSink() {}
+};
+
+/** @brief Store paths to an output iterator
+ * @ingroup Paths */
+template <typename OutputIterator>
+class PathIteratorSink : public PathSink {
+public:
+    explicit PathIteratorSink(OutputIterator out)
+    : _in_path(false), _out(out) {}
+
+    void moveTo(Point const &p) override {
+        flush();
+        _path.start(p);
+        _start_p = p;
+        _in_path = true;
+    }
+//TODO: what if _in_path = false?
+
+    /** @brief Detect if the builder is in a path and thus will NOT
+               create a new moveTo command when given the next line
+        @return true if the builder is inside a subpath.
+     */
+    bool inPath() const {
+        return _in_path;
+    }
+
+    void lineTo(Point const &p) override {
+        // check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z"
+        if (!_in_path) {
+            moveTo(_start_p);
+        }
+        _path.template appendNew<LineSegment>(p);
+    }
+
+    void quadTo(Point const &c, Point const &p) override {
+        // check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z"
+        if (!_in_path) {
+            moveTo(_start_p);
+        }
+        _path.template appendNew<QuadraticBezier>(c, p);
+    }
+
+    void curveTo(Point const &c0, Point const &c1, Point const &p) override {
+        // check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z"
+        if (!_in_path) {
+            moveTo(_start_p);
+        }
+        _path.template appendNew<CubicBezier>(c0, c1, p);
+    }
+
+    void arcTo(Coord rx, Coord ry, Coord angle,
+               bool large_arc, bool sweep, Point const &p) override
+    {
+        // check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z"
+        if (!_in_path) {
+            moveTo(_start_p);
+        }
+        _path.template appendNew<EllipticalArc>(rx, ry, angle,
+                                                large_arc, sweep, p);
+    }
+
+    bool backspace() override
+    {
+        if (_in_path && _path.size() > 0) {
+            _path.erase_last();
+            return true;
+        }
+        return false;
+    }
+
+    void append(Path const &other)
+    {
+        if (!_in_path) {
+            moveTo(other.initialPoint());
+        }
+        _path.append(other);
+    }
+
+    void closePath() override {
+        if (_in_path) {
+            _path.close();
+            flush();
+        }
+    }
+
+    void flush() override {
+        if (_in_path) {
+            _in_path = false;
+            *_out++ = _path;
+            _path.clear();
+        }
+    }
+    
+    void setStitching(bool s) {
+        _path.setStitching(s);
+    }
+
+    using PathSink::feed;
+    void feed(Path const &other) override
+    {
+        flush();
+        *_out++ = other;
+    }
+
+protected:
+    bool _in_path;
+    OutputIterator _out;
+    Path _path;
+    Point _start_p;
+};
+
+typedef std::back_insert_iterator<PathVector> SubpathInserter;
+
+/** @brief Store paths to a PathVector
+ * @ingroup Paths */
+class PathBuilder : public PathIteratorSink<SubpathInserter> {
+private:
+    PathVector _pathset;
+public:
+    /// Create a builder that outputs to an internal pathvector.
+    PathBuilder() : PathIteratorSink<SubpathInserter>(SubpathInserter(_pathset)) {}
+    /// Create a builder that outputs to pathvector given by reference.
+    PathBuilder(PathVector &pv) : PathIteratorSink<SubpathInserter>(SubpathInserter(pv)) {}
+
+    /// Retrieve the path
+    PathVector const &peek() const {return _pathset;}
+    /// Clear the stored path vector
+    void clear() { _pathset.clear(); }
+};
+
+}
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/path.h b/include/2geom/path.h
new file mode 100644
index 0000000..f3042d7
--- /dev/null
+++ b/include/2geom/path.h
@@ -0,0 +1,917 @@
+/** @file
+ * @brief Path - a sequence of contiguous curves
+ *//*
+  * Authors:
+  *   MenTaLguY <mental@rydia.net>
+  *   Marco Cecchetti <mrcekets at gmail.com>
+  *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+  *
+  * Copyright 2007-2014 Authors
+  *
+  * This library is free software; you can redistribute it and/or
+  * modify it either under the terms of the GNU Lesser General Public
+  * License version 2.1 as published by the Free Software Foundation
+  * (the "LGPL") or, at your option, under the terms of the Mozilla
+  * Public License Version 1.1 (the "MPL"). If you do not alter this
+  * notice, a recipient may use your version of this file under either
+  * the MPL or the LGPL.
+  *
+  * You should have received a copy of the LGPL along with this library
+  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+  * You should have received a copy of the MPL along with this library
+  * in the file COPYING-MPL-1.1
+  *
+  * The contents of this file are subject to the Mozilla Public License
+  * Version 1.1 (the "License"); you may not use this file except in
+  * compliance with the License. You may obtain a copy of the License at
+  * http://www.mozilla.org/MPL/
+  *
+  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+  * the specific language governing rights and limitations.
+  */
+
+#ifndef LIB2GEOM_SEEN_PATH_H
+#define LIB2GEOM_SEEN_PATH_H
+
+#include <cstddef>
+#include <iterator>
+#include <algorithm>
+#include <iostream>
+#include <memory>
+#include <optional>
+#include <utility>
+#include <vector>
+
+#include <boost/operators.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+
+#include <2geom/intersection.h>
+#include <2geom/curve.h>
+#include <2geom/bezier-curve.h>
+#include <2geom/transforms.h>
+
+namespace Geom {
+
+class Path;
+class ConvexHull;
+
+namespace PathInternal {
+
+typedef boost::ptr_vector<Curve> Sequence;
+
+struct PathData {
+    Sequence curves;
+    OptRect fast_bounds;
+};
+
+template <typename P>
+class BaseIterator
+    : public boost::random_access_iterator_helper
+        < BaseIterator<P>
+        , Curve const
+        , std::ptrdiff_t
+        , Curve const *
+        , Curve const &
+        >
+{
+  protected:
+    BaseIterator(P &p, unsigned i) : path(&p), index(i) {}
+    // default copy, default assign
+    typedef BaseIterator<P> Self;
+
+  public:
+    BaseIterator() : path(NULL), index(0) {}
+
+    bool operator<(BaseIterator const &other) const {
+        return path == other.path && index < other.index;
+    }
+    bool operator==(BaseIterator const &other) const {
+        return path == other.path && index == other.index;
+    }
+    Curve const &operator*() const {
+        return (*path)[index];
+    }
+
+    Self &operator++() {
+        ++index;
+        return *this;
+    }
+    Self &operator--() {
+        --index;
+        return *this;
+    }
+    Self &operator+=(std::ptrdiff_t d) {
+        index += d;
+        return *this;
+    }
+    Self &operator-=(std::ptrdiff_t d) {
+        index -= d;
+        return *this;
+    }
+    std::ptrdiff_t operator-(Self const &other) const {
+        assert(path == other.path);
+        return (std::ptrdiff_t)index - (std::ptrdiff_t)other.index;
+    }
+
+  private:
+    P *path;
+    unsigned index;
+
+    friend class ::Geom::Path;
+};
+
+}
+
+/** @brief Generalized time value in the path.
+ *
+ * This class exists because when mapping the range of multiple curves onto the same interval
+ * as the curve index, we lose some precision. For instance, a path with 16 curves will
+ * have 4 bits less precision than a path with 1 curve. If you need high precision results
+ * in long paths, either use this class and related methods instead of the standard methods
+ * pointAt(), nearestTime() and so on, or use curveAt() to first obtain the curve, then
+ * call the method again to obtain a high precision result.
+ *
+ * @ingroup Paths */
+struct PathTime
+    : boost::totally_ordered<PathTime>
+{
+    typedef PathInternal::Sequence::size_type size_type;
+
+    Coord t; ///< Time value in the curve
+    size_type curve_index; ///< Index of the curve in the path
+
+    PathTime() : t(0), curve_index(0) {}
+    PathTime(size_type idx, Coord tval) : t(tval), curve_index(idx) {}
+
+    bool operator<(PathTime const &other) const {
+        if (curve_index < other.curve_index) return true;
+        if (curve_index == other.curve_index) {
+            return t < other.t;
+        }
+        return false;
+    }
+    bool operator==(PathTime const &other) const {
+        return curve_index == other.curve_index && t == other.t;
+    }
+    /// Convert times at or beyond 1 to 0 on the next curve.
+    void normalizeForward(size_type path_size) {
+        if (t >= 1) {
+            curve_index = (curve_index + 1) % path_size;
+            t = 0;
+        }
+    }
+    /// Convert times at or before 0 to 1 on the previous curve.
+    void normalizeBackward(size_type path_size) {
+        if (t <= 0) {
+            curve_index = (curve_index - 1) % path_size;
+            t = 1;
+        }
+    }
+
+    Coord asFlatTime() const { return curve_index + t; }
+};
+
+inline std::ostream &operator<<(std::ostream &os, PathTime const &pos) {
+    os << pos.curve_index << ": " << format_coord_nice(pos.t);
+    return os;
+}
+
+
+/** @brief Contiguous subset of the path's parameter domain.
+ * This is a directed interval, which allows one to specify any contiguous subset
+ * of the path's domain, including subsets that wrap around the initial point
+ * of the path.
+ * @ingroup Paths */
+class PathInterval {
+public:
+    typedef PathInternal::Sequence::size_type size_type;
+
+    /** @brief Default interval.
+     * Default-constructed PathInterval includes only the initial point of the initial segment. */
+    PathInterval();
+
+    /** @brief Construct an interval in the path's parameter domain.
+     * @param from Initial time
+     * @param to Final time
+     * @param cross_start If true, the interval will proceed from the initial to final
+     *   time through the initial point of the path, wrapping around the closing segment;
+     *   otherwise it will not wrap around the closing segment.
+     * @param path_size Size of the path to which this interval applies, required
+     *   to clean up degenerate cases */
+    PathInterval(PathTime const &from, PathTime const &to, bool cross_start, size_type path_size);
+
+    /// Get the time value of the initial point.
+    PathTime const &initialTime() const { return _from; }
+    /// Get the time value of the final point.
+    PathTime const &finalTime() const { return _to; }
+
+    PathTime const &from() const { return _from; }
+    PathTime const &to() const { return _to; }
+
+    /// Check whether the interval has only one point.
+    bool isDegenerate() const { return _from == _to; }
+    /// True if the interval goes in the direction of decreasing time values.
+    bool reverse() const { return _reverse; }
+    /// True if the interior of the interval contains the initial point of the path.
+    bool crossesStart() const { return _cross_start; }
+
+    /// Test a path time for inclusion.
+    bool contains(PathTime const &pos) const;
+
+    /// Get a time at least @a min_dist away in parameter space from the ends.
+    /// If no such time exists, the middle point is returned.
+    PathTime inside(Coord min_dist = EPSILON) const;
+
+    /// Select one of two intervals with given endpoints by parameter direction.
+    static PathInterval from_direction(PathTime const &from, PathTime const &to,
+                                       bool reversed, size_type path_size);
+
+    /// Select one of two intervals with given endpoints by whether it includes the initial point.
+    static PathInterval from_start_crossing(PathTime const &from, PathTime const &to,
+                                            bool cross_start, size_type path_size) {
+        PathInterval result(from, to, cross_start, path_size);
+        return result;
+    }
+
+    size_type pathSize() const { return _path_size; }
+    size_type curveCount() const;
+
+private:
+    PathTime _from, _to;
+    size_type _path_size;
+    bool _cross_start, _reverse;
+};
+
+/// Create an interval in the direction of increasing time value.
+/// @relates PathInterval
+inline PathInterval forward_interval(PathTime const &from, PathTime const &to,
+                                     PathInterval::size_type path_size)
+{
+    PathInterval result = PathInterval::from_direction(from, to, false, path_size);
+    return result;
+}
+
+/// Create an interval in the direction of decreasing time value.
+/// @relates PathInterval
+inline PathInterval backward_interval(PathTime const &from, PathTime const &to,
+                                      PathInterval::size_type path_size)
+{
+    PathInterval result = PathInterval::from_direction(from, to, true, path_size);
+    return result;
+}
+
+/// Output an interval in the path's domain.
+/// @relates PathInterval
+inline std::ostream &operator<<(std::ostream &os, PathInterval const &ival) {
+    os << "PathInterval[";
+    if (ival.crossesStart()) {
+        os << ival.from() << " -> 0: 0.0 -> " << ival.to();
+    } else {
+        os << ival.from() << " -> " << ival.to();
+    }
+    os << "]";
+    return os;
+}
+
+typedef Intersection<PathTime> PathIntersection;
+
+template <>
+struct ShapeTraits<Path> {
+    typedef PathTime TimeType;
+    typedef PathInterval IntervalType;
+    typedef Path AffineClosureType;
+    typedef PathIntersection IntersectionType;
+};
+
+/** @brief Stores information about the extremum points on a path, with respect
+ *         to one of the coordinate axes.
+ *  @relates Path::extrema()
+ */
+struct PathExtrema {
+    /** Points with the minimum and maximum value of a coordinate. */
+    Point min_point, max_point;
+
+    /** Directions in which the OTHER coordinates change at the extremum points.
+     *
+     * - equals +1.0 if the other coordinate increases,
+     * - equals  0.0 if the other coordinate is constant (e.g., for an axis-aligned segment),
+     * - equals -1.0 if the other coordinate decreases.
+     */
+    float glance_direction_at_min, glance_direction_at_max;
+
+    /** Path times corresponding to minimum and maximum points. */
+    PathTime min_time, max_time;
+};
+
+/** @brief Sequence of contiguous curves, aka spline.
+ *
+ * Path represents a sequence of contiguous curves, also known as a spline.
+ * It corresponds to a "subpath" in SVG terminology. It can represent both
+ * open and closed subpaths. The final point of each curve is exactly
+ * equal to the initial point of the next curve.
+ *
+ * The path always contains a linear closing segment that connects
+ * the final point of the last "real" curve to the initial point of the
+ * first curve. This way the curves form a closed loop even for open paths.
+ * If the closing segment has nonzero length and the path is closed, it is
+ * considered a normal part of the path data. There are three distinct sets
+ * of end iterators one can use to iterate over the segments:
+ *
+ * - Iterating between @a begin() and @a end() will iterate over segments
+ *   which are part of the path.
+ * - Iterating between @a begin() and @a end_closed()
+ *   will always iterate over a closed loop of segments.
+ * - Iterating between @a begin() and @a end_open() will always skip
+ *   the final linear closing segment.
+ *
+ * If the final point of the last "real" segment coincides exactly with the initial
+ * point of the first segment, the closing segment will be absent from both
+ * [begin(), end_open()) and [begin(), end_closed()).
+ *
+ * Normally, an exception will be thrown when you try to insert a curve
+ * that makes the path non-continuous. If you are working with unsanitized
+ * curve data, you can call setStitching(true), which will insert line segments
+ * to make the path continuous.
+ *
+ * Internally, Path uses copy-on-write data. This is done for two reasons: first,
+ * copying a Curve requires calling a virtual function, so it's a little more expensive
+ * that normal copying; and second, it reduces the memory cost of copying the path.
+ * Therefore you can return Path and PathVector from functions without worrying
+ * about temporary copies.
+ *
+ * Note that this class cannot represent arbitrary shapes, which may contain holes.
+ * To do that, use PathVector, which is more generic.
+ *
+ * It's not very convenient to create a Path directly. To construct paths more easily,
+ * use PathBuilder.
+ *
+ * @ingroup Paths */
+class Path
+    : boost::equality_comparable< Path >
+{
+public:
+    typedef PathInternal::PathData PathData;
+    typedef PathInternal::Sequence Sequence;
+    typedef PathInternal::BaseIterator<Path> iterator;
+    typedef PathInternal::BaseIterator<Path const> const_iterator;
+    typedef Sequence::size_type size_type;
+    typedef Sequence::difference_type difference_type;
+
+    class ClosingSegment : public LineSegment {
+      public:
+        ClosingSegment() : LineSegment() {}
+        ClosingSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {}
+        Curve *duplicate() const override { return new ClosingSegment(*this); }
+        Curve *reverse() const override { return new ClosingSegment((*this)[1], (*this)[0]); }
+    };
+
+    class StitchSegment : public LineSegment {
+      public:
+        StitchSegment() : LineSegment() {}
+        StitchSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {}
+        Curve *duplicate() const override { return new StitchSegment(*this); }
+        Curve *reverse() const override { return new StitchSegment((*this)[1], (*this)[0]); }
+    };
+
+    // Path(Path const &other) - use default copy constructor
+
+    /// Construct an empty path starting at the specified point.
+    explicit Path(Point const &p = Point())
+        : _data(new PathData())
+        , _closing_seg(new ClosingSegment(p, p))
+        , _closed(false)
+        , _exception_on_stitch(true)
+    {
+        _data->curves.push_back(_closing_seg);
+    }
+
+    /// Construct a path containing a range of curves.
+    template <typename Iter>
+    Path(Iter first, Iter last, bool closed = false, bool stitch = false)
+        : _data(new PathData())
+        , _closed(closed)
+        , _exception_on_stitch(!stitch)
+    {
+        for (Iter i = first; i != last; ++i) {
+            _data->curves.push_back(i->duplicate());
+        }
+        if (!_data->curves.empty()) {
+            _closing_seg = new ClosingSegment(_data->curves.back().finalPoint(),
+                                              _data->curves.front().initialPoint());
+        } else {
+            _closing_seg = new ClosingSegment();
+        }
+        _data->curves.push_back(_closing_seg);
+    }
+
+    /// Create a path from a rectangle.
+    explicit Path(Rect const &r);
+    /// Create a path from a convex hull.
+    explicit Path(ConvexHull const &);
+    /// Create a path from a circle, using two elliptical arcs.
+    explicit Path(Circle const &c);
+    /// Create a path from an ellipse, using two elliptical arcs.
+    explicit Path(Ellipse const &e);
+
+    virtual ~Path() {}
+
+    // Path &operator=(Path const &other) - use default assignment operator
+
+    /** @brief Swap contents with another path
+     * @todo Add noexcept specifiers for C++11 */
+    void swap(Path &other) noexcept {
+        using std::swap;
+        swap(other._data, _data);
+        swap(other._closing_seg, _closing_seg);
+        swap(other._closed, _closed);
+        swap(other._exception_on_stitch, _exception_on_stitch);
+    }
+    /** @brief Swap contents of two paths.
+     * @relates Path */
+    friend inline void swap(Path &a, Path &b) noexcept { a.swap(b); }
+
+    /** @brief Access a curve by index */
+    Curve const &operator[](size_type i) const { return _data->curves[i]; }
+    /** @brief Access a curve by index */
+    Curve const &at(size_type i) const { return _data->curves.at(i); }
+
+    /** @brief Access the first curve in the path.
+     * Since the curve always contains at least a degenerate closing segment,
+     * it is always safe to use this method. */
+    Curve const &front() const { return _data->curves.front(); }
+    /// Alias for front().
+    Curve const &initialCurve() const { return _data->curves.front(); }
+    /** @brief Access the last curve in the path. */
+    Curve const &back() const { return back_default(); }
+    Curve const &back_open() const {
+        if (empty()) return _data->curves.back();
+        return _data->curves[_data->curves.size() - 2];
+    }
+    Curve const &back_closed() const {
+        return _closing_seg->isDegenerate()
+            ? _data->curves[_data->curves.size() - 2]
+            : _data->curves[_data->curves.size() - 1];
+    }
+    Curve const &back_default() const {
+        return _includesClosingSegment()
+            ? back_closed()
+            : back_open();
+    }
+    Curve const &finalCurve() const { return back_default(); }
+
+    const_iterator begin() const { return const_iterator(*this, 0); }
+    const_iterator end() const { return end_default(); }
+    const_iterator end_default() const { return const_iterator(*this, size_default()); }
+    const_iterator end_open() const { return const_iterator(*this, size_open()); }
+    const_iterator end_closed() const { return const_iterator(*this, size_closed()); }
+    iterator begin() { return iterator(*this, 0); }
+    iterator end() { return end_default(); }
+    iterator end_default() { return iterator(*this, size_default()); }
+    iterator end_open() { return iterator(*this, size_open()); }
+    iterator end_closed() { return iterator(*this, size_closed()); }
+
+    /// Size without the closing segment, even if the path is closed.
+    size_type size_open() const { return _data->curves.size() - 1; }
+
+    /** @brief Size with the closing segment, if it makes a difference.
+     * If the closing segment is degenerate, i.e. its initial and final points
+     * are exactly equal, then it is not included in this size. */
+    size_type size_closed() const {
+        return _closing_seg->isDegenerate() ? _data->curves.size() - 1 : _data->curves.size();
+    }
+
+    /// Natural size of the path.
+    size_type size_default() const {
+        return _includesClosingSegment() ? size_closed() : size_open();
+    }
+    /// Natural size of the path.
+    size_type size() const { return size_default(); }
+
+    size_type max_size() const { return _data->curves.max_size() - 1; }
+
+    /** @brief Check whether path is empty.
+     * The path is empty if it contains only the closing segment, which according
+     * to the continuity invariant must be degenerate. Note that unlike standard
+     * containers, two empty paths are not necessarily identical, because the
+     * degenerate closing segment may be at a different point, affecting the operation
+     * of methods such as appendNew(). */
+    bool empty() const { return (_data->curves.size() == 1); }
+
+    /// Check whether the path is closed.
+    bool closed() const { return _closed; }
+
+    /** @brief Set whether the path is closed.
+     * When closing a path where the last segment can be represented as a closing
+     * segment, the last segment will be removed. When opening a path, the closing
+     * segment will be erased. This means that closing and then opening a path
+     * will not always give back the original path. */
+    void close(bool closed = true);
+
+    /** @brief Remove all curves from the path.
+     * The initial and final points of the closing segment are set to (0,0).
+     * The stitching flag remains unchanged. */
+    void clear();
+
+    /** @brief Get the approximate bounding box.
+     * The rectangle returned by this method will contain all the curves, but it's not
+     * guaranteed to be the smallest possible one */
+    OptRect boundsFast() const;
+
+    /** @brief Get a tight-fitting bounding box.
+     * This will return the smallest possible axis-aligned rectangle containing
+     * all the curves in the path. */
+    OptRect boundsExact() const;
+
+    Piecewise<D2<SBasis> > toPwSb() const;
+
+    /// Test paths for exact equality.
+    bool operator==(Path const &other) const;
+
+    /// Apply a transform to each curve.
+    template <typename T>
+    Path &operator*=(T const &tr) {
+        BOOST_CONCEPT_ASSERT((TransformConcept<T>));
+        _unshare();
+        for (std::size_t i = 0; i < _data->curves.size(); ++i) {
+            _data->curves[i] *= tr;
+        }
+        return *this;
+    }
+
+    template <typename T>
+    friend Path operator*(Path const &path, T const &tr) {
+        BOOST_CONCEPT_ASSERT((TransformConcept<T>));
+        Path result(path);
+        result *= tr;
+        return result;
+    }
+
+    /** @brief Get the allowed range of time values.
+     * @return Values for which pointAt() and valueAt() yield valid results. */
+    Interval timeRange() const;
+
+    /** Get the curve at the specified time value.
+     * @param t Time value
+     * @param rest Optional storage for the corresponding time value in the curve */
+    Curve const &curveAt(Coord t, Coord *rest = NULL) const;
+
+    /// Get the closing segment of the path.
+    LineSegment const &closingSegment() const { return *_closing_seg; }
+
+    /** @brief Get the point at the specified time value.
+     * Note that this method has reduced precision with respect to calling pointAt()
+     * directly on the curve. If you want high precision results, use the version
+     * that takes a PathTime parameter.
+     *
+     * Allowed time values range from zero to the number of curves; you can retrieve
+     * the allowed range of values with timeRange(). */
+    Point pointAt(Coord t) const;
+
+    /// Get one coordinate (X or Y) at the specified time value.
+    Coord valueAt(Coord t, Dim2 d) const;
+
+    /// Get the curve at the specified path time.
+    Curve const &curveAt(PathTime const &pos) const;
+    /// Get the point at the specified path time.
+    Point pointAt(PathTime const &pos) const;
+    /// Get one coordinate at the specified path time.
+    Coord valueAt(PathTime const &pos, Dim2 d) const;
+
+    Point operator()(Coord t) const { return pointAt(t); }
+
+    /** @brief Find the extrema of the specified coordinate.
+     *
+     * Returns a PathExtrema struct describing "witness" points on the path
+     * where the specified coordinate attains its minimum and maximum values.
+     */
+    PathExtrema extrema(Dim2 d) const;
+
+    /// Compute intersections with axis-aligned line.
+    std::vector<PathTime> roots(Coord v, Dim2 d) const;
+
+    /// Compute intersections with another path.
+    std::vector<PathIntersection> intersect(Path const &other, Coord precision = EPSILON) const;
+
+    /// Compute intersections of the path with itself.
+    std::vector<PathIntersection> intersectSelf(Coord precision = EPSILON) const;
+
+    /** @brief Determine the winding number at the specified point.
+     *
+     * The winding number is the number of full turns made by a ray that connects the passed
+     * point and the path's value (i.e. the result of the pointAt() method) as the time increases
+     * from 0 to the maximum valid value. Positive numbers indicate turns in the direction
+     * of increasing angles.
+     *
+     * Winding numbers are often used as the definition of what is considered "inside"
+     * the shape. Typically points with either nonzero winding or odd winding are
+     * considered to be inside the path. */
+    int winding(Point const &p) const;
+
+    std::vector<Coord> allNearestTimes(Point const &p, Coord from, Coord to) const;
+    std::vector<Coord> allNearestTimes(Point const &p) const {
+        return allNearestTimes(p, 0, size_default());
+    }
+
+    PathTime nearestTime(Point const &p, Coord *dist = NULL) const;
+    std::vector<Coord> nearestTimePerCurve(Point const &p) const;
+
+    std::vector<Point> nodes() const;
+
+    void appendPortionTo(Path &p, Coord f, Coord t) const;
+
+    /** @brief Append a subset of this path to another path.
+     * An extra stitching segment will be inserted if the start point of the portion
+     * and the final point of the target path do not match exactly.
+     * The closing segment of the target path will be modified. */
+    void appendPortionTo(Path &p, PathTime const &from, PathTime const &to, bool cross_start = false) const {
+        PathInterval ival(from, to, cross_start, size_closed());
+        appendPortionTo(p, ival, std::nullopt, std::nullopt);
+    }
+
+    /** @brief Append a subset of this path to another path.
+     * This version allows you to explicitly pass a PathInterval. */
+    void appendPortionTo(Path &p, PathInterval const &ival) const {
+        appendPortionTo(p, ival, std::nullopt, std::nullopt);
+    }
+
+    /** @brief Append a subset of this path to another path, specifying endpoints.
+     * This method is for use in situations where endpoints of the portion segments
+     * have to be set exactly, for instance when computing Boolean operations. */
+    void appendPortionTo(Path &p, PathInterval const &ival,
+                         std::optional<Point> const &p_from, std::optional<Point> const &p_to) const;
+
+    Path portion(Coord f, Coord t) const {
+        Path ret;
+        ret.close(false);
+        appendPortionTo(ret, f, t);
+        return ret;
+    }
+
+    Path portion(Interval const &i) const { return portion(i.min(), i.max()); }
+
+    /** @brief Get a subset of the current path with full precision.
+     * When @a from is larger (later in the path) than @a to, the returned portion
+     * will be reversed. If @a cross_start is true, the portion will be reversed
+     * and will cross the initial point of the path. Therefore, when @a from is larger
+     * than @a to and @a cross_start is true, the returned portion will not be reversed,
+     * but will "wrap around" the end of the path. */
+    Path portion(PathTime const &from, PathTime const &to, bool cross_start = false) const {
+        Path ret;
+        ret.close(false);
+        appendPortionTo(ret, from, to, cross_start);
+        return ret;
+    }
+
+    /** @brief Get a subset of the current path with full precision.
+     * This version allows you to explicitly pass a PathInterval. */
+    Path portion(PathInterval const &ival) const {
+        Path ret;
+        ret.close(false);
+        appendPortionTo(ret, ival);
+        return ret;
+    }
+
+    /** @brief Obtain a reversed version of the current path.
+     * The final point of the current path will become the initial point
+     * of the reversed path, unless it is closed and has a non-degenerate
+     * closing segment. In that case, the new initial point will be the final point
+     * of the last "real" segment. */
+    Path reversed() const;
+
+    void insert(iterator pos, Curve const &curve);
+
+    template <typename Iter>
+    void insert(iterator pos, Iter first, Iter last) {
+        _unshare();
+        Sequence::iterator seq_pos(seq_iter(pos));
+        Sequence source;
+        for (; first != last; ++first) {
+            source.push_back(first->duplicate());
+        }
+        do_update(seq_pos, seq_pos, source);
+    }
+
+    void erase(iterator pos);
+    void erase(iterator first, iterator last);
+
+    // erase last segment of path
+    void erase_last() { erase(iterator(*this, size() - 1)); }
+
+    void start(Point const &p);
+
+    /** @brief Get the first point in the path. */
+    Point initialPoint() const { return (*_closing_seg)[1]; }
+
+    /** @brief Get the last point in the path.
+     * If the path is closed, this is always the same as the initial point. */
+    Point finalPoint() const { return (*_closing_seg)[_closed ? 1 : 0]; }
+
+    /** @brief Get the unit tangent vector at the start of the path,
+     * or the zero vector if undefined. */
+    Point initialUnitTangent() const {
+        for (auto const &curve : *this) {
+            if (!curve.isDegenerate()) {
+                return curve.unitTangentAt(0.0);
+            }
+        }
+        return Point();
+    }
+
+    /** @brief Get the unit tangent vector at the end of the path,
+     * or the zero vector if undefined. */
+    Point finalUnitTangent() const {
+        for (auto it = end(); it != begin();) {
+            --it;
+            if (!it->isDegenerate()) {
+                return it->unitTangentAt(1.0);
+            }
+        }
+        return Point();
+    }
+
+    void setInitial(Point const &p) {
+        _unshare();
+        _closed = false;
+        _data->curves.front().setInitial(p);
+        _closing_seg->setFinal(p);
+    }
+    void setFinal(Point const &p) {
+        _unshare();
+        _closed = false;
+        _data->curves[size_open() - 1].setFinal(p);
+        _closing_seg->setInitial(p);
+    }
+
+    /** @brief Add a new curve to the end of the path.
+     * This inserts the new curve right before the closing segment.
+     * The path takes ownership of the passed pointer, which should not be freed. */
+    void append(Curve *curve) {
+        _unshare();
+        stitchTo(curve->initialPoint());
+        do_append(curve);
+    }
+
+    void append(Curve const &curve) {
+        _unshare();
+        stitchTo(curve.initialPoint());
+        do_append(curve.duplicate());
+    }
+    void append(D2<SBasis> const &curve) {
+        _unshare();
+        stitchTo(Point(curve[X][0][0], curve[Y][0][0]));
+        do_append(new SBasisCurve(curve));
+    }
+    void append(Path const &other) {
+        replace(end_open(), other.begin(), other.end());
+    }
+
+    void replace(iterator replaced, Curve const &curve);
+    void replace(iterator first, iterator last, Curve const &curve);
+    void replace(iterator replaced, Path const &path);
+    void replace(iterator first, iterator last, Path const &path);
+
+    template <typename Iter>
+    void replace(iterator replaced, Iter first, Iter last) {
+        replace(replaced, replaced + 1, first, last);
+    }
+
+    template <typename Iter>
+    void replace(iterator first_replaced, iterator last_replaced, Iter first, Iter last) {
+        _unshare();
+        Sequence::iterator seq_first_replaced(seq_iter(first_replaced));
+        Sequence::iterator seq_last_replaced(seq_iter(last_replaced));
+        Sequence source;
+        for (; first != last; ++first) {
+            source.push_back(first->duplicate());
+        }
+        do_update(seq_first_replaced, seq_last_replaced, source);
+    }
+
+    /** @brief Append a new curve to the path.
+     *
+     * This family of methods will automatically use the current final point of the path
+     * as the first argument of the new curve's constructor. To call this method,
+     * you'll need to write e.g.:
+     * @code
+       path.template appendNew<CubicBezier>(control1, control2, end_point);
+       @endcode
+     * It is important to note that the coordinates passed to appendNew should be finite!
+     * If one of the coordinates is infinite, 2geom will throw a ContinuityError exception.
+     */
+    template <typename CurveType, typename... Args>
+    void appendNew(Args&&... args) {
+        _unshare();
+        do_append(new CurveType(finalPoint(), std::forward<Args>(args)...));
+    }
+
+    /** @brief Reduce the closing segment to a point if it's shorter than precision.
+     * Do this by moving the final point. */
+    void snapEnds(Coord precision = EPSILON);
+
+    /// Append a stitching segment ending at the specified point.
+    void stitchTo(Point const &p);
+
+    /** @brief Return a copy of the path without degenerate curves, except possibly for a
+      * degenerate closing segment. */
+    Path withoutDegenerateCurves() const;
+
+    /** @brief Verify the continuity invariant.
+     * If the path is not contiguous, this will throw a CountinuityError. */
+    void checkContinuity() const;
+
+    /** @brief Enable or disable the throwing of exceptions when stitching discontinuities.
+     * Normally stitching will cause exceptions, but when you are working with unsanitized
+     * curve data, you can disable these exceptions. */
+    void setStitching(bool x) {
+        _exception_on_stitch = !x;
+    }
+
+private:
+    static Sequence::iterator seq_iter(iterator const &iter) {
+        return iter.path->_data->curves.begin() + iter.index;
+    }
+    static Sequence::const_iterator seq_iter(const_iterator const &iter) {
+        return iter.path->_data->curves.begin() + iter.index;
+    }
+
+    // whether the closing segment is part of the path
+    bool _includesClosingSegment() const {
+        return _closed && !_closing_seg->isDegenerate();
+    }
+    void _unshare() {
+        // Called before every mutation.
+        // Ensure we have our own copy of curve data and reset cached values
+        if (!_data.unique()) {
+            _data.reset(new PathData(*_data));
+            _closing_seg = static_cast<ClosingSegment*>(&_data->curves.back());
+        }
+        _data->fast_bounds = OptRect();
+    }
+    PathTime _factorTime(Coord t) const;
+
+    void stitch(Sequence::iterator first_replaced, Sequence::iterator last_replaced, Sequence &sequence);
+    void do_update(Sequence::iterator first, Sequence::iterator last, Sequence &source);
+
+    // n.b. takes ownership of curve object
+    void do_append(Curve *curve);
+
+    std::shared_ptr<PathData> _data;
+    ClosingSegment *_closing_seg;
+    bool _closed;
+    bool _exception_on_stitch;
+}; // end class Path
+
+Piecewise<D2<SBasis> > paths_to_pw(PathVector const &paths);
+
+inline Coord nearest_time(Point const &p, Path const &c) {
+    PathTime pt = c.nearestTime(p);
+    return pt.curve_index + pt.t;
+}
+
+bool are_near(Path const &a, Path const &b, Coord precision = EPSILON);
+
+/**
+ * @brief Find the first point where two paths diverge away from one another.
+ *
+ * If the two paths have a common starting point, the algorithm follows them for as long as the
+ * images of the paths coincide and finds the first point where they stop coinciding. Note that
+ * only the images of paths in the plane are compared, and not their parametrizations, so this
+ * is not a functional (parametric) coincidence. If you want to test parametric coincidence, use
+ * bool are_near(Path const&, Path const&, Coord) instead.
+ *
+ * The function returns the point where the traces of the two paths finally diverge up to the
+ * specified precision. If the traces (images) of the paths are nearly identical until the end,
+ * the returned point is their (almost) common endpoint. If however the image of one of the paths
+ * is completely contained in the image of the other path, the returned point is the endpoint of
+ * the shorter path.
+ *
+ * If the paths have different starting points, then the returned intersection has the special
+ * time values of -1.0 on both paths and the returned intersection point is the midpoint of the
+ * line segment connecting the two starting points.
+ *
+ * @param first     The first path to follow; corresponds to .first in the return value.
+ * @param second    The second path to follow; corresponds to .second in the return value.
+ * @param precision How close the paths' images need to be in order to be considered as overlapping.
+ * @return A path intersection specifying the point and path times where the two paths part ways.
+ */
+PathIntersection parting_point(Path const &first, Path const &second, Coord precision = EPSILON);
+
+std::ostream &operator<<(std::ostream &out, Path const &path);
+
+} // end namespace Geom
+
+
+#endif // LIB2GEOM_SEEN_PATH_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/pathvector.h b/include/2geom/pathvector.h
new file mode 100644
index 0000000..3fd7d36
--- /dev/null
+++ b/include/2geom/pathvector.h
@@ -0,0 +1,304 @@
+/** @file
+ * @brief PathVector - a sequence of subpaths
+ *//*
+ * Authors:
+ *   Johan Engelen <j.b.c.engelen@alumnus.utwente.nl>
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * 
+ * Copyright 2008-2014 authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_PATHVECTOR_H
+#define LIB2GEOM_SEEN_PATHVECTOR_H
+
+#include <optional>
+#include <boost/concept/requires.hpp>
+#include <boost/range/algorithm/equal.hpp>
+#include <2geom/forward.h>
+#include <2geom/path.h>
+#include <2geom/transforms.h>
+
+namespace Geom {
+
+/** @brief Generalized time value in the path vector.
+ *
+ * This class exists because mapping the range of multiple curves onto the same interval
+ * as the curve index, we lose some precision. For instance, a path with 16 curves will
+ * have 4 bits less precision than a path with 1 curve. If you need high precision results
+ * in long paths, use this class and related methods instead of the standard methods
+ * pointAt(), nearestTime() and so on.
+ * 
+ * @ingroup Paths */
+struct PathVectorTime
+    : public PathTime
+    , boost::totally_ordered<PathVectorTime>
+{
+    size_type path_index; ///< Index of the path in the vector
+
+    PathVectorTime() : PathTime(0, 0), path_index(0) {}
+    PathVectorTime(size_type _i, size_type _c, Coord _t)
+        : PathTime(_c, _t), path_index(_i) {}
+    PathVectorTime(size_type _i, PathTime const &pos)
+        : PathTime(pos), path_index(_i) {}
+
+    bool operator<(PathVectorTime const &other) const {
+        if (path_index < other.path_index) return true;
+        if (path_index == other.path_index) {
+            return static_cast<PathTime const &>(*this) < static_cast<PathTime const &>(other);
+        }
+        return false;
+    }
+    bool operator==(PathVectorTime const &other) const {
+        return path_index == other.path_index
+            && static_cast<PathTime const &>(*this) == static_cast<PathTime const &>(other);
+    }
+
+    PathTime const &asPathTime() const {
+        return *static_cast<PathTime const *>(this);
+    }
+};
+
+inline std::ostream &operator<<(std::ostream &os, PathVectorTime const &pvt) {
+    os << pvt.path_index << ": " << pvt.asPathTime();
+    return os;
+}
+
+typedef Intersection<PathVectorTime> PathVectorIntersection;
+typedef PathVectorIntersection PVIntersection; ///< Alias to save typing
+
+template <>
+struct ShapeTraits<PathVector> {
+    typedef PathVectorTime TimeType;
+    //typedef PathVectorInterval IntervalType;
+    typedef PathVector AffineClosureType;
+    typedef PathVectorIntersection IntersectionType;
+};
+
+/** @brief Sequence of subpaths.
+ *
+ * This class corresponds to the SVG notion of a path:
+ * a sequence of any number of open or closed contiguous subpaths.
+ * Unlike Path, this class is closed under boolean operations.
+ *
+ * If you want to represent an arbitrary shape, this is the best class to use.
+ * Shapes with a boundary that is composed of only a single contiguous
+ * component can be represented with Path instead.
+ *
+ * @ingroup Paths
+ */
+class PathVector
+    : MultipliableNoncommutative< PathVector, Affine
+    , MultipliableNoncommutative< PathVector, Translate
+    , MultipliableNoncommutative< PathVector, Scale
+    , MultipliableNoncommutative< PathVector, Rotate
+    , MultipliableNoncommutative< PathVector, HShear
+    , MultipliableNoncommutative< PathVector, VShear
+    , MultipliableNoncommutative< PathVector, Zoom
+    , boost::equality_comparable< PathVector
+      > > > > > > > >
+{
+    typedef std::vector<Path> Sequence;
+public:
+    typedef PathVectorTime Position;
+    typedef Sequence::iterator iterator;
+    typedef Sequence::const_iterator const_iterator;
+    typedef Sequence::size_type size_type;
+    typedef Path value_type;
+    typedef Path &reference;
+    typedef Path const &const_reference;
+    typedef Path *pointer;
+    typedef std::ptrdiff_t difference_type;
+
+    PathVector() {}
+    PathVector(Path const &p)
+        : _data(1, p)
+    {}
+    template <typename InputIter>
+    PathVector(InputIter first, InputIter last)
+        : _data(first, last)
+    {}
+
+    /// Check whether the vector contains any paths.
+    bool empty() const { return _data.empty(); }
+    /// Get the number of paths in the vector.
+    size_type size() const { return _data.size(); }
+    /// Get the total number of curves in the vector.
+    size_type curveCount() const;
+
+    iterator begin() { return _data.begin(); }
+    iterator end() { return _data.end(); }
+    const_iterator begin() const { return _data.begin(); }
+    const_iterator end() const { return _data.end(); }
+    Path &operator[](size_type index) {
+        return _data[index];
+    }
+    Path const &operator[](size_type index) const {
+        return _data[index];
+    }
+    Path &at(size_type index) {
+        return _data.at(index);
+    }
+    Path const &at(size_type index) const {
+        return _data.at(index);
+    }
+    Path &front() { return _data.front(); }
+    Path const &front() const { return _data.front(); }
+    Path &back() { return _data.back(); }
+    Path const &back() const { return _data.back(); }
+    /// Append a path at the end.
+    void push_back(Path const &path) {
+        _data.push_back(path);
+    }
+    /// Remove the last path.
+    void pop_back() {
+        _data.pop_back();
+    }
+    iterator insert(iterator pos, Path const &p) {
+        return _data.insert(pos, p);
+    }
+    template <typename InputIter>
+    void insert(iterator out, InputIter first, InputIter last) {
+        _data.insert(out, first, last);
+    }
+    /// Remove a path from the vector.
+    iterator erase(iterator i) {
+        return _data.erase(i);
+    }
+    /// Remove a range of paths from the vector.
+    iterator erase(iterator first, iterator last) {
+        return _data.erase(first, last);
+    }
+    /// Remove all paths from the vector.
+    void clear() { _data.clear(); }
+    /** @brief Change the number of paths.
+     * If the vector size increases, it is passed with paths that contain only
+     * a degenerate closing segment at (0,0). */
+    void resize(size_type n) { _data.resize(n); }
+    /** @brief Reverse the direction of paths in the vector.
+     * @param reverse_paths If this is true, the order of paths is reversed as well;
+     *                      otherwise each path is reversed, but their order in the
+     *                      PathVector stays the same */
+    void reverse(bool reverse_paths = true);
+    /** @brief Get a new vector with reversed direction of paths.
+     * @param reverse_paths If this is true, the order of paths is reversed as well;
+     *                      otherwise each path is reversed, but their order in the
+     *                      PathVector stays the same */
+    PathVector reversed(bool reverse_paths = true) const;
+
+    /// Get the range of allowed time values.
+    Interval timeRange() const {
+        Interval ret(0, curveCount()); return ret;
+    }
+    /** @brief Get the first point in the first path of the vector.
+     * This method will throw an exception if the vector doesn't contain any paths. */
+    Point initialPoint() const {
+        return _data.front().initialPoint();
+    }
+    /** @brief Get the last point in the last path of the vector.
+     * This method will throw an exception if the vector doesn't contain any paths. */
+    Point finalPoint() const {
+        return _data.back().finalPoint();
+    }
+    /** @brief Get all intersections of the path-vector with itself. This includes both
+     *  self-intersections of constituent paths and intersections between different paths. */
+    std::vector<PathVectorIntersection> intersectSelf(Coord precision = EPSILON) const;
+    Path &pathAt(Coord t, Coord *rest = NULL);
+    Path const &pathAt(Coord t, Coord *rest = NULL) const;
+    Curve const &curveAt(Coord t, Coord *rest = NULL) const;
+    Coord valueAt(Coord t, Dim2 d) const;
+    Point pointAt(Coord t) const;
+
+    Path &pathAt(PathVectorTime const &pos) {
+        return const_cast<Path &>(static_cast<PathVector const*>(this)->pathAt(pos));
+    }
+    Path const &pathAt(PathVectorTime const &pos) const {
+        return at(pos.path_index);
+    }
+    Curve const &curveAt(PathVectorTime const &pos) const {
+        return at(pos.path_index).at(pos.curve_index);
+    }
+    Point pointAt(PathVectorTime const &pos) const {
+        return at(pos.path_index).at(pos.curve_index).pointAt(pos.t);
+    }
+    Coord valueAt(PathVectorTime const &pos, Dim2 d) const {
+        return at(pos.path_index).at(pos.curve_index).valueAt(pos.t, d);
+    }
+
+    OptRect boundsFast() const;
+    OptRect boundsExact() const;
+
+    template <typename T>
+    BOOST_CONCEPT_REQUIRES(((TransformConcept<T>)), (PathVector &))
+    operator*=(T const &t) {
+        if (empty()) return *this;
+        for (auto & i : *this) {
+            i *= t;
+        }
+        return *this;
+    }
+
+    bool operator==(PathVector const &other) const {
+        return boost::range::equal(_data, other._data);
+    }
+
+    void snapEnds(Coord precision = EPSILON);
+
+    std::vector<PVIntersection> intersect(PathVector const &other, Coord precision = EPSILON) const;
+
+    /** @brief Determine the winding number at the specified point.
+     * This is simply the sum of winding numbers for constituent paths. */
+    int winding(Point const &p) const;
+
+    std::optional<PathVectorTime> nearestTime(Point const &p, Coord *dist = NULL) const;
+    std::vector<PathVectorTime> allNearestTimes(Point const &p, Coord *dist = NULL) const;
+
+    std::vector<Point> nodes() const;
+
+private:
+    PathVectorTime _factorTime(Coord t) const;
+
+    Sequence _data;
+};
+
+inline OptRect bounds_fast(PathVector const &pv) { return pv.boundsFast(); }
+inline OptRect bounds_exact(PathVector const &pv) { return pv.boundsExact(); }
+
+std::ostream &operator<<(std::ostream &out, PathVector const &pv);
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_PATHVECTOR_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/piecewise.h b/include/2geom/piecewise.h
new file mode 100644
index 0000000..e34df15
--- /dev/null
+++ b/include/2geom/piecewise.h
@@ -0,0 +1,945 @@
+/** @file
+ * @brief Piecewise function class
+ *//*
+ * Copyright 2007 Michael Sloan <mgsloan@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_PIECEWISE_H
+#define LIB2GEOM_SEEN_PIECEWISE_H
+
+#include <vector>
+#include <map>
+#include <utility>
+#include <boost/concept_check.hpp>
+#include <2geom/concepts.h>
+#include <2geom/math-utils.h>
+#include <2geom/sbasis.h>
+
+
+namespace Geom {
+/**
+ * @brief Function defined as discrete pieces.
+ *
+ * The Piecewise class manages a sequence of elements of a type as segments and
+ * the ’cuts’ between them. These cuts are time values which separate the pieces.
+ * This function representation allows for more interesting functions, as it provides
+ * a viable output for operations such as inversion, which may require multiple
+ * SBasis to properly invert the original.
+ *
+ * As for technical details, while the actual SBasis segments begin on the first
+ * cut and end on the last, the function is defined throughout all inputs by ex-
+ * tending the first and last segments. The exact switching between segments is
+ * arbitrarily such that beginnings (t=0) have preference over endings (t=1). This
+ * only matters if it is discontinuous at the location.
+ * \f[
+ *      f(t) \rightarrow \left\{ 
+ *      \begin{array}{cc}
+ *      s_1,& t <= c_2 \\
+ *      s_2,& c_2 <= t <= c_3\\
+ *      \ldots \\
+ *      s_n,& c_n <= t
+ *      \end{array}\right.
+ * \f]
+ *
+ * @ingroup Fragments
+ */
+template <typename T>
+class Piecewise {
+  BOOST_CLASS_REQUIRE(T, Geom, FragmentConcept);
+
+  public:
+    std::vector<double> cuts;
+    std::vector<T> segs;
+    //segs[i] stretches from cuts[i] to cuts[i+1].
+
+    Piecewise() {}
+
+    explicit Piecewise(const T &s) {
+        push_cut(0.);
+        push_seg(s);
+        push_cut(1.);
+    }
+
+    unsigned input_dim(){return 1;}
+
+    typedef typename T::output_type output_type;
+
+    explicit Piecewise(const output_type & v) {
+        push_cut(0.);
+        push_seg(T(v));
+        push_cut(1.);
+    }
+
+    inline void reserve(unsigned i) { segs.reserve(i); cuts.reserve(i + 1); }
+
+    inline T const& operator[](unsigned i) const { return segs[i]; }
+    inline T&       operator[](unsigned i)       { return segs[i]; }
+    inline output_type operator()(double t) const { return valueAt(t); }
+    inline output_type valueAt(double t) const {
+        unsigned n = segN(t);
+        return segs[n](segT(t, n));
+    }
+    inline output_type firstValue() const {
+        return valueAt(cuts.front());
+    }
+    inline output_type lastValue() const {
+        return valueAt(cuts.back());
+    }
+
+    /**
+    *  The size of the returned vector equals n_derivs+1.
+    */
+    std::vector<output_type> valueAndDerivatives(double t, unsigned n_derivs) const {
+        unsigned n = segN(t);
+        std::vector<output_type> ret, val = segs[n].valueAndDerivatives(segT(t, n), n_derivs);
+        double mult = 1;
+        for(unsigned i = 0; i < val.size(); i++) {
+            ret.push_back(val[i] * mult);
+            mult /= cuts[n+1] - cuts[n];
+        }
+        return ret;
+    }
+
+    //TODO: maybe it is not a good idea to have this?
+    Piecewise<T> operator()(SBasis f);
+    Piecewise<T> operator()(Piecewise<SBasis>f);
+
+    inline unsigned size() const { return segs.size(); }
+    inline bool empty() const { return segs.empty(); }
+    inline void clear() {
+        segs.clear();
+        cuts.clear();
+    }
+
+    /**Convenience/implementation hiding function to add segment/cut pairs.
+     * Asserts that basic size and order invariants are correct
+     */
+    inline void push(const T &s, double to) {
+        assert(cuts.size() - segs.size() == 1);
+        push_seg(s);
+        push_cut(to);
+    }
+    inline void push(T &&s, double to) {
+        assert(cuts.size() - segs.size() == 1);
+        push_seg(s);
+        push_cut(to);
+    }
+    //Convenience/implementation hiding function to add cuts.
+    inline void push_cut(double c) {
+        ASSERT_INVARIANTS(cuts.empty() || c > cuts.back());
+        cuts.push_back(c);
+    }
+    //Convenience/implementation hiding function to add segments.
+    inline void push_seg(const T &s) { segs.push_back(s); }
+    inline void push_seg(T &&s) { segs.emplace_back(s); }
+
+    /**Returns the segment index which corresponds to a 'global' piecewise time.
+     * Also takes optional low/high parameters to expedite the search for the segment.
+     */
+    inline unsigned segN(double t, int low = 0, int high = -1) const {
+        high = (high == -1) ? size() : high;
+        if(t < cuts[0]) return 0;
+        if(t >= cuts[size()]) return size() - 1;
+        while(low < high) {
+            int mid = (high + low) / 2; //Lets not plan on having huge (> INT_MAX / 2) cut sequences
+            double mv = cuts[mid];
+            if(mv < t) {
+                if(t < cuts[mid + 1]) return mid; else low = mid + 1;
+            } else if(t < mv) {
+                if(cuts[mid - 1] < t) return mid - 1; else high = mid - 1;
+            } else {
+                return mid;
+            }
+        }
+        return low;
+    }
+
+    /**Returns the time within a segment, given the 'global' piecewise time.
+     * Also takes an optional index parameter which may be used for efficiency or to find the time on a
+     * segment outside its range.  If it is left to its default, -1, it will call segN to find the index.
+     */
+    inline double segT(double t, int i = -1) const {
+        if(i == -1) i = segN(t);
+        assert(i >= 0);
+        return (t - cuts[i]) / (cuts[i+1] - cuts[i]);
+    }
+
+    inline double mapToDomain(double t, unsigned i) const {
+        return (1-t)*cuts[i] + t*cuts[i+1]; //same as: t * (cuts[i+1] - cuts[i]) + cuts[i]
+    }
+
+    //Offsets the piecewise domain
+    inline void offsetDomain(double o) {
+        assert(std::isfinite(o));
+        if(o != 0)
+            for(unsigned i = 0; i <= size(); i++)
+                cuts[i] += o;
+    }
+
+    //Scales the domain of the function by a value. 0 will result in an empty Piecewise.
+    inline void scaleDomain(double s) {
+        assert(s > 0);
+        if(s == 0) {
+            cuts.clear(); segs.clear();
+            return;
+        }
+        for(unsigned i = 0; i <= size(); i++)
+            cuts[i] *= s;
+    }
+
+    //Retrieves the domain in interval form
+    inline Interval domain() const { return Interval(cuts.front(), cuts.back()); }
+
+    //Transforms the domain into another interval
+    inline void setDomain(Interval dom) {
+        if(empty()) return;
+        /* dom can not be empty
+        if(dom.empty()) {
+            cuts.clear(); segs.clear();
+            return;
+        }*/
+        double cf = cuts.front();
+        double o = dom.min() - cf, s = dom.extent() / (cuts.back() - cf);
+        for(unsigned i = 0; i <= size(); i++)
+            cuts[i] = (cuts[i] - cf) * s + o;
+        //fix floating point precision errors.
+        cuts[0] = dom.min();
+        cuts[size()] = dom.max();
+    }
+
+    //Concatenates this Piecewise function with another, offsetting time of the other to match the end.
+    inline void concat(const Piecewise<T> &other) {
+        if(other.empty()) return;
+
+        if(empty()) {
+            cuts = other.cuts; segs = other.segs;
+            return;
+        }
+
+        segs.insert(segs.end(), other.segs.begin(), other.segs.end());
+        double t = cuts.back() - other.cuts.front();
+        cuts.reserve(cuts.size() + other.size());
+        for(unsigned i = 0; i < other.size(); i++)
+            push_cut(other.cuts[i + 1] + t);
+    }
+
+    //Like concat, but ensures continuity.
+    inline void continuousConcat(const Piecewise<T> &other) {
+        boost::function_requires<AddableConcept<typename T::output_type> >();
+        if(other.empty()) return;
+
+        if(empty()) { segs = other.segs; cuts = other.cuts; return; }
+
+        typename T::output_type y = segs.back().at1() - other.segs.front().at0();
+        double t = cuts.back() - other.cuts.front();
+        reserve(size() + other.size());
+        for(unsigned i = 0; i < other.size(); i++)
+            push(other[i] + y, other.cuts[i + 1] + t);
+    }
+
+    //returns true if the Piecewise<T> meets some basic invariants.
+    inline bool invariants() const {
+        // segs between cuts
+        if(!(segs.size() + 1 == cuts.size() || (segs.empty() && cuts.empty())))
+            return false;
+        // cuts in order
+        for(unsigned i = 0; i < segs.size(); i++)
+            if(cuts[i] >= cuts[i+1])
+                return false;
+        return true;
+    }
+
+};
+
+/**
+ *  ...
+ *  \return ...
+ *  \relates Piecewise
+ */
+template<typename T>
+inline typename FragmentConcept<T>::BoundsType bounds_fast(const Piecewise<T> &f) {
+    boost::function_requires<FragmentConcept<T> >();
+
+    if(f.empty()) return typename FragmentConcept<T>::BoundsType();
+    typename FragmentConcept<T>::BoundsType ret(bounds_fast(f[0]));
+    for(unsigned i = 1; i < f.size(); i++)
+        ret.unionWith(bounds_fast(f[i]));
+    return ret;
+}
+
+/**
+ *  ...
+ *  \return ...
+ *  \relates Piecewise
+ */
+template<typename T>
+inline typename FragmentConcept<T>::BoundsType bounds_exact(const Piecewise<T> &f) {
+    boost::function_requires<FragmentConcept<T> >();
+
+    if(f.empty()) return typename FragmentConcept<T>::BoundsType();
+    typename FragmentConcept<T>::BoundsType ret(bounds_exact(f[0]));
+    for(unsigned i = 1; i < f.size(); i++)
+        ret.unionWith(bounds_exact(f[i]));
+    return ret;
+}
+
+/**
+ *  ...
+ *  \return ...
+ *  \relates Piecewise
+ */
+template<typename T>
+inline typename FragmentConcept<T>::BoundsType bounds_local(const Piecewise<T> &f, const OptInterval &_m) {
+    boost::function_requires<FragmentConcept<T> >();
+
+    if(f.empty() || !_m) return typename FragmentConcept<T>::BoundsType();
+    Interval const &m = *_m;
+    if(m.isSingular()) return typename FragmentConcept<T>::BoundsType(f(m.min()));
+
+    unsigned fi = f.segN(m.min()), ti = f.segN(m.max());
+    double ft = f.segT(m.min(), fi), tt = f.segT(m.max(), ti);
+
+    if(fi == ti) return bounds_local(f[fi], Interval(ft, tt));
+
+    typename FragmentConcept<T>::BoundsType ret(bounds_local(f[fi], Interval(ft, 1.)));
+    for(unsigned i = fi + 1; i < ti; i++)
+        ret.unionWith(bounds_exact(f[i]));
+    if(tt != 0.) ret.unionWith(bounds_local(f[ti], Interval(0., tt)));
+
+    return ret;
+}
+
+/**
+ *  Returns a portion of a piece of a Piecewise<T>, given the piece's index and a to/from time.
+ *  \relates Piecewise
+ */
+template<typename T>
+T elem_portion(const Piecewise<T> &a, unsigned i, double from, double to) {
+    assert(i < a.size());
+    double rwidth = 1 / (a.cuts[i+1] - a.cuts[i]);
+    return portion( a[i], (from - a.cuts[i]) * rwidth, (to - a.cuts[i]) * rwidth );
+}
+
+/**Piecewise<T> partition(const Piecewise<T> &pw, std::vector<double> const &c);
+ * Further subdivides the Piecewise<T> such that there is a cut at every value in c.
+ * Precondition: c sorted lower to higher.
+ *
+ * //Given Piecewise<T> a and b:
+ * Piecewise<T> ac = a.partition(b.cuts);
+ * Piecewise<T> bc = b.partition(a.cuts);
+ * //ac.cuts should be equivalent to bc.cuts
+ *
+ * \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> partition(const Piecewise<T> &pw, std::vector<double> const &c) {
+    assert(pw.invariants());
+    if(c.empty()) return Piecewise<T>(pw);
+
+    Piecewise<T> ret = Piecewise<T>();
+    ret.reserve(c.size() + pw.cuts.size() - 1);
+
+    if(pw.empty()) {
+        ret.cuts = c;
+        for(unsigned i = 0; i < c.size() - 1; i++)
+            ret.push_seg(T());
+        return ret;
+    }
+
+    unsigned si = 0, ci = 0;     //Segment index, Cut index
+
+    //if the cuts have something earlier than the Piecewise<T>, add portions of the first segment
+    while(ci < c.size() && c[ci] < pw.cuts.front()) {
+        bool isLast = (ci == c.size()-1 || c[ci + 1] >= pw.cuts.front());
+        ret.push_cut(c[ci]);
+        ret.push_seg( elem_portion(pw, 0, c[ci], isLast ? pw.cuts.front() : c[ci + 1]) );
+        ci++;
+    }
+
+    ret.push_cut(pw.cuts[0]);
+    double prev = pw.cuts[0];    //previous cut
+    //Loop which handles cuts within the Piecewise<T> domain
+    //Should have the cuts = segs + 1 invariant
+    while(si < pw.size() && ci <= c.size()) {
+        if(ci == c.size() && prev <= pw.cuts[si]) { //cuts exhausted, straight copy the rest
+            ret.segs.insert(ret.segs.end(), pw.segs.begin() + si, pw.segs.end());
+            ret.cuts.insert(ret.cuts.end(), pw.cuts.begin() + si + 1, pw.cuts.end());
+            return ret;
+        }else if(ci == c.size() || c[ci] >= pw.cuts[si + 1]) {  //no more cuts within this segment, finalize
+            if(prev > pw.cuts[si]) {      //segment already has cuts, so portion is required
+                ret.push_seg(portion(pw[si], pw.segT(prev, si), 1.0));
+            } else {                     //plain copy is fine
+                ret.push_seg(pw[si]);
+            }
+            ret.push_cut(pw.cuts[si + 1]);
+            prev = pw.cuts[si + 1];
+            si++;
+        } else if(c[ci] == pw.cuts[si]){                  //coincident
+            //Already finalized the seg with the code immediately above
+            ci++;
+        } else {                                         //plain old subdivision
+            ret.push(elem_portion(pw, si, prev, c[ci]), c[ci]);
+            prev = c[ci];
+            ci++;
+        }
+    }
+
+    //input cuts extend further than this Piecewise<T>, extend the last segment.
+    while(ci < c.size()) {
+        if(c[ci] > prev) {
+            ret.push(elem_portion(pw, pw.size() - 1, prev, c[ci]), c[ci]);
+            prev = c[ci];
+        }
+        ci++;
+    }
+    return ret;
+}
+
+/**
+ *  Returns a Piecewise<T> with a defined domain of [min(from, to), max(from, to)].
+ *  \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> portion(const Piecewise<T> &pw, double from, double to) {
+    if(pw.empty() || from == to) return Piecewise<T>();
+
+    Piecewise<T> ret;
+
+    double temp = from;
+    from = std::min(from, to);
+    to = std::max(temp, to);
+
+    unsigned i = pw.segN(from);
+    ret.push_cut(from);
+    if(i == pw.size() - 1 || to <= pw.cuts[i + 1]) {    //to/from inhabit the same segment
+        ret.push(elem_portion(pw, i, from, to), to);
+        return ret;
+    }
+    ret.push_seg(portion( pw[i], pw.segT(from, i), 1.0 ));
+    i++;
+    unsigned fi = pw.segN(to, i);
+    ret.reserve(fi - i + 1);
+    if (to == pw.cuts[fi]) fi-=1;
+
+    ret.segs.insert(ret.segs.end(), pw.segs.begin() + i, pw.segs.begin() + fi);  //copy segs
+    ret.cuts.insert(ret.cuts.end(), pw.cuts.begin() + i, pw.cuts.begin() + fi + 1);  //and their cuts
+
+    ret.push_seg( portion(pw[fi], 0.0, pw.segT(to, fi)));
+    if(to != ret.cuts.back()) ret.push_cut(to);
+    ret.invariants();
+    return ret;
+}
+
+//TODO: seems like these should be mutating
+/**
+ *  ...
+ *  \return ...
+ *  \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> remove_short_cuts(Piecewise<T> const &f, double tol) {
+    if(f.empty()) return f;
+    Piecewise<T> ret;
+    ret.reserve(f.size());
+    ret.push_cut(f.cuts[0]);
+    for(unsigned i=0; i<f.size(); i++){
+        if (f.cuts[i+1]-f.cuts[i] >= tol || i==f.size()-1) {
+            ret.push(f[i], f.cuts[i+1]);
+        }
+    }
+    return ret;
+}
+
+//TODO: seems like these should be mutating
+/**
+ *  ...
+ *  \return ...
+ *  \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> remove_short_cuts_extending(Piecewise<T> const &f, double tol) {
+    if(f.empty()) return f;
+    Piecewise<T> ret;
+    ret.reserve(f.size());
+    ret.push_cut(f.cuts[0]);
+    double last = f.cuts[0]; // last cut included
+    for(unsigned i=0; i<f.size(); i++){
+        if (f.cuts[i+1]-f.cuts[i] >= tol) {
+            ret.push(elem_portion(f, i, last, f.cuts[i+1]), f.cuts[i+1]);
+            last = f.cuts[i+1];
+        }
+    }
+    return ret;
+}
+
+/**
+ *  ...
+ *  \return ...
+ *  \relates Piecewise
+ */
+template<typename T>
+std::vector<double> roots(const Piecewise<T> &pw) {
+    std::vector<double> ret;
+    for(unsigned i = 0; i < pw.size(); i++) {
+        std::vector<double> sr = roots(pw[i]);
+        for (double & j : sr) ret.push_back(j * (pw.cuts[i + 1] - pw.cuts[i]) + pw.cuts[i]);
+
+    }
+    return ret;
+}
+
+//IMPL: OffsetableConcept
+/**
+ *  ...
+ *  \return \f$ a + b = \f$
+ *  \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> operator+(Piecewise<T> const &a, typename T::output_type b) {
+    boost::function_requires<OffsetableConcept<T> >();
+//TODO:empty
+    Piecewise<T> ret;
+    ret.segs.reserve(a.size());
+    ret.cuts = a.cuts;
+    for(unsigned i = 0; i < a.size();i++)
+        ret.push_seg(a[i] + b);
+    return ret;
+}
+template<typename T>
+Piecewise<T> operator-(Piecewise<T> const &a, typename T::output_type b) {
+    boost::function_requires<OffsetableConcept<T> >();
+//TODO: empty
+    Piecewise<T> ret;
+    ret.segs.reserve(a.size());
+    ret.cuts = a.cuts;
+    for(unsigned i = 0; i < a.size();i++)
+        ret.push_seg(a[i] - b);
+    return ret;
+}
+template<typename T>
+Piecewise<T>& operator+=(Piecewise<T>& a, typename T::output_type b) {
+    boost::function_requires<OffsetableConcept<T> >();
+
+    if(a.empty()) { a.push_cut(0.); a.push(T(b), 1.); return a; }
+
+    for(unsigned i = 0; i < a.size();i++)
+        a[i] += b;
+    return a;
+}
+template<typename T>
+Piecewise<T>& operator-=(Piecewise<T>& a, typename T::output_type b) {
+    boost::function_requires<OffsetableConcept<T> >();
+
+    if(a.empty()) { a.push_cut(0.); a.push(T(-b), 1.); return a; }
+
+    for(unsigned i = 0;i < a.size();i++)
+        a[i] -= b;
+    return a;
+}
+
+//IMPL: ScalableConcept
+/**
+ *  ...
+ *  \return \f$ -a = \f$
+ *  \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> operator-(Piecewise<T> const &a) {
+    boost::function_requires<ScalableConcept<T> >();
+
+    Piecewise<T> ret;
+    ret.segs.reserve(a.size());
+    ret.cuts = a.cuts;
+    for(unsigned i = 0; i < a.size();i++)
+        ret.push_seg(- a[i]);
+    return ret;
+}
+/**
+ *  ...
+ *  \return \f$ a * b = \f$
+ *  \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> operator*(Piecewise<T> const &a, double b) {
+    boost::function_requires<ScalableConcept<T> >();
+
+    if(a.empty()) return Piecewise<T>();
+
+    Piecewise<T> ret;
+    ret.segs.reserve(a.size());
+    ret.cuts = a.cuts;
+    for(unsigned i = 0; i < a.size();i++)
+        ret.push_seg(a[i] * b);
+    return ret;
+}
+/**
+ *  ...
+ *  \return \f$ a * b = \f$
+ *  \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> operator*(Piecewise<T> const &a, T b) {
+    boost::function_requires<ScalableConcept<T> >();
+
+    if(a.empty()) return Piecewise<T>();
+
+    Piecewise<T> ret;
+    ret.segs.reserve(a.size());
+    ret.cuts = a.cuts;
+    for(unsigned i = 0; i < a.size();i++)
+        ret.push_seg(a[i] * b);
+    return ret;
+}
+/**
+ *  ...
+ *  \return \f$ a / b = \f$
+ *  \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> operator/(Piecewise<T> const &a, double b) {
+    boost::function_requires<ScalableConcept<T> >();
+
+    //FIXME: b == 0?
+    if(a.empty()) return Piecewise<T>();
+
+    Piecewise<T> ret;
+    ret.segs.reserve(a.size());
+    ret.cuts = a.cuts;
+    for(unsigned i = 0; i < a.size();i++)
+        ret.push_seg(a[i] / b);
+    return ret;
+}
+template<typename T>
+Piecewise<T>& operator*=(Piecewise<T>& a, double b) {
+    boost::function_requires<ScalableConcept<T> >();
+
+    for(unsigned i = 0; i < a.size();i++)
+        a[i] *= b;
+    return a;
+}
+template<typename T>
+Piecewise<T>& operator/=(Piecewise<T>& a, double b) {
+    boost::function_requires<ScalableConcept<T> >();
+
+    //FIXME: b == 0?
+
+    for(unsigned i = 0; i < a.size();i++)
+        a[i] /= b;
+    return a;
+}
+
+//IMPL: AddableConcept
+/**
+ *  ...
+ *  \return \f$ a + b = \f$
+ *  \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> operator+(Piecewise<T> const &a, Piecewise<T> const &b) {
+    boost::function_requires<AddableConcept<T> >();
+
+    Piecewise<T> pa = partition(a, b.cuts), pb = partition(b, a.cuts);
+    Piecewise<T> ret;
+    assert(pa.size() == pb.size());
+    ret.segs.reserve(pa.size());
+    ret.cuts = pa.cuts;
+    for (unsigned i = 0; i < pa.size(); i++)
+        ret.push_seg(pa[i] + pb[i]);
+    return ret;
+}
+/**
+ *  ...
+ *  \return \f$ a - b = \f$
+ *  \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> operator-(Piecewise<T> const &a, Piecewise<T> const &b) {
+    boost::function_requires<AddableConcept<T> >();
+
+    Piecewise<T> pa = partition(a, b.cuts), pb = partition(b, a.cuts);
+    Piecewise<T> ret = Piecewise<T>();
+    assert(pa.size() == pb.size());
+    ret.segs.reserve(pa.size());
+    ret.cuts = pa.cuts;
+    for (unsigned i = 0; i < pa.size(); i++)
+        ret.push_seg(pa[i] - pb[i]);
+    return ret;
+}
+template<typename T>
+inline Piecewise<T>& operator+=(Piecewise<T> &a, Piecewise<T> const &b) {
+    a = a+b;
+    return a;
+}
+template<typename T>
+inline Piecewise<T>& operator-=(Piecewise<T> &a, Piecewise<T> const &b) {
+    a = a-b;
+    return a;
+}
+
+/**
+ *  ...
+ *  \return \f$ a \cdot b = \f$
+ *  \relates Piecewise
+ */
+template<typename T1,typename T2>
+Piecewise<T2> operator*(Piecewise<T1> const &a, Piecewise<T2> const &b) {
+    //function_requires<MultiplicableConcept<T1> >();
+    //function_requires<MultiplicableConcept<T2> >();
+
+    Piecewise<T1> pa = partition(a, b.cuts);
+    Piecewise<T2> pb = partition(b, a.cuts);
+    Piecewise<T2> ret = Piecewise<T2>();
+    assert(pa.size() == pb.size());
+    ret.segs.reserve(pa.size());
+    ret.cuts = pa.cuts;
+    for (unsigned i = 0; i < pa.size(); i++)
+        ret.push_seg(pa[i] * pb[i]);
+    return ret;
+}
+
+/**
+ *  ...
+ *  \return \f$ a \cdot b \f$
+ *  \relates Piecewise
+ */
+template<typename T>
+inline Piecewise<T>& operator*=(Piecewise<T> &a, Piecewise<T> const &b) {
+    a = a * b;
+    return a;
+}
+
+Piecewise<SBasis> divide(Piecewise<SBasis> const &a, Piecewise<SBasis> const &b, unsigned k);
+//TODO: replace divide(a,b,k) by divide(a,b,tol,k)?
+//TODO: atm, relative error is ~(tol/a)%. Find a way to make it independent of a.
+//Nota: the result is 'truncated' where b is smaller than 'zero': ~ a/max(b,zero).
+Piecewise<SBasis>
+divide(Piecewise<SBasis> const &a, Piecewise<SBasis> const &b, double tol, unsigned k, double zero=1.e-3);
+Piecewise<SBasis>
+divide(SBasis const &a, Piecewise<SBasis> const &b, double tol, unsigned k, double zero=1.e-3);
+Piecewise<SBasis>
+divide(Piecewise<SBasis> const &a, SBasis const &b, double tol, unsigned k, double zero=1.e-3);
+Piecewise<SBasis>
+divide(SBasis const &a, SBasis const &b, double tol, unsigned k, double zero=1.e-3);
+
+//Composition: functions called compose_* are pieces of compose that are factored out in pw.cpp.
+std::map<double,unsigned> compose_pullback(std::vector<double> const &cuts, SBasis const &g);
+int compose_findSegIdx(std::map<double,unsigned>::iterator  const &cut,
+                       std::map<double,unsigned>::iterator  const &next,
+                       std::vector<double>  const &levels,
+                       SBasis const &g);
+
+/**
+ *  ...
+ *  \return ...
+ *  \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> compose(Piecewise<T> const &f, SBasis const &g){
+    /// \todo add concept check
+    Piecewise<T> result;
+    if (f.empty()) return result;
+    if (g.isZero()) return Piecewise<T>(f(0));
+    if (f.size()==1){
+        double t0 = f.cuts[0], width = f.cuts[1] - t0;
+        return (Piecewise<T>) compose(f.segs[0],compose(Linear(-t0 / width, (1-t0) / width), g));
+    }
+
+    //first check bounds...
+    Interval bs = *bounds_fast(g);
+    if (f.cuts.front() > bs.max()  || bs.min() > f.cuts.back()){
+        int idx = (bs.max() < f.cuts[1]) ? 0 : f.cuts.size()-2;
+        double t0 = f.cuts[idx], width = f.cuts[idx+1] - t0;
+        return (Piecewise<T>) compose(f.segs[idx],compose(Linear(-t0 / width, (1-t0) / width), g));
+    }
+
+    std::vector<double> levels;//we can forget first and last cuts...
+    levels.insert(levels.begin(),f.cuts.begin()+1,f.cuts.end()-1);
+    //TODO: use a std::vector<pairs<double,unsigned> > instead of a map<double,unsigned>.
+    std::map<double,unsigned> cuts_pb = compose_pullback(levels,g);
+
+    //-- Compose each piece of g with the relevant seg of f.
+    result.cuts.push_back(0.);
+    std::map<double,unsigned>::iterator cut=cuts_pb.begin();
+    std::map<double,unsigned>::iterator next=cut; next++;
+    while(next!=cuts_pb.end()){
+        //assert(std::abs(int((*cut).second-(*next).second))<1);
+        //TODO: find a way to recover from this error? the root finder missed some root;
+        //  the levels/variations of f might be too close/fast...
+        int idx = compose_findSegIdx(cut,next,levels,g);
+        double t0=(*cut).first;
+        double t1=(*next).first;
+
+        if (!are_near(t0,t1,EPSILON*EPSILON)) { // prevent adding cuts that are extremely close together and that may cause trouble with rounding e.g. when reversing the path
+            SBasis sub_g=compose(g, Linear(t0,t1));
+            sub_g=compose(Linear(-f.cuts[idx]/(f.cuts[idx+1]-f.cuts[idx]),
+                                 (1-f.cuts[idx])/(f.cuts[idx+1]-f.cuts[idx])),sub_g);
+            result.push(compose(f[idx],sub_g),t1);
+        }
+
+        cut++;
+        next++;
+    }
+    return(result);
+}
+
+/**
+ *  ...
+ *  \return ...
+ *  \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> compose(Piecewise<T> const &f, Piecewise<SBasis> const &g){
+/// \todo add concept check
+  Piecewise<T> result;
+  for(unsigned i = 0; i < g.segs.size(); i++){
+      Piecewise<T> fgi=compose(f, g.segs[i]);
+      fgi.setDomain(Interval(g.cuts[i], g.cuts[i+1]));
+      result.concat(fgi);
+  }
+  return result;
+}
+
+/*
+Piecewise<D2<SBasis> > compose(D2<SBasis2d> const &sb2d, Piecewise<D2<SBasis> > const &pwd2sb){
+/// \todo add concept check
+  Piecewise<D2<SBasis> > result;
+  result.push_cut(0.);
+  for(unsigned i = 0; i < pwd2sb.size(); i++){
+     result.push(compose_each(sb2d,pwd2sb[i]),i+1);
+  }
+  return result;
+}*/
+
+/** Compose an SBasis with the inverse of another.
+ * WARNING: It's up to the user to check that the second SBasis is indeed
+ * invertible (i.e. strictly increasing or decreasing).
+ *  \return \f$ f \cdot g^{-1} \f$
+ *  \relates Piecewise
+ */
+Piecewise<SBasis> pw_compose_inverse(SBasis const &f, SBasis const &g, unsigned order, double zero);
+
+
+
+template <typename T>
+Piecewise<T> Piecewise<T>::operator()(SBasis f){return compose((*this),f);}
+template <typename T>
+Piecewise<T> Piecewise<T>::operator()(Piecewise<SBasis>f){return compose((*this),f);}
+
+/**
+ *  ...
+ *  \return ...
+ *  \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> integral(Piecewise<T> const &a) {
+    Piecewise<T> result;
+    result.segs.resize(a.segs.size());
+    result.cuts = a.cuts;
+    typename T::output_type c = a.segs[0].at0();
+    for(unsigned i = 0; i < a.segs.size(); i++){
+        result.segs[i] = integral(a.segs[i])*(a.cuts[i+1]-a.cuts[i]);
+        result.segs[i]+= c-result.segs[i].at0();
+        c = result.segs[i].at1();
+    }
+    return result;
+}
+
+/**
+ *  ...
+ *  \return ...
+ *  \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> derivative(Piecewise<T> const &a) {
+    Piecewise<T> result;
+    result.segs.resize(a.segs.size());
+    result.cuts = a.cuts;
+    for(unsigned i = 0; i < a.segs.size(); i++){
+        result.segs[i] = derivative(a.segs[i])/(a.cuts[i+1]-a.cuts[i]);
+    }
+    return result;
+}
+
+std::vector<double> roots(Piecewise<SBasis> const &f);
+
+std::vector<std::vector<double> >multi_roots(Piecewise<SBasis> const &f, std::vector<double> const &values);
+
+//TODO: implement level_sets directly for pwsb instead of sb (and derive it fo sb).
+//It should be faster than the reverse as the algorithm may jump over full cut intervals.
+std::vector<Interval> level_set(Piecewise<SBasis> const &f, Interval const &level, double tol=1e-5);
+std::vector<Interval> level_set(Piecewise<SBasis> const &f, double v, double vtol, double tol=1e-5);
+//std::vector<Interval> level_sets(Piecewise<SBasis> const &f, std::vector<Interval> const &levels, double tol=1e-5);
+//std::vector<Interval> level_sets(Piecewise<SBasis> const &f, std::vector<double> &v, double vtol, double tol=1e-5);
+
+
+/**
+ *  ...
+ *  \return ...
+ *  \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> reverse(Piecewise<T> const &f) {
+    Piecewise<T> ret = Piecewise<T>();
+    ret.reserve(f.size());
+    double start = f.cuts[0];
+    double end = f.cuts.back();
+    for (unsigned i = 0; i < f.cuts.size(); i++) {
+        double x = f.cuts[f.cuts.size() - 1 - i];
+        ret.push_cut(end - (x - start));
+    }
+    for (unsigned i = 0; i < f.segs.size(); i++)
+        ret.push_seg(reverse(f[f.segs.size() - i - 1]));
+    return ret;
+}
+
+/**
+ *  Interpolates between a and b.
+ *  \return a if t = 0, b if t = 1, or an interpolation between a and b for t in [0,1]
+ *  \relates Piecewise
+ */
+template<typename T>
+Piecewise<T> lerp(double t, Piecewise<T> const &a, Piecewise<T> b) {
+    // Make sure both paths have the same number of segments and cuts at the same locations
+    b.setDomain(a.domain());
+    Piecewise<T> pA = partition(a, b.cuts);
+    Piecewise<T> pB = partition(b, a.cuts);
+
+    return (pA*(1-t)  +  pB*t);
+}
+
+}
+#endif //LIB2GEOM_SEEN_PIECEWISE_H
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/point.h b/include/2geom/point.h
new file mode 100644
index 0000000..3a29066
--- /dev/null
+++ b/include/2geom/point.h
@@ -0,0 +1,449 @@
+/** @file
+ * @brief Cartesian point / 2D vector and related operations
+ *//*
+ *  Authors:
+ *    Michael G. Sloan <mgsloan@gmail.com>
+ *    Nathan Hurst <njh@njhurst.com>
+ *    Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *
+ * Copyright (C) 2006-2009 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_POINT_H
+#define LIB2GEOM_SEEN_POINT_H
+
+#include <iostream>
+#include <iterator>
+#include <boost/operators.hpp>
+#include <2geom/forward.h>
+#include <2geom/coord.h>
+#include <2geom/int-point.h>
+#include <2geom/math-utils.h>
+#include <2geom/utils.h>
+
+namespace Geom {
+
+class Point
+    : boost::additive< Point
+    , boost::totally_ordered< Point
+    , boost::multiplicative< Point, Coord
+    , boost::multiplicative< Point
+    , boost::multiplicative< Point, IntPoint
+    , MultipliableNoncommutative< Point, Affine
+    , MultipliableNoncommutative< Point, Translate
+    , MultipliableNoncommutative< Point, Rotate
+    , MultipliableNoncommutative< Point, Scale
+    , MultipliableNoncommutative< Point, HShear
+    , MultipliableNoncommutative< Point, VShear
+    , MultipliableNoncommutative< Point, Zoom
+      > > > > > > > > > > > > // base class chaining, see documentation for Boost.Operator
+{
+    Coord _pt[2] = { 0, 0 };
+public:
+    using D1Value = Coord;
+    using D1Reference = Coord &;
+    using D1ConstReference = Coord const &;
+
+    /// @name Create points
+    /// @{
+    /** Construct a point at the origin. */
+    Point() = default;
+    /** Construct a point from its coordinates. */
+    Point(Coord x, Coord y)
+        : _pt{ x, y }
+    {}
+    /** Construct from integer point. */
+    Point(IntPoint const &p)
+        : Point(p[X], p[Y])
+    {}
+    /** @brief Construct a point from its polar coordinates.
+     * The angle is specified in radians, in the mathematical convention (increasing
+     * counter-clockwise from +X). */
+    static Point polar(Coord angle, Coord radius) {
+        Point ret(polar(angle));
+        ret *= radius;
+        return ret;
+    }
+    /** @brief Construct an unit vector from its angle.
+     * The angle is specified in radians, in the mathematical convention (increasing
+     * counter-clockwise from +X). */
+    static Point polar(Coord angle);
+    /// @}
+
+    /// @name Access the coordinates of a point
+    /// @{
+    Coord operator[](unsigned i) const { return _pt[i]; }
+    Coord &operator[](unsigned i) { return _pt[i]; }
+
+    Coord operator[](Dim2 d) const noexcept { return _pt[d]; }
+    Coord &operator[](Dim2 d) noexcept { return _pt[d]; }
+
+    Coord x() const noexcept { return _pt[X]; }
+    Coord &x() noexcept { return _pt[X]; }
+    Coord y() const noexcept { return _pt[Y]; }
+    Coord &y() noexcept { return _pt[Y]; }
+    /// @}
+
+    /// @name Vector operations
+    /// @{
+    /** @brief Compute the distance from origin.
+     * @return Length of the vector from origin to this point */
+    Coord length() const { return std::hypot(_pt[0], _pt[1]); }
+    void normalize();
+    Point normalized() const {
+        Point ret(*this);
+        ret.normalize();
+        return ret;
+    }
+
+    /** @brief Return a point like this point but rotated -90 degrees.
+     * If the y axis grows downwards and the x axis grows to the
+     * right, then this is 90 degrees counter-clockwise. */
+    Point ccw() const {
+        return Point(_pt[Y], -_pt[X]);
+    }
+
+    /** @brief Return a point like this point but rotated +90 degrees.
+     * If the y axis grows downwards and the x axis grows to the
+     * right, then this is 90 degrees clockwise. */
+    Point cw() const {
+        return Point(-_pt[Y], _pt[X]);
+    }
+    /// @}
+
+    /// @name Vector-like arithmetic operations
+    /// @{
+    Point operator-() const {
+        return Point(-_pt[X], -_pt[Y]);
+    }
+    Point &operator+=(Point const &o) {
+        _pt[X] += o._pt[X];
+        _pt[Y] += o._pt[Y];
+        return *this;
+    }
+    Point &operator-=(Point const &o) {
+        _pt[X] -= o._pt[X];
+        _pt[Y] -= o._pt[Y];
+        return *this;
+    }
+    Point &operator*=(Coord s) {
+        for (double & i : _pt) i *= s;
+        return *this;
+    }
+    Point &operator*=(Point const &o) {
+        _pt[X] *= o._pt[X];
+        _pt[Y] *= o._pt[Y];
+        return *this;
+    }
+    Point &operator*=(IntPoint const &o) {
+        _pt[X] *= o.x();
+        _pt[Y] *= o.y();
+        return *this;
+    }
+    Point &operator/=(Coord s) {
+        //TODO: s == 0?
+        for (double & i : _pt) i /= s;
+        return *this;
+    }
+    Point &operator/=(Point const &o) {
+        _pt[X] /= o._pt[X];
+        _pt[Y] /= o._pt[Y];
+        return *this;
+    }
+    Point &operator/=(IntPoint const &o) {
+        _pt[X] /= o.x();
+        _pt[Y] /= o.y();
+        return *this;
+    }
+    /// @}
+
+    /// @name Affine transformations
+    /// @{
+    Point &operator*=(Affine const &m);
+    // implemented in transforms.cpp
+    Point &operator*=(Translate const &t);
+    Point &operator*=(Scale const &s);
+    Point &operator*=(Rotate const &r);
+    Point &operator*=(HShear const &s);
+    Point &operator*=(VShear const &s);
+    Point &operator*=(Zoom const &z);
+    /// @}
+
+    /// @name Conversion to integer points
+    /// @{
+    /** @brief Round to nearest integer coordinates. */
+    IntPoint round() const {
+        IntPoint ret(::round(_pt[X]), ::round(_pt[Y]));
+        return ret;
+    }
+    /** @brief Round coordinates downwards. */
+    IntPoint floor() const {
+        IntPoint ret(::floor(_pt[X]), ::floor(_pt[Y]));
+        return ret;
+    }
+    /** @brief Round coordinates upwards. */
+    IntPoint ceil() const {
+        IntPoint ret(::ceil(_pt[X]), ::ceil(_pt[Y]));
+        return ret;
+    }
+    /// @}
+
+    /// @name Various utilities
+    /// @{
+    /** @brief Check whether both coordinates are finite. */
+    bool isFinite() const {
+        for (double i : _pt) {
+            if(!std::isfinite(i)) return false;
+        }
+        return true;
+    }
+    /** @brief Check whether both coordinates are zero. */
+    bool isZero() const {
+        return _pt[X] == 0 && _pt[Y] == 0;
+    }
+    /** @brief Check whether the length of the vector is close to 1. */
+    bool isNormalized(Coord eps=EPSILON) const {
+        return are_near(length(), 1.0, eps);
+    }
+    /** @brief Equality operator.
+     * This tests for exact identity (as opposed to are_near()). Note that due to numerical
+     * errors, this test might return false even if the points should be identical. */
+    bool operator==(const Point &in_pnt) const {
+        return (_pt[X] == in_pnt[X]) && (_pt[Y] == in_pnt[Y]);
+    }
+    /** @brief Lexicographical ordering for points.
+     * Y coordinate is regarded as more significant. When sorting according to this
+     * ordering, the points will be sorted according to the Y coordinate, and within
+     * points with the same Y coordinate according to the X coordinate. */
+    bool operator<(const Point &p) const {
+        return _pt[Y] < p[Y] || (_pt[Y] == p[Y] && _pt[X] < p[X]);
+    }
+    /// @}
+
+    /** @brief Lexicographical ordering functor.
+     * @param d The dimension with higher significance */
+    template <Dim2 DIM> struct LexLess;
+    template <Dim2 DIM> struct LexGreater;
+    //template <Dim2 DIM, typename First = std::less<Coord>, typename Second = std::less<Coord> > LexOrder;
+    /** @brief Lexicographical ordering functor with runtime dimension. */
+    struct LexLessRt {
+        LexLessRt(Dim2 d) : dim(d) {}
+        inline bool operator()(Point const &a, Point const &b) const;
+    private:
+        Dim2 dim;
+    };
+    struct LexGreaterRt {
+        LexGreaterRt(Dim2 d) : dim(d) {}
+        inline bool operator()(Point const &a, Point const &b) const;
+    private:
+        Dim2 dim;
+    };
+    //template <typename First = std::less<Coord>, typename Second = std::less<Coord> > LexOrder
+};
+
+/** @brief Output operator for points.
+ * Prints out the coordinates.
+ * @relates Point */
+std::ostream &operator<<(std::ostream &out, const Geom::Point &p);
+
+template<> struct Point::LexLess<X> {
+    typedef std::less<Coord> Primary;
+    typedef std::less<Coord> Secondary;
+    typedef std::less<Coord> XOrder;
+    typedef std::less<Coord> YOrder;
+    bool operator()(Point const &a, Point const &b) const {
+        return a[X] < b[X] || (a[X] == b[X] && a[Y] < b[Y]);
+    }
+};
+template<> struct Point::LexLess<Y> {
+    typedef std::less<Coord> Primary;
+    typedef std::less<Coord> Secondary;
+    typedef std::less<Coord> XOrder;
+    typedef std::less<Coord> YOrder;
+    bool operator()(Point const &a, Point const &b) const {
+        return a[Y] < b[Y] || (a[Y] == b[Y] && a[X] < b[X]);
+    }
+};
+template<> struct Point::LexGreater<X> {
+    typedef std::greater<Coord> Primary;
+    typedef std::greater<Coord> Secondary;
+    typedef std::greater<Coord> XOrder;
+    typedef std::greater<Coord> YOrder;
+    bool operator()(Point const &a, Point const &b) const {
+        return a[X] > b[X] || (a[X] == b[X] && a[Y] > b[Y]);
+    }
+};
+template<> struct Point::LexGreater<Y> {
+    typedef std::greater<Coord> Primary;
+    typedef std::greater<Coord> Secondary;
+    typedef std::greater<Coord> XOrder;
+    typedef std::greater<Coord> YOrder;
+    bool operator()(Point const &a, Point const &b) const {
+        return a[Y] > b[Y] || (a[Y] == b[Y] && a[X] > b[X]);
+    }
+};
+inline bool Point::LexLessRt::operator()(Point const &a, Point const &b) const {
+    return dim ? Point::LexLess<Y>()(a, b) : Point::LexLess<X>()(a, b);
+}
+inline bool Point::LexGreaterRt::operator()(Point const &a, Point const &b) const {
+    return dim ? Point::LexGreater<Y>()(a, b) : Point::LexGreater<X>()(a, b);
+}
+
+/** @brief Compute the second (Euclidean) norm of @a p.
+ * This corresponds to the length of @a p. The result will not overflow even if
+ * \f$p_X^2 + p_Y^2\f$ is larger that the maximum value that can be stored
+ * in a <code>double</code>.
+ * @return \f$\sqrt{p_X^2 + p_Y^2}\f$
+ * @relates Point */
+inline Coord L2(Point const &p) {
+    return p.length();
+}
+
+/** @brief Compute the square of the Euclidean norm of @a p.
+ * Warning: this can overflow where L2 won't.
+ * @return \f$p_X^2 + p_Y^2\f$
+ * @relates Point */
+inline Coord L2sq(Point const &p) {
+    return p[0]*p[0] + p[1]*p[1];
+}
+
+/** @brief Returns p * Geom::rotate_degrees(90), but more efficient.
+ *
+ * Angle direction in 2Geom: If you use the traditional mathematics convention that y
+ * increases upwards, then positive angles are anticlockwise as per the mathematics convention.  If
+ * you take the common non-mathematical convention that y increases downwards, then positive angles
+ * are clockwise, as is common outside of mathematics.
+ *
+ * There is no function to rotate by -90 degrees: use -rot90(p) instead.
+ * @relates Point */
+inline Point rot90(Point const &p) {
+    return Point(-p[Y], p[X]);
+}
+
+/** @brief Linear interpolation between two points.
+ * @param t Time value
+ * @param a First point
+ * @param b Second point
+ * @return Point on a line between a and b. The ratio of its distance from a
+ *         and the distance between a and b will be equal to t.
+ * @relates Point */
+inline Point lerp(Coord t, Point const &a, Point const &b) {
+    return (1 - t) * a + t * b;
+}
+
+/** @brief Return a point halfway between the specified ones.
+ * @relates Point */
+inline Point middle_point(Point const &p1, Point const &p2) {
+    return lerp(0.5, p1, p2);
+}
+
+/** @brief Compute the dot product of a and b.
+ * Dot product can be interpreted as a measure of how parallel the vectors are.
+ * For perpendicular vectors, it is zero. For parallel ones, its absolute value is highest,
+ * and the sign depends on whether they point in the same direction (+) or opposite ones (-).
+ * @return \f$a \cdot b = a_X b_X + a_Y b_Y\f$.
+ * @relates Point */
+inline Coord dot(Point const &a, Point const &b) {
+    return a[X] * b[X] + a[Y] * b[Y];
+}
+
+/** @brief Compute the 2D cross product.
+ * This is also known as "perp dot product". It will be zero for parallel vectors,
+ * and the absolute value will be highest for perpendicular vectors.
+ * @return \f$a \times b = a_X b_Y - a_Y b_X\f$.
+ * @relates Point*/
+inline Coord cross(Point const &a, Point const &b)
+{
+    // equivalent implementation:
+    // return dot(a, b.ccw());
+    return a[X] * b[Y] - a[Y] * b[X];
+}
+
+/// Compute the (Euclidean) distance between points.
+/// @relates Point
+inline Coord distance (Point const &a, Point const &b) {
+    return (a - b).length();
+}
+
+/// Compute the square of the distance between points.
+/// @relates Point
+inline Coord distanceSq (Point const &a, Point const &b) {
+    return L2sq(a - b);
+}
+
+//IMPL: NearConcept
+/// Test whether two points are no further apart than some threshold.
+/// @relates Point
+inline bool are_near(Point const &a, Point const &b, double eps = EPSILON) {
+    // do not use an unqualified calls to distance before the empty
+    // specialization of iterator_traits is defined - see end of file
+    return are_near((a - b).length(), 0, eps);
+}
+
+/// Test whether the relative distance between two points is less than some threshold.
+inline bool are_near_rel(Point const &a, Point const &b, double eps = EPSILON) {
+    return (a - b).length() <= eps * (a.length() + b.length()) / 2;
+}
+
+/// Test whether three points lie approximately on the same line.
+/// @relates Point
+inline bool are_collinear(Point const& p1, Point const& p2, Point const& p3,
+                          double eps = EPSILON)
+{
+    return are_near( cross(p3, p2) - cross(p3, p1) + cross(p2, p1), 0, eps);
+}
+
+Point unit_vector(Point const &a);
+Coord L1(Point const &p);
+Coord LInfty(Point const &p);
+bool is_zero(Point const &p);
+bool is_unit_vector(Point const &p, Coord eps = EPSILON);
+double atan2(Point const &p);
+double angle_between(Point const &a, Point const &b);
+Point abs(Point const &b);
+Point constrain_angle(Point const &A, Point const &B, unsigned int n = 4, Geom::Point const &dir = Geom::Point(1,0));
+
+} // end namespace Geom
+
+// This is required to fix a bug in GCC 4.3.3 (and probably others) that causes the compiler
+// to try to instantiate the iterator_traits template and fail. Probably it thinks that Point
+// is an iterator and tries to use std::distance instead of Geom::distance.
+namespace std {
+template <> class iterator_traits<Geom::Point> {};
+}
+
+#endif // LIB2GEOM_SEEN_POINT_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/polynomial.h b/include/2geom/polynomial.h
new file mode 100644
index 0000000..640cab6
--- /dev/null
+++ b/include/2geom/polynomial.h
@@ -0,0 +1,264 @@
+/**
+ * \file
+ * \brief Polynomial in canonical (monomial) basis
+ *//*
+ * Authors:
+ *    MenTaLguY <mental@rydia.net>
+ *    Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * 
+ * Copyright 2007-2015 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_POLY_H
+#define LIB2GEOM_SEEN_POLY_H
+#include <assert.h>
+#include <vector>
+#include <iostream>
+#include <algorithm>
+#include <complex>
+#include <2geom/coord.h>
+#include <2geom/utils.h>
+
+namespace Geom {
+
+/** @brief Polynomial in canonical (monomial) basis.
+ * @ingroup Fragments */
+class Poly : public std::vector<double>{
+public:
+    // coeff; // sum x^i*coeff[i]
+
+    //unsigned size() const { return coeff.size();}
+    unsigned degree() const { return size()-1;}
+
+    //double operator[](const int i) const { return (*this)[i];}
+    //double& operator[](const int i) { return (*this)[i];}
+
+    Poly operator+(const Poly& p) const {
+        Poly result;
+        const unsigned out_size = std::max(size(), p.size());
+        const unsigned min_size = std::min(size(), p.size());
+        result.reserve(out_size);
+
+        for(unsigned i = 0; i < min_size; i++) {
+            result.push_back((*this)[i] + p[i]);
+        }
+        for(unsigned i = min_size; i < size(); i++)
+            result.push_back((*this)[i]);
+        for(unsigned i = min_size; i < p.size(); i++)
+            result.push_back(p[i]);
+        assert(result.size() == out_size);
+        return result;
+    }
+    Poly operator-(const Poly& p) const {
+        Poly result;
+        const unsigned out_size = std::max(size(), p.size());
+        const unsigned min_size = std::min(size(), p.size());
+        result.reserve(out_size);
+
+        for(unsigned i = 0; i < min_size; i++) {
+            result.push_back((*this)[i] - p[i]);
+        }
+        for(unsigned i = min_size; i < size(); i++)
+            result.push_back((*this)[i]);
+        for(unsigned i = min_size; i < p.size(); i++)
+            result.push_back(-p[i]);
+        assert(result.size() == out_size);
+        return result;
+    }
+    Poly operator-=(const Poly& p) {
+        const unsigned out_size = std::max(size(), p.size());
+        const unsigned min_size = std::min(size(), p.size());
+        resize(out_size);
+
+        for(unsigned i = 0; i < min_size; i++) {
+            (*this)[i] -= p[i];
+        }
+        for(unsigned i = min_size; i < out_size; i++)
+            (*this)[i] = -p[i];
+        return *this;
+    }
+    Poly operator-(const double k) const {
+        Poly result;
+        const unsigned out_size = size();
+        result.reserve(out_size);
+
+        for(unsigned i = 0; i < out_size; i++) {
+            result.push_back((*this)[i]);
+        }
+        result[0] -= k;
+        return result;
+    }
+    Poly operator-() const {
+        Poly result;
+        result.resize(size());
+
+        for(unsigned i = 0; i < size(); i++) {
+            result[i] = -(*this)[i];
+        }
+        return result;
+    }
+    Poly operator*(const double p) const {
+        Poly result;
+        const unsigned out_size = size();
+        result.reserve(out_size);
+
+        for(unsigned i = 0; i < out_size; i++) {
+            result.push_back((*this)[i]*p);
+        }
+        assert(result.size() == out_size);
+        return result;
+    }
+    // equivalent to multiply by x^terms, negative terms are disallowed
+    Poly shifted(unsigned const terms) const {
+        Poly result;
+        size_type const out_size = size() + terms;
+        result.reserve(out_size);
+
+        result.resize(terms, 0.0);
+        result.insert(result.end(), this->begin(), this->end());
+
+        assert(result.size() == out_size);
+        return result;
+    }
+    Poly operator*(const Poly& p) const;
+
+    template <typename T>
+    T eval(T x) const {
+        T r = 0;
+        for(int k = size()-1; k >= 0; k--) {
+            r = r*x + T((*this)[k]);
+        }
+        return r;
+    }
+
+    template <typename T>
+    T operator()(T t) const { return (T)eval(t);}
+
+    void normalize();
+
+    void monicify();
+    Poly() {}
+    Poly(const Poly& p) : std::vector<double>(p) {}
+    Poly(const double a) {push_back(a);}
+
+public:
+    template <class T, class U>
+    void val_and_deriv(T x, U &pd) const {
+        pd[0] = back();
+        int nc = size() - 1;
+        int nd = pd.size() - 1;
+        for(unsigned j = 1; j < pd.size(); j++)
+            pd[j] = 0.0;
+        for(int i = nc -1; i >= 0; i--) {
+            int nnd = std::min(nd, nc-i);
+            for(int j = nnd; j >= 1; j--)
+                pd[j] = pd[j]*x + operator[](i);
+            pd[0] = pd[0]*x + operator[](i);
+        }
+        double cnst = 1;
+        for(int i = 2; i <= nd; i++) {
+            cnst *= i;
+            pd[i] *= cnst;
+        }
+    }
+
+    static Poly linear(double ax, double b) {
+        Poly p;
+        p.push_back(b);
+        p.push_back(ax);
+        return p;
+    }
+};
+
+inline Poly operator*(double a, Poly const & b) { return b * a;}
+
+Poly integral(Poly const & p);
+Poly derivative(Poly const & p);
+Poly divide_out_root(Poly const & p, double x);
+Poly compose(Poly const & a, Poly const & b);
+Poly divide(Poly const &a, Poly const &b, Poly &r);
+Poly gcd(Poly const &a, Poly const &b, const double tol=1e-10);
+
+/*** solve(Poly p)
+ * find all p.degree() roots of p.
+ * This function can take a long time with suitably crafted polynomials, but in practice it should be fast.  Should we provide special forms for degree() <= 4?
+ */
+std::vector<std::complex<double> > solve(const Poly & p);
+
+#ifdef HAVE_GSL
+/*** solve_reals(Poly p)
+ * find all real solutions to Poly p.
+ * currently we just use solve and pick out the suitably real looking values, there may be a better algorithm.
+ */
+std::vector<double> solve_reals(const Poly & p);
+#endif
+double polish_root(Poly const & p, double guess, double tol);
+
+
+/** @brief Analytically solve quadratic equation.
+ * The equation is given in the standard form: ax^2 + bx + c = 0.
+ * Only real roots are returned. */
+std::vector<Coord> solve_quadratic(Coord a, Coord b, Coord c);
+
+/** @brief Analytically solve cubic equation.
+ * The equation is given in the standard form: ax^3 + bx^2 + cx + d = 0.
+ * Only real roots are returned. */
+std::vector<Coord> solve_cubic(Coord a, Coord b, Coord c, Coord d);
+
+
+inline std::ostream &operator<< (std::ostream &out_file, const Poly &in_poly) {
+    if(in_poly.size() == 0)
+        out_file << "0";
+    else {
+        for(int i = (int)in_poly.size()-1; i >= 0; --i) {
+            if(i == 1) {
+                out_file << "" << in_poly[i] << "*x";
+                out_file << " + ";
+            } else if(i) {
+                out_file << "" << in_poly[i] << "*x^" << i;
+                out_file << " + ";
+            } else
+                out_file << in_poly[i];
+
+        }
+    }
+    return out_file;
+}
+
+} // namespace Geom
+
+#endif //LIB2GEOM_SEEN_POLY_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/ray.h b/include/2geom/ray.h
new file mode 100644
index 0000000..4e60fd8
--- /dev/null
+++ b/include/2geom/ray.h
@@ -0,0 +1,192 @@
+/**
+ * \file
+ * \brief  Infinite straight ray
+ *//*
+ * Copyright 2008  Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_RAY_H
+#define LIB2GEOM_SEEN_RAY_H
+
+#include <vector>
+#include <2geom/point.h>
+#include <2geom/bezier-curve.h> // for LineSegment
+#include <2geom/exception.h>
+#include <2geom/math-utils.h>
+#include <2geom/transforms.h>
+#include <2geom/angle.h>
+
+namespace Geom
+{
+
+/**
+ * @brief Straight ray from a specific point to infinity.
+ *
+ * Rays are "half-lines" - they begin at some specific point and extend in a straight line
+ * to infinity.
+ *
+ * @ingroup Primitives
+ */
+class Ray {
+private:
+    Point _origin;
+    Point _vector;
+
+public:
+    Ray() : _origin(0,0), _vector(1,0) {}
+    Ray(Point const& origin, Coord angle)
+        : _origin(origin)
+    {
+        sincos(angle, _vector[Y], _vector[X]);
+    }
+    Ray(Point const& A, Point const& B) {
+        setPoints(A, B);
+    }
+    Point origin() const { return _origin; }
+    Point vector() const { return _vector; }
+    Point versor() const { return _vector.normalized(); }
+    void setOrigin(Point const &o) { _origin = o; }
+    void setVector(Point const& v) { _vector = v; }
+    Coord angle() const { return std::atan2(_vector[Y], _vector[X]); }
+    void setAngle(Coord a) { sincos(a, _vector[Y], _vector[X]); }
+    void setPoints(Point const &a, Point const &b) {
+        _origin = a;
+        _vector = b - a;
+        if (are_near(_vector, Point(0,0)) )
+            _vector = Point(0,0);
+        else
+            _vector.normalize();
+    }
+    bool isDegenerate() const {
+        return ( _vector[X] == 0 && _vector[Y] == 0 );
+    }
+    Point pointAt(Coord t) const {
+        return _origin + _vector * t;
+    }
+    Coord valueAt(Coord t, Dim2 d) const {
+        return _origin[d] + _vector[d] * t;
+    }
+    std::vector<Coord> roots(Coord v, Dim2 d) const {
+        std::vector<Coord> result;
+        if ( _vector[d] != 0 ) {
+            double t = (v - _origin[d]) / _vector[d];
+            if (t >= 0)	result.push_back(t);
+        } else if (_vector[(d+1)%2] == v) {
+            THROW_INFINITESOLUTIONS();
+        }
+        return result;
+    }
+    Coord nearestTime(Point const& point) const {
+        if ( isDegenerate() ) return 0;
+        double t = dot(point - _origin, _vector);
+        if (t < 0) t = 0;
+        return t;
+    }
+    Ray reverse() const {
+        Ray result;
+        result.setOrigin(_origin);
+        result.setVector(-_vector);
+        return result;
+    }
+    Curve *portion(Coord f, Coord t) const {
+        return new LineSegment(pointAt(f), pointAt(t));
+    }
+    LineSegment segment(Coord f, Coord t) const {
+        return LineSegment(pointAt(f), pointAt(t));
+    }
+    Ray transformed(Affine const& m) const {
+        return Ray(_origin * m, (_origin + _vector) * m);
+    }
+}; // end class Ray
+
+inline
+double distance(Point const& _point, Ray const& _ray) {
+	double t = _ray.nearestTime(_point);
+	return ::Geom::distance(_point, _ray.pointAt(t));
+}
+
+inline
+bool are_near(Point const& _point, Ray const& _ray, double eps = EPSILON) {
+	return are_near(distance(_point, _ray), 0, eps);
+}
+
+inline
+bool are_same(Ray const& r1, Ray const& r2, double eps = EPSILON) {
+	return are_near(r1.vector(), r2.vector(), eps)
+			&& are_near(r1.origin(), r2.origin(), eps);
+}
+
+// evaluate the angle between r1 and r2 rotating r1 in cw or ccw direction on r2
+// the returned value is an angle in the interval [0, 2PI[
+inline
+double angle_between(Ray const& r1, Ray const& r2, bool cw = true) {
+	double angle = angle_between(r1.vector(), r2.vector());
+	if (angle < 0) angle += 2*M_PI;
+	if (!cw) angle = 2*M_PI - angle;
+	return angle;
+}
+
+/**
+ * @brief Returns the angle bisector for the two given rays.
+ * 
+ * @a r1 is rotated half the way to @a r2 in either clockwise or counter-clockwise direction.
+ *
+ * @pre Both passed rays must have the same origin.
+ * 
+ * @remarks If the versors of both given rays point in the same direction, the direction of the
+ * angle bisector ray depends on the third parameter:
+ * - If @a cw is set to @c true, the returned ray will equal the passed rays @a r1 and @a r2.
+ * - If @a cw is set to @c false, the returned ray will go in the opposite direction.
+ * 
+ * @throws RangeError if the given rays do not have the same origins
+ */
+inline
+Ray make_angle_bisector_ray(Ray const& r1, Ray const& r2, bool cw = true)
+{
+    if ( !are_near(r1.origin(), r2.origin()) )
+    {
+        THROW_RANGEERROR("passed rays do not have the same origin");
+    }
+
+    Ray bisector(r1.origin(), r1.origin() +  r1.vector() * Rotate(angle_between(r1, r2) / 2.0));
+    
+    return (cw ? bisector : bisector.reverse());
+}
+
+}  // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_RAY_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/rect.h b/include/2geom/rect.h
new file mode 100644
index 0000000..5edfc95
--- /dev/null
+++ b/include/2geom/rect.h
@@ -0,0 +1,263 @@
+/**
+ * \file
+ * \brief Axis-aligned rectangle
+ *//*
+ * Authors:
+ *   Michael Sloan <mgsloan@gmail.com>
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * Copyright 2007-2011 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * Authors of original rect class:
+ *   Lauris Kaplinski <lauris@kaplinski.com>
+ *   Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *   bulia byak <buliabyak@users.sf.net>
+ *   MenTaLguY <mental@rydia.net>
+ */
+
+#ifndef LIB2GEOM_SEEN_RECT_H
+#define LIB2GEOM_SEEN_RECT_H
+
+#include <2geom/affine.h>
+#include <2geom/interval.h>
+#include <2geom/int-rect.h>
+
+namespace Geom {
+
+/** Values for the <align> parameter of preserveAspectRatio.
+ * See: http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute */
+enum Align {
+    ALIGN_NONE,
+    ALIGN_XMIN_YMIN,
+    ALIGN_XMID_YMIN,
+    ALIGN_XMAX_YMIN,
+    ALIGN_XMIN_YMID,
+    ALIGN_XMID_YMID,
+    ALIGN_XMAX_YMID,
+    ALIGN_XMIN_YMAX,
+    ALIGN_XMID_YMAX,
+    ALIGN_XMAX_YMAX
+};
+
+/** Values for the <meetOrSlice> parameter of preserveAspectRatio.
+ * See: http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute */
+enum Expansion {
+    EXPANSION_MEET,
+    EXPANSION_SLICE
+};
+
+/// Convert an align specification to coordinate fractions.
+Point align_factors(Align align);
+
+/** @brief Structure that specifies placement of within a viewport.
+ * Use this to create transformations that preserve aspect. */
+struct Aspect {
+    Align align;
+    Expansion expansion;
+    bool deferred; ///< for SVG compatibility
+
+    Aspect(Align a = ALIGN_NONE, Expansion ex = EXPANSION_MEET)
+        : align(a), expansion(ex), deferred(false)
+    {}
+};
+
+/**
+ * @brief Axis aligned, non-empty rectangle.
+ * @ingroup Primitives
+ */
+class Rect
+    : public GenericRect<Coord>
+{
+    typedef GenericRect<Coord> Base;
+public:
+    /// @name Create rectangles.
+    /// @{
+    /** @brief Create a rectangle that contains only the point at (0,0). */
+    Rect() {}
+    /** @brief Create a rectangle from X and Y intervals. */
+    Rect(Interval const &a, Interval const &b) : Base(a,b) {}
+    /** @brief Create a rectangle from two points. */
+    Rect(Point const &a, Point const &b) : Base(a,b) {}
+    Rect(Coord x0, Coord y0, Coord x1, Coord y1) : Base(x0, y0, x1, y1) {}
+    Rect(Base const &b) : Base(b) {}
+    Rect(IntRect const &ir) : Base(ir.min(), ir.max()) {}
+    /// @}
+
+    /// @name Inspect dimensions.
+    /// @{
+    /** @brief Check whether the rectangle has zero area up to specified tolerance.
+     * @param eps Maximum value of the area to consider empty
+     * @return True if rectangle has an area smaller than tolerance, false otherwise */
+    bool hasZeroArea(Coord eps = EPSILON) const { return (area() <= eps); }
+    /// Check whether the rectangle has finite area
+    bool isFinite() const { return (*this)[X].isFinite() && (*this)[Y].isFinite(); }
+    /// Calculate the diameter of the smallest circle that would contain the rectangle.
+    Coord diameter() const { return distance(corner(0), corner(2)); }
+    /// @}
+
+    /// @name Test other rectangles and points for inclusion.
+    /// @{
+    /** @brief Check whether the interiors of the rectangles have any common points. */
+    bool interiorIntersects(Rect const &r) const {
+        return f[X].interiorIntersects(r[X]) && f[Y].interiorIntersects(r[Y]);
+    }
+    /** @brief Check whether the interior includes the given point. */
+    bool interiorContains(Point const &p) const {
+        return f[X].interiorContains(p[X]) && f[Y].interiorContains(p[Y]);
+    }
+    /** @brief Check whether the interior includes all points in the given rectangle.
+     * Interior of the rectangle is the entire rectangle without its borders. */
+    bool interiorContains(Rect const &r) const { 
+        return f[X].interiorContains(r[X]) && f[Y].interiorContains(r[Y]);
+    }
+    inline bool interiorContains(OptRect const &r) const;
+    /// @}
+
+    /// @name Rounding to integer coordinates
+    /// @{
+    /** @brief Return the smallest integer rectangle which contains this one. */
+    IntRect roundOutwards() const {
+        IntRect ir(f[X].roundOutwards(), f[Y].roundOutwards());
+        return ir;
+    }
+    /** @brief Return the largest integer rectangle which is contained in this one. */
+    OptIntRect roundInwards() const {
+        OptIntRect oir(f[X].roundInwards(), f[Y].roundInwards());
+        return oir;
+    }
+    /// @}
+
+    /// @name SVG viewbox functionality.
+    /// @{
+    /** @brief Transform contents to viewport.
+     * Computes an affine that transforms the contents of this rectangle
+     * to the specified viewport. The aspect parameter specifies how to
+     * to the transformation (whether the aspect ratio of content
+     * should be kept and where it should be placed in the viewport). */
+    Affine transformTo(Rect const &viewport, Aspect const &aspect = Aspect()) const;
+    /// @}
+
+    /// @name Operators
+    /// @{
+    Rect &operator*=(Affine const &m);
+    bool operator==(IntRect const &ir) const {
+        return f[X] == ir[X] && f[Y] == ir[Y];
+    }
+    bool operator==(Rect const &other) const {
+        return Base::operator==(other);
+    }
+    /// @}
+};
+
+/**
+ * @brief Axis-aligned rectangle that can be empty.
+ * @ingroup Primitives
+ */
+class OptRect
+    : public GenericOptRect<Coord>
+{
+    typedef GenericOptRect<Coord> Base;
+public:
+    OptRect() : Base() {}
+    OptRect(Rect const &a) : Base(a) {}
+    OptRect(Point const &a, Point const &b) : Base(a, b) {}
+    OptRect(Coord x0, Coord y0, Coord x1, Coord y1) : Base(x0, y0, x1, y1) {}
+    OptRect(OptInterval const &x_int, OptInterval const &y_int) : Base(x_int, y_int) {}
+    OptRect(Base const &b) : Base(b) {}
+
+    OptRect(IntRect const &r) : Base(Rect(r)) {}
+    OptRect(OptIntRect const &r) : Base() {
+        if (r) *this = Rect(*r);
+    }
+
+    Affine transformTo(Rect const &viewport, Aspect const &aspect = Aspect()) {
+        Affine ret = Affine::identity();
+        if (empty()) return ret;
+        ret = (*this)->transformTo(viewport, aspect);
+        return ret;
+    }
+
+    bool operator==(OptRect const &other) const {
+        return Base::operator==(other);
+    }
+    bool operator==(Rect const &other) const {
+        return Base::operator==(other);
+    }
+};
+
+Coord distanceSq(Point const &p, Rect const &rect);
+Coord distance(Point const &p, Rect const &rect);
+/// Minimum square of distance to rectangle, or infinity if empty.
+Coord distanceSq(Point const &p, OptRect const &rect);
+/// Minimum distance to rectangle, or infinity if empty.
+Coord distance(Point const &p, OptRect const &rect);
+
+inline bool Rect::interiorContains(OptRect const &r) const {
+    return !r || interiorContains(static_cast<Rect const &>(*r));
+}
+
+// the functions below do not work when defined generically
+inline OptRect operator&(Rect const &a, Rect const &b) {
+    OptRect ret(a);
+    ret.intersectWith(b);
+    return ret;
+}
+inline OptRect intersect(Rect const &a, Rect const &b) {
+    return a & b;
+}
+inline OptRect intersect(OptRect const &a, OptRect const &b) {
+    return a & b;
+}
+inline Rect unify(Rect const &a, Rect const &b) {
+    return a | b;
+}
+inline OptRect unify(OptRect const &a, OptRect const &b) {
+    return a | b;
+}
+
+/** @brief Union a list of rectangles
+ * @deprecated Use OptRect::from_range instead */
+inline Rect union_list(std::vector<Rect> const &r) {
+    if(r.empty()) return Rect(Interval(0,0), Interval(0,0));
+    Rect ret = r[0];
+    for(unsigned i = 1; i < r.size(); i++)
+        ret.unionWith(r[i]);
+    return ret;
+}
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_RECT_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/sbasis-2d.h b/include/2geom/sbasis-2d.h
new file mode 100644
index 0000000..98dec67
--- /dev/null
+++ b/include/2geom/sbasis-2d.h
@@ -0,0 +1,371 @@
+/**
+ * \file
+ * \brief Obsolete 2D SBasis function class
+ *//*
+ * Authors:
+ *      Nathan Hurst <?@?.?>
+ *      JFBarraud <?@?.?>
+ * 
+ * Copyright 2006-2008  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_SBASIS_2D_H
+#define LIB2GEOM_SEEN_SBASIS_2D_H
+#include <vector>
+#include <cassert>
+#include <algorithm>
+#include <2geom/d2.h>
+#include <2geom/sbasis.h>
+#include <iostream>
+
+namespace Geom{
+
+class Linear2d{
+public:
+    /*  
+        u 0,1
+        v 0,2
+    */
+    double a[4];
+    Linear2d() {
+        a[0] = 0; 
+        a[1] = 0;
+        a[2] = 0; 
+        a[3] = 0; 
+    }
+    Linear2d(double aa) {
+        for(double & i : a) 
+            i = aa;
+    }
+    Linear2d(double a00, double a01, double a10, double a11) 
+    {
+        a[0] = a00; 
+        a[1] = a01;
+        a[2] = a10; 
+        a[3] = a11; 
+    }
+
+    double operator[](const int i) const {
+        assert(i >= 0);
+        assert(i < 4);
+        return a[i];
+    }
+    double& operator[](const int i) {
+        assert(i >= 0);
+        assert(i < 4);
+        return a[i];
+    }
+    double apply(double u, double v) {
+        return (a[0]*(1-u)*(1-v) +
+                a[1]*u*(1-v) +
+                a[2]*(1-u)*v +
+                a[3]*u*v);
+    }
+};
+
+inline Linear extract_u(Linear2d const &a, double u) {
+    return Linear(a[0]*(1-u) +
+                  a[1]*u,
+                  a[2]*(1-u) +
+                  a[3]*u);
+}
+inline Linear extract_v(Linear2d const &a, double v) {
+    return Linear(a[0]*(1-v) +
+                  a[2]*v,
+                  a[1]*(1-v) +
+                  a[3]*v);
+}
+inline Linear2d operator-(Linear2d const &a) {
+    return Linear2d(-a.a[0], -a.a[1],
+                    -a.a[2], -a.a[3]);
+}
+inline Linear2d operator+(Linear2d const & a, Linear2d const & b) {
+    return Linear2d(a[0] + b[0], 
+                  a[1] + b[1],
+                  a[2] + b[2], 
+                  a[3] + b[3]);
+}
+inline Linear2d operator-(Linear2d const & a, Linear2d const & b) {
+    return Linear2d(a[0] - b[0],
+                  a[1] - b[1],
+                  a[2] - b[2],
+                  a[3] - b[3]);
+}
+inline Linear2d& operator+=(Linear2d & a, Linear2d const & b) {
+    for(unsigned i = 0; i < 4; i++)
+        a[i] += b[i];
+    return a;
+}
+inline Linear2d& operator-=(Linear2d & a, Linear2d const & b) {
+    for(unsigned i = 0; i < 4; i++)
+        a[i] -= b[i];
+    return a;
+}
+inline Linear2d& operator*=(Linear2d & a, double b) {
+    for(unsigned i = 0; i < 4; i++)
+        a[i] *= b;
+    return a;
+}
+
+inline bool operator==(Linear2d const & a, Linear2d const & b) {
+    for(unsigned i = 0; i < 4; i++)
+        if(a[i] != b[i])
+            return false;
+    return true;
+}
+inline bool operator!=(Linear2d const & a, Linear2d const & b) {
+    for(unsigned i = 0; i < 4; i++)
+        if(a[i] == b[i])
+            return false;
+    return true;
+}
+inline Linear2d operator*(double const a, Linear2d const & b) {
+    return Linear2d(a*b[0], a*b[1],
+                  a*b[2], a*b[3]);
+}
+
+class SBasis2d : public std::vector<Linear2d>{
+public:
+    // vector in u,v
+    unsigned us, vs; // number of u terms, v terms
+    SBasis2d() {}
+    SBasis2d(Linear2d const & bo) 
+        : us(1), vs(1) {
+        push_back(bo);
+    }
+    SBasis2d(SBasis2d const & a) 
+        : std::vector<Linear2d>(a), us(a.us), vs(a.vs) {}
+    
+    Linear2d& index(unsigned ui, unsigned vi) {
+        assert(ui < us);
+        assert(vi < vs);
+        return (*this)[ui + vi*us];        
+    }
+    
+    Linear2d index(unsigned ui, unsigned vi) const {
+        if(ui >= us) 
+            return Linear2d(0);
+        if(vi >= vs)
+            return Linear2d(0);
+        return (*this)[ui + vi*us];        
+    }
+    
+    double apply(double u, double v) const {
+        double s = u*(1-u);
+        double t = v*(1-v);
+        Linear2d p;
+        double tk = 1;
+// XXX rewrite as horner
+        for(unsigned vi = 0; vi < vs; vi++) {
+            double sk = 1;
+            for(unsigned ui = 0; ui < us; ui++) {
+                p += (sk*tk)*index(ui, vi);
+                sk *= s;
+            }
+            tk *= t;
+        }
+        return p.apply(u,v);
+    }
+
+    void clear() {
+        fill(begin(), end(), Linear2d(0));
+    }
+    
+    void normalize(); // remove extra zeros
+
+    double tail_error(unsigned tail) const;
+    
+    void truncate(unsigned k);
+};
+
+inline SBasis2d operator-(const SBasis2d& p) {
+    SBasis2d result;
+    result.reserve(p.size());
+        
+    for(unsigned i = 0; i < p.size(); i++) {
+        result.push_back(-p[i]);
+    }
+    return result;
+}
+
+inline SBasis2d operator+(const SBasis2d& a, const SBasis2d& b) {
+    SBasis2d result;
+    result.us = std::max(a.us, b.us);
+    result.vs = std::max(a.vs, b.vs);
+    const unsigned out_size = result.us*result.vs;
+    result.resize(out_size);
+        
+    for(unsigned vi = 0; vi < result.vs; vi++) {
+        for(unsigned ui = 0; ui < result.us; ui++) {
+            Linear2d bo;
+            if(ui < a.us && vi < a.vs)
+                bo += a.index(ui, vi);
+            if(ui < b.us && vi < b.vs)
+                bo += b.index(ui, vi);
+            result.index(ui, vi) = bo;
+        }
+    }
+    return result;
+}
+
+inline SBasis2d operator-(const SBasis2d& a, const SBasis2d& b) {
+    SBasis2d result;
+    result.us = std::max(a.us, b.us);
+    result.vs = std::max(a.vs, b.vs);
+    const unsigned out_size = result.us*result.vs;
+    result.resize(out_size);
+        
+    for(unsigned vi = 0; vi < result.vs; vi++) {
+        for(unsigned ui = 0; ui < result.us; ui++) {
+            Linear2d bo;
+            if(ui < a.us && vi < a.vs)
+                bo += a.index(ui, vi);
+            if(ui < b.us && vi < b.vs)
+                bo -= b.index(ui, vi);
+            result.index(ui, vi) = bo;
+        }
+    }
+    return result;
+}
+
+
+inline SBasis2d& operator+=(SBasis2d& a, const Linear2d& b) {
+    if(a.size() < 1)
+        a.push_back(b);
+    else
+        a[0] += b;
+    return a;
+}
+
+inline SBasis2d& operator-=(SBasis2d& a, const Linear2d& b) {
+    if(a.size() < 1)
+        a.push_back(-b);
+    else
+        a[0] -= b;
+    return a;
+}
+
+inline SBasis2d& operator+=(SBasis2d& a, double b) {
+    if(a.size() < 1)
+        a.push_back(Linear2d(b));
+    else {
+        for(unsigned i = 0; i < 4; i++)
+            a[0] += double(b);
+    }
+    return a;
+}
+
+inline SBasis2d& operator-=(SBasis2d& a, double b) {
+    if(a.size() < 1)
+        a.push_back(Linear2d(-b));
+    else {
+        a[0] -= b;
+    }
+    return a;
+}
+
+inline SBasis2d& operator*=(SBasis2d& a, double b) {
+    for(unsigned i = 0; i < a.size(); i++)
+        a[i] *= b;
+    return a;
+}
+
+inline SBasis2d& operator/=(SBasis2d& a, double b) {
+    for(unsigned i = 0; i < a.size(); i++)
+        a[i] *= (1./b);
+    return a;
+}
+
+SBasis2d operator*(double k, SBasis2d const &a);
+SBasis2d operator*(SBasis2d const &a, SBasis2d const &b);
+
+SBasis2d shift(SBasis2d const &a, int sh);
+
+SBasis2d shift(Linear2d const &a, int sh);
+
+SBasis2d truncate(SBasis2d const &a, unsigned terms);
+
+SBasis2d multiply(SBasis2d const &a, SBasis2d const &b);
+
+SBasis2d integral(SBasis2d const &c);
+
+SBasis2d partial_derivative(SBasis2d const &a, int dim);
+
+SBasis2d sqrt(SBasis2d const &a, int k);
+
+// return a kth order approx to 1/a)
+SBasis2d reciprocal(Linear2d const &a, int k);
+
+SBasis2d divide(SBasis2d const &a, SBasis2d const &b, int k);
+
+// a(b(t))
+SBasis2d compose(SBasis2d const &a, SBasis2d const &b);
+SBasis2d compose(SBasis2d const &a, SBasis2d const &b, unsigned k);
+SBasis2d inverse(SBasis2d const &a, int k);
+
+// these two should probably be replaced with compose
+SBasis extract_u(SBasis2d const &a, double u);
+SBasis extract_v(SBasis2d const &a, double v);
+
+SBasis compose(Linear2d const &a, D2<SBasis> const &p);
+
+SBasis compose(SBasis2d const &fg, D2<SBasis> const &p);
+
+D2<SBasis> compose_each(D2<SBasis2d> const &fg, D2<SBasis> const &p);
+
+inline std::ostream &operator<< (std::ostream &out_file, const Linear2d &bo) {
+    out_file << "{" << bo[0] << ", " << bo[1] << "}, ";
+    out_file << "{" << bo[2] << ", " << bo[3] << "}";
+    return out_file;
+}
+
+inline std::ostream &operator<< (std::ostream &out_file, const SBasis2d & p) {
+    for(unsigned i = 0; i < p.size(); i++) {
+        out_file << p[i] << "s^" << i << " + ";
+    }
+    return out_file;
+}
+
+D2<SBasis>
+sb2dsolve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B, unsigned degmax=2);
+
+D2<SBasis>
+sb2d_cubic_solve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B);
+
+} // end namespace Geom
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/sbasis-curve.h b/include/2geom/sbasis-curve.h
new file mode 100644
index 0000000..93d6772
--- /dev/null
+++ b/include/2geom/sbasis-curve.h
@@ -0,0 +1,160 @@
+/**
+ * \file
+ * \brief Symmetric power basis curve
+ *//*
+ * Authors:
+ *   MenTaLguY <mental@rydia.net>
+ *   Marco Cecchetti <mrcekets at gmail.com>
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * 
+ * Copyright 2007-2009 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_SBASIS_CURVE_H
+#define LIB2GEOM_SEEN_SBASIS_CURVE_H
+
+#include <2geom/curve.h>
+#include <2geom/exception.h>
+#include <2geom/nearest-time.h>
+#include <2geom/sbasis-geometric.h>
+#include <2geom/transforms.h>
+
+namespace Geom 
+{
+
+/** @brief Symmetric power basis curve.
+ *
+ * Symmetric power basis (S-basis for short) polynomials are a versatile numeric
+ * representation of arbitrary continuous curves. They are the main representation of curves
+ * in 2Geom.
+ *
+ * S-basis is defined for odd degrees and composed of the following polynomials:
+ * \f{align*}{
+         P_k^0(t) &= t^k (1-t)^{k+1} \\
+         P_k^1(t) &= t^{k+1} (1-t)^k \f}
+ * This can be understood more easily with the help of the chart below. Each square
+ * represents a product of a specific number of \f$t\f$ and \f$(1-t)\f$ terms. Red dots
+ * are the canonical (monomial) basis, the green dots are the Bezier basis, and the blue
+ * dots are the S-basis, all of them of degree 7.
+ *
+ * @image html sbasis.png "Illustration of the monomial, Bezier and symmetric power bases"
+ *
+ * The S-Basis has several important properties:
+ * - S-basis polynomials are closed under multiplication.
+ * - Evaluation is fast, using a modified Horner scheme.
+ * - Degree change is as trivial as in the monomial basis. To elevate, just add extra
+ *   zero coefficients. To reduce the degree, truncate the terms in the highest powers.
+ *   Compare this with Bezier curves, where degree change is complicated.
+ * - Conversion between S-basis and Bezier basis is numerically stable.
+ *
+ * More in-depth information can be found in the following paper:
+ * J Sanchez-Reyes, "The symmetric analogue of the polynomial power basis".
+ * ACM Transactions on Graphics, Vol. 16, No. 3, July 1997, pages 319--357.
+ * http://portal.acm.org/citation.cfm?id=256162
+ *
+ * @ingroup Curves
+ */
+class SBasisCurve : public Curve {
+private:
+    D2<SBasis> inner;
+  
+public:
+    explicit SBasisCurve(D2<SBasis> const &sb) : inner(sb) {}
+    explicit SBasisCurve(Curve const &other) : inner(other.toSBasis()) {}
+
+    Curve *duplicate() const override { return new SBasisCurve(*this); }
+    Point initialPoint() const override    { return inner.at0(); }
+    Point finalPoint() const override      { return inner.at1(); }
+    bool isDegenerate() const override     { return inner.isConstant(0); }
+    bool isLineSegment() const override    { return inner[X].size() == 1; }
+    Point pointAt(Coord t) const override  { return inner.valueAt(t); }
+    std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const override {
+        return inner.valueAndDerivatives(t, n);
+    }
+    Coord valueAt(Coord t, Dim2 d) const override { return inner[d].valueAt(t); }
+    void setInitial(Point const &v) override {
+        for (unsigned d = 0; d < 2; d++) { inner[d][0][0] = v[d]; }
+    }
+    void setFinal(Point const &v) override {
+        for (unsigned d = 0; d < 2; d++) { inner[d][0][1] = v[d]; }
+    }
+    Rect boundsFast() const override  { return *bounds_fast(inner); }
+    Rect boundsExact() const override { return *bounds_exact(inner); }
+    void expandToTransformed(Rect &bbox, Affine const &transform) const override {
+        bbox |= bounds_exact(inner * transform);
+    }
+    OptRect boundsLocal(OptInterval const &i, unsigned deg) const override {
+        return bounds_local(inner, i, deg);
+    }
+    std::vector<Coord> roots(Coord v, Dim2 d) const override { return Geom::roots(inner[d] - v); }
+    Coord nearestTime( Point const& p, Coord from = 0, Coord to = 1 ) const override {
+        return nearest_time(p, inner, from, to);
+    }
+    std::vector<Coord> allNearestTimes( Point const& p, Coord from = 0,
+        Coord to = 1 ) const override
+    {
+        return all_nearest_times(p, inner, from, to);
+    }
+    Coord length(Coord tolerance) const override { return ::Geom::length(inner, tolerance); }
+    Curve *portion(Coord f, Coord t) const override {
+        return new SBasisCurve(Geom::portion(inner, f, t));
+    }
+
+    using Curve::operator*=;
+    void operator*=(Affine const &m) override { inner = inner * m; }
+
+    Curve *derivative() const override {
+        return new SBasisCurve(Geom::derivative(inner));
+    }
+    D2<SBasis> toSBasis() const override { return inner; }
+    bool operator==(Curve const &c) const override {
+        SBasisCurve const *other = dynamic_cast<SBasisCurve const *>(&c);
+        if (!other) return false;
+        return inner == other->inner;
+    }
+    bool isNear(Curve const &/*c*/, Coord /*eps*/) const override {
+        THROW_NOTIMPLEMENTED();
+        return false;
+    }
+    int degreesOfFreedom() const override {
+        return inner[0].degreesOfFreedom() + inner[1].degreesOfFreedom();
+    }
+};
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_SBASIS_CURVE_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/sbasis-geometric.h b/include/2geom/sbasis-geometric.h
new file mode 100644
index 0000000..7f1e8aa
--- /dev/null
+++ b/include/2geom/sbasis-geometric.h
@@ -0,0 +1,146 @@
+/**
+ * \file
+ * \brief two-dimensional geometric operators.  
+ * 
+ * These operators are built on a more 'polynomially robust'
+ * transformation to map a function that takes a [0,1] parameter to a
+ * 2d vector into a function that takes the same [0,1] parameter to a
+ * unit vector with the same direction.
+ *
+ * Rather that using (X/sqrt(X))(t) which involves two unstable
+ * operations, sqrt and divide, this approach forms a curve directly
+ * from the various tangent directions at each end (angular jet).  As
+ * a result, the final path has a convergence behaviour derived from
+ * that of the sin and cos series. -- njh
+ *//*
+ * Copyright 2007, JFBarraud
+ * Copyright 2007, njh
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_SBASIS_GEOMETRIC_H
+#define LIB2GEOM_SEEN_SBASIS_GEOMETRIC_H
+
+#include <2geom/d2.h>
+#include <2geom/piecewise.h>
+#include <vector>
+
+namespace Geom {
+
+Piecewise<D2<SBasis> > 
+cutAtRoots(Piecewise<D2<SBasis> > const &M, double tol=1e-4);
+
+Piecewise<SBasis>
+atan2(D2<SBasis> const &vect, 
+           double tol=.01, unsigned order=3);
+
+Piecewise<SBasis>
+atan2(Piecewise<D2<SBasis> >const &vect, 
+           double tol=.01, unsigned order=3);
+
+D2<Piecewise<SBasis> >
+tan2(SBasis const &angle, 
+           double tol=.01, unsigned order=3);
+
+D2<Piecewise<SBasis> >
+tan2(Piecewise<SBasis> const &angle, 
+           double tol=.01, unsigned order=3);
+
+Piecewise<D2<SBasis> >
+unitVector(D2<SBasis> const &vect, 
+           double tol=.01, unsigned order=3);
+Piecewise<D2<SBasis> >
+unitVector(Piecewise<D2<SBasis> > const &vect, 
+           double tol=.01, unsigned order=3);
+
+// Piecewise<D2<SBasis> >
+// uniform_speed(D2<SBasis> const M, 
+//               double tol=.1);
+
+Piecewise<SBasis> curvature(          D2<SBasis>   const &M, double tol=.01);
+Piecewise<SBasis> curvature(Piecewise<D2<SBasis> > const &M, double tol=.01);
+
+Piecewise<SBasis> arcLengthSb(          D2<SBasis>   const &M, double tol=.01);
+Piecewise<SBasis> arcLengthSb(Piecewise<D2<SBasis> > const &M, double tol=.01);
+
+double length(          D2<SBasis>   const &M, double tol=.01);
+double length(Piecewise<D2<SBasis> > const &M, double tol=.01);
+
+void length_integrating(D2<SBasis> const &B, double &result, double &abs_error, double tol);
+
+Piecewise<D2<SBasis> >
+arc_length_parametrization(D2<SBasis> const &M, 
+                           unsigned order=3, 
+                           double tol=.01);
+Piecewise<D2<SBasis> >
+arc_length_parametrization(Piecewise<D2<SBasis> > const &M,
+                           unsigned order=3,
+                           double tol=.01);
+
+
+unsigned centroid(Piecewise<D2<SBasis> > const &p, Point& centroid, double &area);
+
+std::vector<D2<SBasis> >
+cubics_fitting_curvature(Point const &M0,   Point const &M1,
+                         Point const &dM0,  Point const &dM1,
+                         double d2M0xdM0,  double d2M1xdM1,
+                         int insist_on_speed_signs = 1,
+                         double epsilon = 1e-5);
+
+std::vector<D2<SBasis> >
+cubics_fitting_curvature(Point const &M0,   Point const &M1,
+                         Point const &dM0,  Point const &dM1,
+                         Point const &d2M0, Point const &d2M1,
+                         int insist_on_speed_signs = 1,
+                         double epsilon = 1e-5);
+
+std::vector<D2<SBasis> >
+cubics_with_prescribed_curvature(Point const &M0,   Point const &M1,
+                                 Point const &dM0,  Point const &dM1,
+                                 double k0,         double k1,
+                                 int insist_on_speed_signs = 1,
+                                 double error = 1e-5);
+
+
+std::vector<double> find_tangents(Point P, D2<SBasis> const &A);
+std::vector<double> find_tangents_by_vector(Point V, D2<SBasis> const &A);
+std::vector<double> find_normals(Point P, D2<SBasis> const &A);
+std::vector<double> find_normals_by_vector(Point V, D2<SBasis> const &A);
+
+};
+
+#endif
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
+
diff --git a/include/2geom/sbasis-math.h b/include/2geom/sbasis-math.h
new file mode 100644
index 0000000..e191dae
--- /dev/null
+++ b/include/2geom/sbasis-math.h
@@ -0,0 +1,99 @@
+/** @file
+ * @brief some std functions to work with (pw)s-basis
+ *//*
+ *  Authors:
+ *   Jean-Francois Barraud
+ *
+ * Copyright (C) 2006-2007 authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+//this a first try to define sqrt, cos, sin, etc...
+//TODO: define a truncated compose(sb,sb, order) and extend it to pw<sb>.
+//TODO: in all these functions, compute 'order' according to 'tol'.
+//TODO: use template to define the pw version automatically from the sb version?
+
+#ifndef LIB2GEOM_SEEN_SBASIS_MATH_H
+#define LIB2GEOM_SEEN_SBASIS_MATH_H
+
+
+#include <2geom/sbasis.h>
+#include <2geom/piecewise.h>
+#include <2geom/d2.h>
+
+namespace Geom{
+//-|x|---------------------------------------------------------------
+Piecewise<SBasis> abs(          SBasis const &f);
+Piecewise<SBasis> abs(Piecewise<SBasis>const &f);
+
+//- max(f,g), min(f,g) ----------------------------------------------
+Piecewise<SBasis> max(          SBasis  const &f,           SBasis  const &g);
+Piecewise<SBasis> max(Piecewise<SBasis> const &f,           SBasis  const &g);
+Piecewise<SBasis> max(          SBasis  const &f, Piecewise<SBasis> const &g);
+Piecewise<SBasis> max(Piecewise<SBasis> const &f, Piecewise<SBasis> const &g);
+Piecewise<SBasis> min(          SBasis  const &f,           SBasis  const &g);
+Piecewise<SBasis> min(Piecewise<SBasis> const &f,           SBasis  const &g);
+Piecewise<SBasis> min(          SBasis  const &f, Piecewise<SBasis> const &g);
+Piecewise<SBasis> min(Piecewise<SBasis> const &f, Piecewise<SBasis> const &g);
+
+//-sign(x)---------------------------------------------------------------
+Piecewise<SBasis> signSb(          SBasis const &f);
+Piecewise<SBasis> signSb(Piecewise<SBasis>const &f);
+
+//-Sqrt---------------------------------------------------------------
+Piecewise<SBasis> sqrt(          SBasis const &f, double tol=1e-3, int order=3);
+Piecewise<SBasis> sqrt(Piecewise<SBasis>const &f, double tol=1e-3, int order=3);
+
+//-sin/cos--------------------------------------------------------------
+Piecewise<SBasis> cos(          SBasis  const &f, double tol=1e-3, int order=3);
+Piecewise<SBasis> cos(Piecewise<SBasis> const &f, double tol=1e-3, int order=3);
+Piecewise<SBasis> sin(          SBasis  const &f, double tol=1e-3, int order=3);
+Piecewise<SBasis> sin(Piecewise<SBasis> const &f, double tol=1e-3, int order=3);
+//-Log---------------------------------------------------------------
+Piecewise<SBasis> log(          SBasis const &f, double tol=1e-3, int order=3);
+Piecewise<SBasis> log(Piecewise<SBasis>const &f, double tol=1e-3, int order=3);
+
+//--1/x------------------------------------------------------------
+//TODO: change this...
+Piecewise<SBasis> reciprocalOnDomain(Interval range, double tol=1e-3);
+Piecewise<SBasis> reciprocal(          SBasis const &f, double tol=1e-3, int order=3);
+Piecewise<SBasis> reciprocal(Piecewise<SBasis>const &f, double tol=1e-3, int order=3);
+
+//--interpolate------------------------------------------------------------
+Piecewise<SBasis> interpolate( std::vector<double> times, std::vector<double> values, unsigned smoothness = 1);
+}
+
+#endif //SEEN_GEOM_PW_SB_CALCULUS_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/sbasis-poly.h b/include/2geom/sbasis-poly.h
new file mode 100644
index 0000000..d18bc36
--- /dev/null
+++ b/include/2geom/sbasis-poly.h
@@ -0,0 +1,56 @@
+/** @file
+ * @brief Conversion between SBasis and Poly.  Not recommended for general use due to instability.
+ *//*
+ * Authors:
+ *      ? <?@?.?>
+ * 
+ * Copyright ?-?  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_SBASIS_POLY_H
+#define LIB2GEOM_SEEN_SBASIS_POLY_H
+
+#include <2geom/polynomial.h>
+#include <2geom/sbasis.h>
+
+namespace Geom{
+
+SBasis poly_to_sbasis(Poly const & p);
+Poly sbasis_to_poly(SBasis const & s);
+
+};
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/sbasis-to-bezier.h b/include/2geom/sbasis-to-bezier.h
new file mode 100644
index 0000000..eadb47b
--- /dev/null
+++ b/include/2geom/sbasis-to-bezier.h
@@ -0,0 +1,87 @@
+/**
+ * \file
+ * \brief Conversion between SBasis and Bezier basis polynomials
+ *//*
+ * Authors:
+ *      ? <?@?.?>
+ * 
+ * Copyright ?-?  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_SBASIS_TO_BEZIER_H
+#define LIB2GEOM_SEEN_SBASIS_TO_BEZIER_H
+
+#include <2geom/d2.h>
+#include <2geom/pathvector.h>
+
+#include <vector>
+
+namespace Geom {
+
+class PathBuilder;
+
+void sbasis_to_bezier (Bezier &bz, SBasis const &sb, size_t sz = 0);
+void sbasis_to_bezier (D2<Bezier> &bz, D2<SBasis> const &sb, size_t sz = 0);
+void sbasis_to_bezier (std::vector<Point> & bz, D2<SBasis> const& sb, size_t sz = 0);
+void sbasis_to_cubic_bezier (std::vector<Point> & bz, D2<SBasis> const& sb);
+void bezier_to_sbasis (SBasis & sb, Bezier const& bz);
+void bezier_to_sbasis (D2<SBasis> & sb, std::vector<Point> const& bz);
+void build_from_sbasis(PathBuilder &pb, D2<SBasis> const &B, double tol, bool only_cubicbeziers);
+
+#if 0
+// this produces a degree k bezier from a degree k sbasis
+Bezier
+sbasis_to_bezier(SBasis const &B, unsigned q = 0);
+
+// inverse
+SBasis bezier_to_sbasis(Bezier const &B);
+
+
+std::vector<Geom::Point>
+sbasis_to_bezier(D2<SBasis> const &B, unsigned q = 0);
+#endif
+
+
+PathVector path_from_piecewise(Piecewise<D2<SBasis> > const &B, double tol, bool only_cubicbeziers = false);
+
+Path path_from_sbasis(D2<SBasis> const &B, double tol, bool only_cubicbeziers = false);
+inline Path cubicbezierpath_from_sbasis(D2<SBasis> const &B, double tol)
+    { return path_from_sbasis(B, tol, true); }
+
+} // end namespace Geom
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/sbasis.h b/include/2geom/sbasis.h
new file mode 100644
index 0000000..5cb0e93
--- /dev/null
+++ b/include/2geom/sbasis.h
@@ -0,0 +1,530 @@
+/** @file
+ * @brief Polynomial in symmetric power basis (S-basis)
+ *//*
+ *  Authors:
+ *   Nathan Hurst <njh@mail.csse.monash.edu.au>
+ *   Michael Sloan <mgsloan@gmail.com>
+ *
+ * Copyright (C) 2006-2007 authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_SBASIS_H
+#define LIB2GEOM_SEEN_SBASIS_H
+#include <cassert>
+#include <iostream>
+#include <utility>
+#include <vector>
+
+#include <2geom/linear.h>
+#include <2geom/interval.h>
+#include <2geom/utils.h>
+#include <2geom/exception.h>
+
+//#define USE_SBASISN 1
+
+
+#if defined(USE_SBASIS_OF)
+
+#include "sbasis-of.h"
+
+#elif defined(USE_SBASISN)
+
+#include "sbasisN.h"
+namespace Geom{
+
+/*** An empty SBasis is identically 0. */
+class SBasis : public SBasisN<1>;
+
+};
+#else
+
+namespace Geom {
+
+/**
+ * @brief Polynomial in symmetric power basis
+ * @ingroup Fragments
+ */
+class SBasis {
+    std::vector<Linear> d;
+    void push_back(Linear const&l) { d.push_back(l); }
+
+public:
+    // As part of our migration away from SBasis isa vector we provide this minimal set of vector interface methods.
+    size_t size() const {return d.size();}
+    typedef std::vector<Linear>::iterator iterator;
+    typedef std::vector<Linear>::const_iterator const_iterator;
+    Linear operator[](unsigned i) const {
+        return d[i];
+    }
+    Linear& operator[](unsigned i) { return d.at(i); }
+    const_iterator begin() const { return d.begin();}
+    const_iterator end() const { return d.end();}
+    iterator begin() { return d.begin();}
+    iterator end() { return d.end();}
+    bool empty() const { return d.size() == 1 && d[0][0] == 0 && d[0][1] == 0; }
+    Linear &back() {return d.back();}
+    Linear const &back() const {return d.back();}
+    void pop_back() {
+		if (d.size() > 1) {
+			d.pop_back();
+		} else {
+			d[0][0] = 0;
+			d[0][1] = 0;
+		}
+	}
+    void resize(unsigned n) { d.resize(std::max<unsigned>(n, 1));}
+    void resize(unsigned n, Linear const& l) { d.resize(std::max<unsigned>(n, 1), l);}
+    void reserve(unsigned n) { d.reserve(n);}
+    void clear() {
+    	d.resize(1);
+    	d[0][0] = 0;
+    	d[0][1] = 0;
+    }
+    void insert(iterator before, const_iterator src_begin, const_iterator src_end) { d.insert(before, src_begin, src_end);}
+    Linear& at(unsigned i) { return d.at(i);}
+    //void insert(Linear* before, int& n, Linear const &l) { d.insert(std::vector<Linear>::iterator(before), n, l);}
+    bool operator==(SBasis const&B) const { return d == B.d;}
+    bool operator!=(SBasis const&B) const { return d != B.d;}
+    
+    SBasis()
+		: d(1, Linear(0, 0))
+	{}
+    explicit SBasis(double a)
+        : d(1, Linear(a, a))
+    {}
+    explicit SBasis(double a, double b)
+        : d(1, Linear(a, b))
+    {}
+    SBasis(SBasis const &a)
+        : d(a.d)
+    {}
+    SBasis(std::vector<Linear> ls)
+        : d(std::move(ls))
+    {}
+    SBasis(Linear const &bo)
+		: d(1, bo)
+	{}
+    SBasis(Linear* bo)
+		: d(1, bo ? *bo : Linear(0, 0))
+    {}
+    explicit SBasis(size_t n, Linear const&l) : d(n, l) {}
+
+    SBasis(Coord c0, Coord c1, Coord c2, Coord c3)
+        : d(2)
+    {
+        d[0][0] = c0;
+        d[1][0] = c1;
+        d[1][1] = c2;
+        d[0][1] = c3;
+    }
+    SBasis(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, Coord c5)
+        : d(3)
+    {
+        d[0][0] = c0;
+        d[1][0] = c1;
+        d[2][0] = c2;
+        d[2][1] = c3;
+        d[1][1] = c4;
+        d[0][1] = c5;
+    }
+    SBasis(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, Coord c5,
+           Coord c6, Coord c7)
+        : d(4)
+    {
+        d[0][0] = c0;
+        d[1][0] = c1;
+        d[2][0] = c2;
+        d[3][0] = c3;
+        d[3][1] = c4;
+        d[2][1] = c5;
+        d[1][1] = c6;
+        d[0][1] = c7;
+    }
+    SBasis(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, Coord c5,
+           Coord c6, Coord c7, Coord c8, Coord c9)
+        : d(5)
+    {
+        d[0][0] = c0;
+        d[1][0] = c1;
+        d[2][0] = c2;
+        d[3][0] = c3;
+        d[4][0] = c4;
+        d[4][1] = c5;
+        d[3][1] = c6;
+        d[2][1] = c7;
+        d[1][1] = c8;
+        d[0][1] = c9;
+    }
+
+    // construct from a sequence of coefficients
+    template <typename Iter>
+    SBasis(Iter first, Iter last) {
+        assert(std::distance(first, last) % 2 == 0);
+        assert(std::distance(first, last) >= 2);
+        for (; first != last; ++first) {
+            --last;
+            push_back(Linear(*first, *last));
+        }
+    }
+
+    //IMPL: FragmentConcept
+    typedef double output_type;
+    inline bool isZero(double eps=EPSILON) const {
+    	assert(size() > 0);
+        for(unsigned i = 0; i < size(); i++) {
+            if(!(*this)[i].isZero(eps)) return false;
+        }
+        return true;
+    }
+    inline bool isConstant(double eps=EPSILON) const {
+    	assert(size() > 0);
+        if(!(*this)[0].isConstant(eps)) return false;
+        for (unsigned i = 1; i < size(); i++) {
+            if(!(*this)[i].isZero(eps)) return false;
+        }
+        return true;
+    }
+
+    bool isFinite() const;
+    inline Coord at0() const { return (*this)[0][0]; }
+    inline Coord &at0() { return (*this)[0][0]; }
+    inline Coord at1() const { return (*this)[0][1]; }
+    inline Coord &at1() { return (*this)[0][1]; }
+    
+    int degreesOfFreedom() const { return size()*2;}
+
+    double valueAt(double t) const {
+    	assert(size() > 0);
+        double s = t*(1-t);
+        double p0 = 0, p1 = 0;
+        for(unsigned k = size(); k > 0; k--) {
+            const Linear &lin = (*this)[k-1];
+            p0 = p0*s + lin[0];
+            p1 = p1*s + lin[1];
+        }
+        return (1-t)*p0 + t*p1;
+    }
+    //double valueAndDerivative(double t, double &der) const {
+    //}
+    double operator()(double t) const {
+        return valueAt(t);
+    }
+
+    std::vector<double> valueAndDerivatives(double t, unsigned n) const;
+
+    SBasis toSBasis() const { return SBasis(*this); }
+
+    double tailError(unsigned tail) const;
+
+// compute f(g)
+    SBasis operator()(SBasis const & g) const;
+
+//MUTATOR PRISON
+    //remove extra zeros
+    void normalize() {
+        while(size() > 1 && back().isZero(0))
+            pop_back();
+    }
+
+    void truncate(unsigned k) { if(k < size()) resize(std::max<size_t>(k, 1)); }
+private:
+    void derive(); // in place version
+};
+
+//TODO: figure out how to stick this in linear, while not adding an sbasis dep
+inline SBasis Linear::toSBasis() const { return SBasis(*this); }
+
+//implemented in sbasis-roots.cpp
+OptInterval bounds_exact(SBasis const &a);
+OptInterval bounds_fast(SBasis const &a, int order = 0);
+OptInterval bounds_local(SBasis const &a, const OptInterval &t, int order = 0);
+
+/** Returns a function which reverses the domain of a.
+ \param a sbasis function
+ \relates SBasis
+
+useful for reversing a parameteric curve.
+*/
+inline SBasis reverse(SBasis const &a) {
+    SBasis result(a.size(), Linear());
+    
+    for(unsigned k = 0; k < a.size(); k++)
+        result[k] = reverse(a[k]);
+    return result;
+}
+
+//IMPL: ScalableConcept
+inline SBasis operator-(const SBasis& p) {
+    if(p.isZero()) return SBasis();
+    SBasis result(p.size(), Linear());
+        
+    for(unsigned i = 0; i < p.size(); i++) {
+        result[i] = -p[i];
+    }
+    return result;
+}
+SBasis operator*(SBasis const &a, double k);
+inline SBasis operator*(double k, SBasis const &a) { return a*k; }
+inline SBasis operator/(SBasis const &a, double k) { return a*(1./k); }
+SBasis& operator*=(SBasis& a, double b);
+inline SBasis& operator/=(SBasis& a, double b) { return (a*=(1./b)); }
+
+//IMPL: AddableConcept
+SBasis operator+(const SBasis& a, const SBasis& b);
+SBasis operator-(const SBasis& a, const SBasis& b);
+SBasis& operator+=(SBasis& a, const SBasis& b);
+SBasis& operator-=(SBasis& a, const SBasis& b);
+
+//TODO: remove?
+/*inline SBasis operator+(const SBasis & a, Linear const & b) {
+    if(b.isZero()) return a;
+    if(a.isZero()) return b;
+    SBasis result(a);
+    result[0] += b;
+    return result;
+}
+inline SBasis operator-(const SBasis & a, Linear const & b) {
+    if(b.isZero()) return a;
+    SBasis result(a);
+    result[0] -= b;
+    return result;
+}
+inline SBasis& operator+=(SBasis& a, const Linear& b) {
+    if(a.isZero())
+        a.push_back(b);
+    else
+        a[0] += b;
+    return a;
+}
+inline SBasis& operator-=(SBasis& a, const Linear& b) {
+    if(a.isZero())
+        a.push_back(-b);
+    else
+        a[0] -= b;
+    return a;
+    }*/
+
+//IMPL: OffsetableConcept
+inline SBasis operator+(const SBasis & a, double b) {
+    if(a.isZero()) return Linear(b, b);
+    SBasis result(a);
+    result[0] += b;
+    return result;
+}
+inline SBasis operator-(const SBasis & a, double b) {
+    if(a.isZero()) return Linear(-b, -b);
+    SBasis result(a);
+    result[0] -= b;
+    return result;
+}
+inline SBasis& operator+=(SBasis& a, double b) {
+    if(a.isZero())
+        a = SBasis(Linear(b,b));
+    else
+        a[0] += b;
+    return a;
+}
+inline SBasis& operator-=(SBasis& a, double b) {
+    if(a.isZero())
+        a = SBasis(Linear(-b,-b));
+    else
+        a[0] -= b;
+    return a;
+}
+
+SBasis shift(SBasis const &a, int sh);
+SBasis shift(Linear const &a, int sh);
+
+inline SBasis truncate(SBasis const &a, unsigned terms) {
+    SBasis c;
+    c.insert(c.begin(), a.begin(), a.begin() + std::min(terms, (unsigned)a.size()));
+    return c;
+}
+
+SBasis multiply(SBasis const &a, SBasis const &b);
+// This performs a multiply and accumulate operation in about the same time as multiply.  return a*b + c
+SBasis multiply_add(SBasis const &a, SBasis const &b, SBasis c);
+
+SBasis integral(SBasis const &c);
+SBasis derivative(SBasis const &a);
+
+SBasis sqrt(SBasis const &a, int k);
+
+// return a kth order approx to 1/a)
+SBasis reciprocal(Linear const &a, int k);
+SBasis divide(SBasis const &a, SBasis const &b, int k);
+
+inline SBasis operator*(SBasis const & a, SBasis const & b) {
+    return multiply(a, b);
+}
+
+inline SBasis& operator*=(SBasis& a, SBasis const & b) {
+    a = multiply(a, b);
+    return a;
+}
+
+/** Returns the degree of the first non zero coefficient.
+ \param a sbasis function
+ \param tol largest abs val considered 0
+ \return first non zero coefficient
+ \relates SBasis
+*/
+inline unsigned 
+valuation(SBasis const &a, double tol=0){
+    unsigned val=0;
+    while( val<a.size() &&
+           fabs(a[val][0])<tol &&
+           fabs(a[val][1])<tol ) 
+        val++;
+    return val;
+}
+
+// a(b(t))
+SBasis compose(SBasis const &a, SBasis const &b);
+SBasis compose(SBasis const &a, SBasis const &b, unsigned k);
+SBasis inverse(SBasis a, int k);
+//compose_inverse(f,g)=compose(f,inverse(g)), but is numerically more stable in some good cases...
+//TODO: requires g(0)=0 & g(1)=1 atm. generalization should be obvious.
+SBasis compose_inverse(SBasis const &f, SBasis const &g, unsigned order=2, double tol=1e-3);
+
+/** Returns the sbasis on domain [0,1] that was t on [from, to]
+ \param t sbasis function
+ \param from,to interval
+ \return sbasis
+ \relates SBasis
+*/
+SBasis portion(const SBasis &t, double from, double to);
+inline SBasis portion(const SBasis &t, Interval const &ivl) { return portion(t, ivl.min(), ivl.max()); }
+
+// compute f(g)
+inline SBasis
+SBasis::operator()(SBasis const & g) const {
+    return compose(*this, g);
+}
+ 
+inline std::ostream &operator<< (std::ostream &out_file, const Linear &bo) {
+    out_file << "{" << bo[0] << ", " << bo[1] << "}";
+    return out_file;
+}
+
+inline std::ostream &operator<< (std::ostream &out_file, const SBasis & p) {
+    for(unsigned i = 0; i < p.size(); i++) {
+        if (i != 0) {
+            out_file << " + ";
+        }
+        out_file << p[i] << "s^" << i;
+    }
+    return out_file;
+}
+
+// These are deprecated, use sbasis-math.h versions if possible
+SBasis sin(Linear bo, int k);
+SBasis cos(Linear bo, int k);
+
+std::vector<double> roots(SBasis const & s);
+std::vector<double> roots(SBasis const & s, Interval const inside);
+std::vector<std::vector<double> > multi_roots(SBasis const &f,
+                                 std::vector<double> const &levels,
+                                 double htol=1e-7,
+                                 double vtol=1e-7,
+                                 double a=0,
+                                 double b=1);
+
+//--------- Levelset like functions -----------------------------------------------------
+
+/** Solve f(t) = v +/- tolerance. The collection of intervals where
+ *     v - vtol <= f(t) <= v+vtol
+ *   is returned (with a precision tol on the boundaries).
+    \param f sbasis function
+    \param level the value of v.
+    \param vtol: error tolerance on v.
+    \param a, b limit search on domain [a,b]
+    \param tol: tolerance on the result bounds.
+    \returns a vector of intervals.
+*/
+std::vector<Interval> level_set (SBasis const &f,
+		double level,
+		double vtol = 1e-5,
+		double a=0.,
+		double b=1.,
+		double tol = 1e-5);
+
+/** Solve f(t)\in I=[u,v], which defines a collection of intervals (J_k). More precisely,
+ *  a collection (J'_k) is returned with J'_k = J_k up to a given tolerance.
+    \param f sbasis function
+    \param level: the given interval of deisred values for f.
+    \param a, b limit search on domain [a,b]
+    \param tol: tolerance on the bounds of the result.
+    \returns a vector of intervals.
+*/
+std::vector<Interval> level_set (SBasis const &f,
+		Interval const &level,
+		double a=0.,
+		double b=1.,
+		double tol = 1e-5);
+
+/** 'Solve' f(t) = v +/- tolerance for several values of v at once.
+    \param f sbasis function
+    \param levels vector of values, that should be sorted.
+    \param vtol: error tolerance on v.
+    \param a, b limit search on domain [a,b]
+    \param tol: the bounds of the returned intervals are exact up to that tolerance.
+    \returns a vector of vectors of intervals.
+*/
+std::vector<std::vector<Interval> > level_sets (SBasis const &f,
+		std::vector<double> const &levels,
+		double a=0.,
+		double b=1.,
+		double vtol = 1e-5,
+		double tol = 1e-5);
+
+/** 'Solve' f(t)\in I=[u,v] for several intervals I at once.
+    \param f sbasis function
+    \param levels vector of 'y' intervals, that should be disjoints and sorted.
+    \param a, b limit search on domain [a,b]
+    \param tol: the bounds of the returned intervals are exact up to that tolerance.
+    \returns a vector of vectors of intervals.
+*/
+std::vector<std::vector<Interval> > level_sets (SBasis const &f,
+		std::vector<Interval> const &levels,
+		double a=0.,
+		double b=1.,
+		double tol = 1e-5);
+
+}
+#endif
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
+#endif
diff --git a/include/2geom/solver.h b/include/2geom/solver.h
new file mode 100644
index 0000000..5b082cb
--- /dev/null
+++ b/include/2geom/solver.h
@@ -0,0 +1,88 @@
+/**
+ * \file
+ * \brief Finding roots of Bernstein-Bezier polynomials
+ *//*
+ * Authors:
+ *      ? <?@?.?>
+ *
+ * Copyright ?-?  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_SOLVER_H
+#define LIB2GEOM_SEEN_SOLVER_H
+
+#include <2geom/point.h>
+#include <2geom/sbasis.h>
+#include <vector>
+
+namespace Geom {
+
+	class Point;
+	class Bezier;
+
+unsigned
+crossing_count(Geom::Point const *V,	/*  Control pts of Bezier curve	*/
+	       unsigned degree);	/*  Degree of Bezier curve */
+void
+find_parametric_bezier_roots(
+    Geom::Point const *w, /* The control points  */
+    unsigned degree,	/* The degree of the polynomial */
+    std::vector<double> & solutions,	/* RETURN candidate t-values */
+    unsigned depth);	/* The depth of the recursion */
+
+unsigned
+crossing_count(double const *V,	/*  Control pts of Bezier curve	*/
+	       unsigned degree,	/*  Degree of Bezier curve */
+	       double left_t, double right_t);
+
+
+void
+find_bernstein_roots(
+    double const *w, /* The control points  */
+    unsigned degree,	/* The degree of the polynomial */
+    std::vector<double> & solutions,	/* RETURN candidate t-values */
+    unsigned depth,	/* The depth of the recursion */
+    double left_t=0, double right_t=1, bool use_secant=true);
+
+};
+
+void
+find_bernstein_roots(std::vector<double> &solutions, /* RETURN candidate t-values */
+                     Geom::Bezier const& bz,
+                     double left_t, double right_t);
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/svg-path-parser.h b/include/2geom/svg-path-parser.h
new file mode 100644
index 0000000..e25316c
--- /dev/null
+++ b/include/2geom/svg-path-parser.h
@@ -0,0 +1,199 @@
+/**
+ * \file
+ * \brief  parse SVG path specifications
+ *
+ * Copyright 2007 MenTaLguY <mental@rydia.net>
+ * Copyright 2007 Aaron Spike <aaron@ekips.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_SVG_PATH_PARSER_H
+#define LIB2GEOM_SEEN_SVG_PATH_PARSER_H
+
+#include <iostream>
+#include <iterator>
+#include <stdexcept>
+#include <vector>
+#include <cstdio>
+#include <2geom/exception.h>
+#include <2geom/point.h>
+#include <2geom/path-sink.h>
+#include <2geom/forward.h>
+
+namespace Geom {
+
+/** @brief Read SVG path data and feed it to a PathSink
+ *
+ * This class provides an interface to an SVG path data parser written in Ragel.
+ * It supports parsing the path data either at once or block-by-block.
+ * Use the parse() functions to parse complete data and the feed() and finish()
+ * functions to parse partial data.
+ *
+ * The parser will call the appropriate methods on the PathSink supplied
+ * at construction. To store the path in memory as a PathVector, pass
+ * a PathBuilder. You can also use one of the freestanding helper functions
+ * if you don't need to parse data block-by-block.
+ *
+ * @ingroup Paths
+ */
+class SVGPathParser {
+public:
+    SVGPathParser(PathSink &sink);
+    ~SVGPathParser();
+
+    /** @brief Reset internal state.
+     * Discards the internal state associated with partially parsed data,
+     * letting you start from scratch. Note that any partial data written
+     * to the path sink is not affected - you need to clear it yourself. */
+    void reset();
+
+    /** @brief Parse a C-style string.
+     * The path sink is flushed and the internal state is reset after this call.
+     * Note that the state is not reset before this method, so you can use it to
+     * process the last block of partial data.
+     * @param str String to parse
+     * @param len Length of string or -1 if null-terminated */
+    void parse(char const *str, int len = -1);
+    /** @brief Parse an STL string. */
+    void parse(std::string const &s);
+
+    /** @brief Parse a part of path data stored in a C-style string.
+     * This method does not reset internal state, so it can be called multiple
+     * times to parse successive blocks of a longer SVG path data string.
+     * To finish parsing, call finish() after the final block or call parse()
+     * with the last block of data.
+     * @param str String to parse
+     * @param len Length of string or -1 if null-terminated */
+    void feed(char const *str, int len = -1);
+    /** @brief Parse a part of path data stored in an STL string. */
+    void feed(std::string const &s);
+
+    /** @brief Finalize parsing.
+     * After the last block of data was submitted with feed(), call this method
+     * to finalize parsing, flush the path sink and reset internal state.
+     * You should not call this after parse(). */
+    void finish();
+
+    /** @brief Set the threshold for considering the closing segment degenerate.
+     * When the current point was reached by a relative command, is closer
+     * to the initial point of the path than the specified threshold
+     * and a 'z' is encountered, the last segment will be adjusted instead so that
+     * the closing segment has exactly zero length. This is useful when reading
+     * SVG 1.1 paths that have non-linear final segments written in relative
+     * coordinates, which always suffer from some loss of precision. SVG 2
+     * allows alternate placement of 'z' which does not have this problem. */
+    void setZSnapThreshold(Coord threshold) { _z_snap_threshold = threshold; }
+    Coord zSnapThreshold() const { return _z_snap_threshold; }
+
+private:
+    bool _absolute;
+    bool _moveto_was_absolute;
+    Point _current;
+    Point _initial;
+    Point _cubic_tangent;
+    Point _quad_tangent;
+    std::vector<Coord> _params;
+    PathSink &_sink;
+    Coord _z_snap_threshold;
+    Curve *_curve;
+
+    int cs;
+    std::string _number_part;
+
+    void _push(Coord value);
+    Coord _pop();
+    bool _pop_flag();
+    Coord _pop_coord(Geom::Dim2 axis);
+    Point _pop_point();
+    void _moveTo(Point const &p);
+    void _lineTo(Point const &p);
+    void _curveTo(Point const &c0, Point const &c1, Point const &p);
+    void _quadTo(Point const &c, Point const &p);
+    void _arcTo(double rx, double ry, double angle,
+                bool large_arc, bool sweep, Point const &p);
+    void _closePath();
+    void _pushCurve(Curve *c);
+
+    void _parse(char const *str, char const *strend, bool finish);
+};
+
+/** @brief Feed SVG path data to the specified sink
+ * @ingroup Paths */
+void parse_svg_path(char const *str, PathSink &sink);
+/** @brief Feed SVG path data to the specified sink
+ * @ingroup Paths */
+inline void parse_svg_path(std::string const &str, PathSink &sink) {
+    parse_svg_path(str.c_str(), sink);
+}
+/** Feed SVG path data from a C stream to the specified sink
+ * @ingroup Paths */
+void parse_svg_path_file(FILE *fi, PathSink &sink);
+
+/** @brief Create path vector from SVG path data stored in a C string
+ * @ingroup Paths */
+inline PathVector parse_svg_path(char const *str) {
+    PathVector ret;
+    SubpathInserter iter(ret);
+    PathIteratorSink<SubpathInserter> generator(iter);
+
+    parse_svg_path(str, generator);
+    return ret;
+}
+
+/** @brief Create path vector from a C stream with SVG path data
+ * @ingroup Paths */
+inline PathVector read_svgd_f(FILE * fi) {
+    PathVector ret;
+    SubpathInserter iter(ret);
+    PathIteratorSink<SubpathInserter> generator(iter);
+
+    parse_svg_path_file(fi, generator);
+    return ret;
+}
+
+/** @brief Create path vector from SVG path data stored in a file
+ * @ingroup Paths */
+inline PathVector read_svgd(char const *filename) {
+    FILE* fi = fopen(filename, "r");
+    if(fi == NULL) throw(std::runtime_error("Error opening file"));
+    PathVector out = read_svgd_f(fi);
+    fclose(fi);
+    return out;
+}
+
+} // end namespace Geom
+
+#endif
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/svg-path-writer.h b/include/2geom/svg-path-writer.h
new file mode 100644
index 0000000..92a80ec
--- /dev/null
+++ b/include/2geom/svg-path-writer.h
@@ -0,0 +1,122 @@
+/** @file
+ * @brief Path sink which writes an SVG-compatible command string
+ *//*
+ * Authors:
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ * 
+ * Copyright 2014 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_SVG_PATH_WRITER_H
+#define LIB2GEOM_SEEN_SVG_PATH_WRITER_H
+
+#include <2geom/path-sink.h>
+#include <sstream>
+
+namespace Geom {
+
+/** @brief Serialize paths to SVG path data strings.
+ * You can access the generated string by calling the str() method.
+ * @ingroup Paths
+ */
+class SVGPathWriter
+    : public PathSink
+{
+public:
+    SVGPathWriter();
+    ~SVGPathWriter() override {}
+
+    void moveTo(Point const &p) override;
+    void lineTo(Point const &p) override;
+    void quadTo(Point const &c, Point const &p) override;
+    void curveTo(Point const &c0, Point const &c1, Point const &p) override;
+    void arcTo(double rx, double ry, double angle,
+               bool large_arc, bool sweep, Point const &p) override;
+    void closePath() override;
+    void flush() override;
+
+    /// Clear any path data written so far.
+    void clear();
+
+    /** @brief Set output precision.
+     * When the parameter is negative, the path writer enters a verbatim mode
+     * which preserves all values exactly. */
+    void setPrecision(int prec);
+
+    /** @brief Enable or disable length optimization.
+     * 
+     * When set to true, the path writer will optimize the generated path data
+     * for minimum length. However, this will make the data less readable,
+     * because spaces between commands and coordinates will be omitted where
+     * unnecessary for correct parsing.
+     *
+     * When set to false, the string will be a straightforward, partially redundant
+     * representation of the passed commands, optimized for readability.
+     * Commands and coordinates will always be separated by spaces and the command
+     * symbol will not be omitted for multiple consecutive commands of the same type.
+     *
+     * Length optimization is turned off by default. */
+    void setOptimize(bool opt) { _optimize = opt; }
+
+    /** @brief Enable or disable the use of V, H, T and S commands where possible.
+     * Shorthands are turned on by default. */
+    void setUseShorthands(bool use) { _use_shorthands = use; }
+
+    /// Retrieve the generated path data string.
+    std::string str() const { return _s.str(); }
+
+private:
+    void _setCommand(char cmd);
+    std::string _formatCoord(Coord par);
+
+    std::ostringstream _s, _ns;
+    std::vector<Coord> _current_pars;
+    Point _subpath_start;
+    Point _current;
+    Point _quad_tangent;
+    Point _cubic_tangent;
+    Coord _epsilon;
+    int _precision;
+    bool _optimize;
+    bool _use_shorthands;
+    char _command;
+};
+
+std::string write_svg_path(PathVector const &pv, int prec = -1, bool optimize = false, bool shorthands = true);
+
+} // namespace Geom
+
+#endif // LIB2GEOM_SEEN_SVG_PATH_WRITER_H
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/sweep-bounds.h b/include/2geom/sweep-bounds.h
new file mode 100644
index 0000000..e0ebf29
--- /dev/null
+++ b/include/2geom/sweep-bounds.h
@@ -0,0 +1,62 @@
+/**
+ * \file
+ * \brief Sweepline intersection of groups of rectangles
+ *//*
+ * Authors:
+ *      ? <?@?.?>
+ * 
+ * Copyright ?-?  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_SWEEP_H
+#define LIB2GEOM_SEEN_SWEEP_H
+
+#include <vector>
+#include <2geom/d2.h>
+
+namespace Geom {
+
+std::vector<std::vector<unsigned> >
+sweep_bounds(std::vector<Rect>, Dim2 dim = X);
+
+std::vector<std::vector<unsigned> >
+sweep_bounds(std::vector<Rect>, std::vector<Rect>, Dim2 dim = X);
+
+}
+
+#endif
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/sweeper.h b/include/2geom/sweeper.h
new file mode 100644
index 0000000..233d181
--- /dev/null
+++ b/include/2geom/sweeper.h
@@ -0,0 +1,189 @@
+/** @file
+ * @brief Class for implementing sweepline algorithms
+ *//*
+  * Authors:
+  *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+  *
+  * Copyright 2015 Authors
+  *
+  * This library is free software; you can redistribute it and/or
+  * modify it either under the terms of the GNU Lesser General Public
+  * License version 2.1 as published by the Free Software Foundation
+  * (the "LGPL") or, at your option, under the terms of the Mozilla
+  * Public License Version 1.1 (the "MPL"). If you do not alter this
+  * notice, a recipient may use your version of this file under either
+  * the MPL or the LGPL.
+  *
+  * You should have received a copy of the LGPL along with this library
+  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+  * You should have received a copy of the MPL along with this library
+  * in the file COPYING-MPL-1.1
+  *
+  * The contents of this file are subject to the Mozilla Public License
+  * Version 1.1 (the "License"); you may not use this file except in
+  * compliance with the License. You may obtain a copy of the License at
+  * http://www.mozilla.org/MPL/
+  *
+  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+  * the specific language governing rights and limitations.
+  */
+
+#ifndef LIB2GEOM_SEEN_SWEEPER_H
+#define LIB2GEOM_SEEN_SWEEPER_H
+
+#include <2geom/coord.h>
+#include <algorithm>
+#include <vector>
+#include <boost/intrusive/list.hpp>
+
+namespace Geom {
+
+// exposition only
+template <typename Item>
+class SweepVector {
+public:
+    typedef typename std::vector<Item>::const_iterator ItemIterator;
+
+    SweepVector(std::vector<Item> const &v)
+        : _items(v)
+    {}
+
+    std::vector<Item> const &items() { return _items; }
+    Interval itemBounds(ItemIterator /*ii*/) { return Interval(); }
+
+    void addActiveItem(ItemIterator /*ii*/) {}
+    void removeActiveItem(ItemIterator /*ii*/) {}
+
+private:
+    std::vector<Item> const &_items;
+};
+
+/** @brief Generic sweepline algorithm.
+ *
+ * This class encapsulates an algorithm that sorts the objects according
+ * to their bounds, then moves an imaginary line (sweepline) over those
+ * bounds from left to right. Objects are added to the active list when
+ * the line starts intersecting their bounds, and removed when it completely
+ * passes over them.
+ *
+ * To use this, create a class that exposes the following methods:
+ * - Range items() - returns a forward iterable range of items that will be swept.
+ * - Interval itemBounds(iterator i) - given an iterator from the above range,
+ *   compute the bounding interval of the referenced item in the direction of sweep.
+ * - void addActiveItem(iterator i) - add an item to the active list.
+ * - void removeActiveItem(iterator i) - remove an item from the active list.
+ *
+ * Create the object, then instantiate this template with the above class
+ * as the template parameter, pass it the constructed object of the class,
+ * and call the process() method.
+ *
+ * A good choice for the active list is a Boost intrusive list, which allows
+ * you to get an iterator from a value in constant time.
+ *
+ * Look in path.cpp for example usage.
+ *
+ * @tparam Item The type of items to sweep
+ * @tparam SweepTraits Traits class that defines the items' bounds,
+ *   how to interpret them and how to sort the events
+ * @ingroup Utilities
+ */
+template <typename SweepSet>
+class Sweeper {
+public:
+    typedef typename SweepSet::ItemIterator Iter;
+
+    explicit Sweeper(SweepSet &set)
+        : _set(set)
+    {
+        std::size_t sz = std::distance(set.items().begin(), set.items().end());
+        _entry_events.reserve(sz);
+        _exit_events.reserve(sz);
+    }
+
+    /** @brief Process entry and exit events.
+     * This will iterate over all inserted items, calling the methods
+     * addActiveItem and removeActiveItem on the SweepSet passed at construction
+     * according to the order of the boundaries of each item. */
+    void process() {
+        if (_set.items().empty()) return;
+
+        Iter last = _set.items().end();
+        for (Iter i = _set.items().begin(); i != last; ++i) {
+            Interval b = _set.itemBounds(i);
+            // guard against NANs
+            assert(b.min() == b.min() && b.max() == b.max());
+            _entry_events.push_back(Event(b.max(), i));
+            _exit_events.push_back(Event(b.min(), i));
+        }
+
+        std::make_heap(_entry_events.begin(), _entry_events.end());
+        std::make_heap(_exit_events.begin(), _exit_events.end());
+
+        Event next_entry = _get_next(_entry_events);
+        Event next_exit = _get_next(_exit_events);
+
+        while (next_entry || next_exit) {
+            assert(next_exit);
+
+            if (!next_entry || next_exit > next_entry) {
+                // exit event - remove record from active list
+                _set.removeActiveItem(next_exit.item);
+                next_exit = _get_next(_exit_events);
+            } else {
+                // entry event - add record to active list
+                _set.addActiveItem(next_entry.item);
+                next_entry = _get_next(_entry_events);
+            }
+        }
+    }
+
+private:
+    struct Event
+        : boost::totally_ordered<Event>
+    {
+        Coord coord;
+        Iter item;
+
+        Event(Coord c, Iter const &i)
+            : coord(c), item(i)
+        {}
+        Event()
+            : coord(nan("")), item()
+        {}
+        bool operator<(Event const &other) const { return coord < other.coord; }
+        bool operator==(Event const &other) const { return coord == other.coord; }
+        operator bool() const { return !std::isnan(coord); }
+    };
+
+    static Event _get_next(std::vector<Event> &heap) {
+        if (heap.empty()) {
+            Event e;
+            return e;
+        }
+        std::pop_heap(heap.begin(), heap.end());
+        Event ret = heap.back();
+        heap.pop_back();
+        return ret;
+    }
+
+    SweepSet &_set;
+    std::vector<Event> _entry_events;
+    std::vector<Event> _exit_events;
+};
+
+} // namespace Geom
+
+#endif // !LIB2GEOM_SEEN_SWEEPER_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/symbolic/determinant-minor.h b/include/2geom/symbolic/determinant-minor.h
new file mode 100644
index 0000000..d70c397
--- /dev/null
+++ b/include/2geom/symbolic/determinant-minor.h
@@ -0,0 +1,175 @@
+/*
+ *  GiNaC Copyright (C) 1999-2008 Johannes Gutenberg University Mainz, Germany
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _GEOM_SL_DETERMINANT_MINOR_H_
+#define _GEOM_SL_DETERMINANT_MINOR_H_
+
+#include <map>
+
+
+namespace Geom { namespace SL {
+
+/*
+ * determinant_minor
+ * This routine has been taken from the ginac project
+ * and adapted as needed; comments are the original ones.
+ */
+
+/** Recursive determinant for small matrices having at least one symbolic
+ *  entry.  The basic algorithm, known as Laplace-expansion, is enhanced by
+ *  some bookkeeping to avoid calculation of the same submatrices ("minors")
+ *  more than once.  According to W.M.Gentleman and S.C.Johnson this algorithm
+ *  is better than elimination schemes for matrices of sparse multivariate
+ *  polynomials and also for matrices of dense univariate polynomials if the
+ *  matrix' dimesion is larger than 7.
+ *
+ *  @return the determinant as a new expression (in expanded form)
+ *  @see matrix::determinant() */
+
+template< typename Coeff >
+Coeff determinant_minor(Matrix<Coeff> const& M)
+{
+    assert(M.rows() == M.columns());
+    // for small matrices the algorithm does not make any sense:
+    const unsigned int n = M.columns();
+    if (n == 1)
+        return M(0,0);
+    if (n == 2)
+        return (M(0,0) * M(1,1) - M(0,1) * M(1,0));
+    if (n == 3)
+        return ( M(0,0)*M(1,1)*M(2,2) + M(0,2)*M(1,0)*M(2,1)
+                + M(0,1)*M(1,2)*M(2,0) - M(0,2)*M(1,1)*M(2,0)
+                - M(0,0)*M(1,2)*M(2,1) - M(0,1)*M(1,0)*M(2,2) );
+
+    // This algorithm can best be understood by looking at a naive
+    // implementation of Laplace-expansion, like this one:
+    // ex det;
+    // matrix minorM(this->rows()-1,this->cols()-1);
+    // for (unsigned r1=0; r1<this->rows(); ++r1) {
+    //     // shortcut if element(r1,0) vanishes
+    //     if (m[r1*col].is_zero())
+    //         continue;
+    //     // assemble the minor matrix
+    //     for (unsigned r=0; r<minorM.rows(); ++r) {
+    //         for (unsigned c=0; c<minorM.cols(); ++c) {
+    //             if (r<r1)
+    //                 minorM(r,c) = m[r*col+c+1];
+    //             else
+    //                 minorM(r,c) = m[(r+1)*col+c+1];
+    //         }
+    //     }
+    //     // recurse down and care for sign:
+    //     if (r1%2)
+    //         det -= m[r1*col] * minorM.determinant_minor();
+    //     else
+    //         det += m[r1*col] * minorM.determinant_minor();
+    // }
+    // return det.expand();
+    // What happens is that while proceeding down many of the minors are
+    // computed more than once.  In particular, there are binomial(n,k)
+    // kxk minors and each one is computed factorial(n-k) times.  Therefore
+    // it is reasonable to store the results of the minors.  We proceed from
+    // right to left.  At each column c we only need to retrieve the minors
+    // calculated in step c-1.  We therefore only have to store at most
+    // 2*binomial(n,n/2) minors.
+
+    // Unique flipper counter for partitioning into minors
+    std::vector<unsigned int> Pkey;
+    Pkey.reserve(n);
+    // key for minor determinant (a subpartition of Pkey)
+    std::vector<unsigned int> Mkey;
+    Mkey.reserve(n-1);
+    // we store our subminors in maps, keys being the rows they arise from
+    typedef typename std::map<std::vector<unsigned>, Coeff> Rmap;
+    typedef typename std::map<std::vector<unsigned>, Coeff>::value_type Rmap_value;
+    Rmap A;
+    Rmap B;
+    Coeff det;
+    // initialize A with last column:
+    for (unsigned int r = 0; r < n; ++r)
+    {
+        Pkey.erase(Pkey.begin(),Pkey.end());
+        Pkey.push_back(r);
+        A.insert(Rmap_value(Pkey,M(r,n-1)));
+    }
+    // proceed from right to left through matrix
+    for (int c = n-2; c >= 0; --c)
+    {
+        Pkey.erase(Pkey.begin(),Pkey.end());  // don't change capacity
+        Mkey.erase(Mkey.begin(),Mkey.end());
+        for (unsigned int i = 0; i < n-c; ++i)
+            Pkey.push_back(i);
+        unsigned int fc = 0;  // controls logic for our strange flipper counter
+        do
+        {
+            det = Geom::SL::zero<Coeff>()();
+            for (unsigned int r = 0; r < n-c; ++r)
+            {
+                // maybe there is nothing to do?
+                if (M(Pkey[r], c).is_zero())
+                    continue;
+                // create the sorted key for all possible minors
+                Mkey.erase(Mkey.begin(),Mkey.end());
+                for (unsigned int i = 0; i < n-c; ++i)
+                    if (i != r)
+                        Mkey.push_back(Pkey[i]);
+                // Fetch the minors and compute the new determinant
+                if (r % 2)
+                    det -= M(Pkey[r],c)*A[Mkey];
+                else
+                    det += M(Pkey[r],c)*A[Mkey];
+            }
+            // store the new determinant at its place in B:
+            if (!det.is_zero())
+                B.insert(Rmap_value(Pkey,det));
+            // increment our strange flipper counter
+            for (fc = n-c; fc > 0; --fc)
+            {
+                ++Pkey[fc-1];
+                if (Pkey[fc-1]<fc+c)
+                    break;
+            }
+            if (fc < n-c && fc > 0)
+                for (unsigned int j = fc; j < n-c; ++j)
+                    Pkey[j] = Pkey[j-1]+1;
+        } while(fc);
+        // next column, so change the role of A and B:
+        A.swap(B);
+        B.clear();
+    }
+
+    return det;
+}
+
+
+
+} /*end namespace Geom*/  } /*end namespace SL*/
+
+#endif  // _GEOM_SL_DETERMINANT_MINOR_H_
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/symbolic/implicit.h b/include/2geom/symbolic/implicit.h
new file mode 100644
index 0000000..82d77cd
--- /dev/null
+++ b/include/2geom/symbolic/implicit.h
@@ -0,0 +1,353 @@
+/*
+ * Routines to compute the implicit equation of a parametric polynomial curve
+ *
+ * Authors:
+ *      Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 2008  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+
+#ifndef _GEOM_SL_IMPLICIT_H_
+#define _GEOM_SL_IMPLICIT_H_
+
+
+
+#include <2geom/symbolic/multipoly.h>
+#include <2geom/symbolic/matrix.h>
+
+
+#include <2geom/exception.h>
+
+#include <array>
+
+
+namespace Geom { namespace SL {
+
+typedef MultiPoly<1, double> MVPoly1;
+typedef MultiPoly<2, double> MVPoly2;
+typedef MultiPoly<3, double> MVPoly3;
+typedef std::array<MVPoly1, 3> poly_vector_type;
+typedef std::array<poly_vector_type, 2> basis_type;
+typedef std::array<double, 3> coeff_vector_type;
+
+namespace detail {
+
+/*
+ *  transform a univariate polynomial f(t) in a 3-variate polynomial
+ *  p(t, x, y) = f(t) * x^i * y^j
+ */
+inline
+void poly1_to_poly3(MVPoly3 & p3, MVPoly1 const& p1, size_t i, size_t j)
+{
+    multi_index_type I = make_multi_index(0, i, j);
+    for (; I[0] < p1.get_poly().size(); ++I[0])
+    {
+        p3.coefficient(I, p1[I[0]]);
+    }
+}
+
+/*
+ *  evaluates the degree of a poly_vector_type, such a degree is defined as:
+ *  deg({p[0](t), p[1](t), p[2](t)}) := {max(deg(p[i](t)), i = 0, 1, 2), k}
+ *  here k is the index where the max is achieved,
+ *  if deg(p[i](t)) == deg(p[j](t)) and i < j then k = i
+ */
+inline
+std::pair<size_t, size_t> deg(poly_vector_type const& p)
+{
+    std::pair<size_t, size_t> d;
+    d.first = p[0].get_poly().real_degree();
+    d.second = 0;
+    size_t k = p[1].get_poly().real_degree();
+    if (d.first < k)
+    {
+        d.first = k;
+        d.second = 1;
+    }
+    k = p[2].get_poly().real_degree();
+    if (d.first < k)
+    {
+        d.first = k;
+        d.second = 2;
+    }
+    return d;
+}
+
+} // end namespace detail
+
+
+/*
+ * A polynomial parametrization could be seen as 1-variety V in R^3,
+ * intersection of two surfaces x = f(t), y = g(t), this variety V has
+ * attached an ideal I in the ring of polynomials in t, x, y with coefficients
+ * on reals; a basis of generators for I is given by p(t, x, y) = x - f(t),
+ * q(t, x, y) = y - g(t); such a basis has the nice property that could be
+ * written as a couple of vectors of dim 3 with entries in R[t]; the original
+ * polinomials p and q can be obtained by doing a dot product between each
+ * vector and the vector {x, y, 1}
+ * As reference you can read the text book:
+ * Ideals, Varieties and Algorithms by Cox, Little, O'Shea
+ */
+inline
+void make_initial_basis(basis_type& b, MVPoly1 const& p, MVPoly1 const& q)
+{
+    // first basis vector
+    b[0][0] = 1;
+    b[0][1] = 0;
+    b[0][2] = -p;
+
+    // second basis vector
+    b[1][0] = 0;
+    b[1][1] = 1;
+    b[1][2] = -q;
+}
+
+/*
+ * Starting from the initial basis for the ideal I is possible to make up
+ * a new basis, still showing off the nice property that each generator is
+ * a moving line that is a linear combination of x, y, 1 where the coefficients
+ * are polynomials in R[t], and moreover each generator is of minimal degree.
+ * Can be proved that given a polynomial parametrization f(t), g(t)
+ * we are able to make up a "micro" basis of generators p(t, x, y), q(t, x, y)
+ * for the ideal I such that the deg(p, t) = m <= n/2 and deg(q, t) = n - m,
+ * where n = max(deg(f(t)), deg(g(t))); this let us halve the order of
+ * the Bezout matrix.
+ * Reference:
+ * Zheng, Sederberg - A Direct Approach to Computing the micro-basis
+ *                    of a Planar Rational Curves
+ * Deng, Chen, Shen - Computing micro-Basis of Rational Curves and Surfaces
+ *                    Using Polynomial Matrix Factorization
+ */
+inline
+void microbasis(basis_type& b, MVPoly1 const& p, MVPoly1 const& q)
+{
+    typedef std::pair<size_t, size_t> degree_pair_t;
+
+    size_t n = std::max(p.get_poly().real_degree(), q.get_poly().real_degree());
+    make_initial_basis(b, p, q);
+    degree_pair_t n0 = detail::deg(b[0]);
+    degree_pair_t n1 = detail::deg(b[1]);
+    size_t d;
+    double r0, r1;
+    //size_t iter = 0;
+    while ((n0.first + n1.first) > n)// && iter < 30)
+    {
+//        ++iter;
+//        std::cout << "iter = " << iter << std::endl;
+//        for (size_t i= 0; i < 2; ++i)
+//            for (size_t j= 0; j < 3; ++j)
+//                std::cout << b[i][j] << std::endl;
+//        std::cout << n0.first << ", " << n0.second << std::endl;
+//        std::cout << n1.first << ", " << n1.second << std::endl;
+//        std::cout << "-----" << std::endl;
+//        if (n0.first < n1.first)
+//        {
+//            d = n1.first - n0.first;
+//            r = b[1][n1.second][n1.first] / b[0][n1.second][n0.first];
+//            for (size_t i = 0; i < b[0].size(); ++i)
+//                b[1][i] -= ((r * b[0][i]).get_poly() << d);
+//            b[1][n1.second][n1.first] = 0;
+//            n1 = detail::deg(b[1]);
+//        }
+//        else
+//        {
+//            d = n0.first - n1.first;
+//            r = b[0][n0.second][n0.first] / b[1][n0.second][n1.first];
+//            for (size_t i = 0; i < b[0].size(); ++i)
+//                b[0][i] -= ((r * b[1][i]).get_poly() << d);
+//            b[0][n0.second][n0.first] = 0;
+//            n0 = detail::deg(b[0]);
+//        }
+
+        // this version shouldn't suffer of ill-conditioning due to
+        // cancellation issue
+        if (n0.first < n1.first)
+        {
+            d = n1.first - n0.first;
+            r0 = b[0][n1.second][n0.first];
+            r1 = b[1][n1.second][n1.first];
+            for (size_t i = 0; i < b[0].size(); ++i)
+            {
+                b[1][i] *= r0;
+                b[1][i] -= ((r1 * b[0][i]).get_poly() << d);
+                // without the following division the modulus grows
+                // beyond the limit of the double type
+                b[1][i] /= r0;
+            }
+            n1 = detail::deg(b[1]);
+        }
+        else
+        {
+            d = n0.first - n1.first;
+            r0 = b[0][n1.second][n0.first];
+            r1 = b[1][n1.second][n1.first];
+
+            for (size_t i = 0; i < b[0].size(); ++i)
+            {
+                b[0][i] *= r1;
+                b[0][i] -= ((r0 * b[1][i]).get_poly() << d);
+                b[0][i] /= r1;
+            }
+            n0 = detail::deg(b[0]);
+        }
+
+    }
+}
+
+/*
+ *  computes the dot product:
+ *  p(t, x, y) = {p0(t), p1(t), p2(t)} . {x, y, 1}
+ */
+inline
+void basis_to_poly(MVPoly3 & p0, poly_vector_type const& v)
+{
+    MVPoly3 p1, p2;
+    detail::poly1_to_poly3(p0, v[0], 1,0);
+    detail::poly1_to_poly3(p1, v[1], 0,1);
+    detail::poly1_to_poly3(p2, v[2], 0,0);
+    p0 += p1;
+    p0 += p2;
+}
+
+
+/*
+ * Make up a Bezout matrix with two basis genarators as input.
+ *
+ * A Bezout matrix is the matrix related to the symmetric bilinear form
+ * (f,g) -> B[f,g] where B[f,g](s,t) = (f(t)*g(s) - f(s)*g(t))/(s-t)
+ * where f, g are polynomials, this function is called a bezoutian.
+ * Given a basis of generators {p(t, x, y), q(t, x, y)} for the ideal I
+ * related to our parametrization x = f(t), y = g(t), we are able to prove
+ * that the implicit equation of such polynomial parametrization can be
+ * evaluated computing the determinant of the Bezout matrix made up using
+ * the polinomial p and q as univariate polynomials in t with coefficients
+ * in R[x,y], so the resulting Bezout matrix will be a matrix with bivariate
+ * polynomials as entries. A Bezout matrix is always symmetric.
+ * Reference:
+ * Sederberg, Zheng - Algebraic Methods for Computer Aided Geometric Design
+ */
+Matrix<MVPoly2>
+make_bezout_matrix (MVPoly3 const& p, MVPoly3 const& q)
+{
+    size_t pdeg = p.get_poly().real_degree();
+    size_t qdeg = q.get_poly().real_degree();
+    size_t n = std::max(pdeg, qdeg);
+
+    Matrix<MVPoly2> BM(n, n);
+    //std::cerr << "rows, columns " << BM.rows() << " , " << BM.columns() << std::endl;
+    for (size_t i = n; i >= 1; --i)
+    {
+        for (size_t j = n; j >= i; --j)
+        {
+            size_t m = std::min(i, n + 1 - j);
+            //std::cerr << "m = " << m << std::endl;
+            for (size_t k = 1; k <= m; ++k)
+            {
+                //BM(i-1,j-1) += (p[j-1+k] * q[i-k] - p[i-k] * q[j-1+k]);
+                BM(n-i,n-j) += (p.coefficient(j-1+k) * q.coefficient(i-k)
+                                - p.coefficient(i-k) * q.coefficient(j-1+k));
+            }
+        }
+    }
+
+    for (size_t i = 0; i < n; ++i)
+    {
+        for (size_t j = 0; j < i; ++j)
+            BM(j,i) = BM(i,j);
+    }
+    return BM;
+}
+
+/*
+ *  Make a matrix that represents a main minor (i.e. with the diagonal
+ *  on the diagonal of the matrix to which it owns) of the Bezout matrix
+ *  with order n-1 where n is the order of the Bezout matrix.
+ *  The minor is obtained by removing the "h"-th row and the "h"-th column,
+ *  and as the Bezout matrix is symmetric.
+ */
+Matrix<MVPoly2>
+make_bezout_main_minor (MVPoly3 const& p, MVPoly3 const& q, size_t h)
+{
+    size_t pdeg = p.get_poly().real_degree();
+    size_t qdeg = q.get_poly().real_degree();
+    size_t n = std::max(pdeg, qdeg);
+
+    Matrix<MVPoly2> BM(n-1, n-1);
+    size_t u = 0, v;
+    for (size_t i = 1; i <= n; ++i)
+    {
+        v = 0;
+        if (i == h)
+        {
+            u = 1;
+            continue;
+        }
+        for (size_t j = 1; j <= i; ++j)
+        {
+            if (j == h)
+            {
+                v = 1;
+                continue;
+            }
+            size_t m = std::min(i, n + 1 - j);
+            for (size_t k = 1; k <= m; ++k)
+            {
+                //BM(i-u-1,j-v-1) += (p[j-1+k] * q[i-k] - p[i-k] * q[j-1+k]);
+                BM(i-u-1,j-v-1) += (p.coefficient(j-1+k) * q.coefficient(i-k)
+                                  - p.coefficient(i-k) * q.coefficient(j-1+k));
+            }
+        }
+    }
+
+    --n;
+    for (size_t i = 0; i < n; ++i)
+    {
+        for (size_t j = 0; j < i; ++j)
+            BM(j,i) = BM(i,j);
+    }
+    return BM;
+}
+
+
+} /*end namespace Geom*/  } /*end namespace SL*/
+
+
+
+
+#endif // _GEOM_SL_IMPLICIT_H_
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/symbolic/matrix.h b/include/2geom/symbolic/matrix.h
new file mode 100644
index 0000000..d9dc690
--- /dev/null
+++ b/include/2geom/symbolic/matrix.h
@@ -0,0 +1,265 @@
+/*
+ * Matrix<CoeffT> class template
+ *
+ * Authors:
+ *      Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 2008  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+
+#ifndef _GEOM_SL_MATRIX_H_
+#define _GEOM_SL_MATRIX_H_
+
+
+#include <array>
+#include <vector>
+#include <map>
+
+#include <2geom/point.h>
+#include <2geom/numeric/matrix.h>
+#include <2geom/symbolic/multipoly.h>
+
+
+
+
+namespace Geom { namespace SL {
+
+/*
+ *  generic Matrix class template
+ *  needed for building up a matrix with polynomial entries
+ */
+template< typename Coeff>
+class Matrix
+{
+  public:
+    typedef Coeff coeff_type;
+    typedef std::vector<coeff_type> container_type;
+
+    Matrix()
+    {}
+
+    Matrix(size_t m, size_t n)
+        : m_data(m*n), m_rows(m), m_columns(n)
+    {
+    }
+
+    void resize(size_t m, size_t n)
+    {
+        m_data.resize(m,n);
+        m_rows = m;
+        m_columns = n;
+    }
+
+    size_t rows() const
+    {
+        return m_rows;
+    }
+
+    size_t columns() const
+    {
+        return m_columns;
+    }
+
+    coeff_type const& operator() (size_t i, size_t j) const
+    {
+        return m_data[i * columns() + j];
+    }
+
+    coeff_type & operator() (size_t i, size_t j)
+    {
+        return m_data[i * columns() + j];
+    }
+
+
+  private:
+    container_type m_data;
+    size_t m_rows;
+    size_t m_columns;
+};
+
+
+template< typename Coeff, typename charT >
+inline
+std::basic_ostream<charT> &
+operator<< ( std::basic_ostream<charT> & os,
+             const Matrix<Coeff> & _matrix )
+{
+    if (_matrix.rows() == 0 || _matrix.columns() == 0) return os;
+
+    os << "{{" << _matrix(0,0);
+    for (size_t j = 1; j < _matrix.columns(); ++j)
+    {
+        os << ", " << _matrix(0,j);
+    }
+    os << "}";
+
+    for (size_t i = 1; i < _matrix.rows(); ++i)
+    {
+        os << ", {" << _matrix(i,0);
+        for (size_t j = 1; j < _matrix.columns(); ++j)
+        {
+            os << ", " << _matrix(i,j);
+        }
+        os << "}";
+    }
+    os << "}";
+    return os;
+}
+
+template <size_t N, typename CoeffT, typename T>
+void polynomial_matrix_evaluate (Matrix<T> & A,
+                                 Matrix< MultiPoly<N, CoeffT> > const& M,
+                                 std::array<T, N> const& X)
+{
+    A.resize(M.rows(), M.columns());
+    for (size_t i = 0; i < M.rows(); ++i)
+    {
+        for (size_t j = 0; j < M.columns(); ++j)
+        {
+            A(i,j) = M(i,j)(X);
+        }
+    }
+}
+
+
+inline
+void polynomial_matrix_evaluate (NL::Matrix & A,
+                                 Matrix< MultiPoly<2, double> > const& M,
+                                 Point const& P)
+{
+    for (size_t i = 0; i < M.rows(); ++i)
+    {
+        for (size_t j = 0; j < M.columns(); ++j)
+        {
+            A(i,j) = M(i,j)(P[X], P[Y]);
+        }
+    }
+}
+
+
+/*
+template< typename Coeff>
+class SymmetricSquareMatrix
+{
+  public:
+    typedef Coeff coeff_type;
+    typedef std::vector<coeff_type> container_type;
+
+    SymmetricSquareMatrix(size_t n)
+        : m_data((n*n)/2 + n), m_size(n)
+    {
+
+    }
+
+    size_t rows() const
+    {
+        return m_size;
+    }
+
+    size_t columns() const
+    {
+        return m_size;
+    }
+
+    coeff_type const& operator() (size_t i, size_t j) const
+    {
+        return m_data[i * columns() + j];
+    }
+
+    coeff_type & operator() (size_t i, size_t j)
+    {
+        return m_data[i * columns() + j];
+    }
+
+    coeff_type det()
+    {
+
+    }
+
+  private:
+    container_type m_data;
+    size_t m_size;
+};
+*/
+
+/*
+ * This is an adaptation of the LU algorithm used in the numerical case.
+ * This algorithm is based on the article due to Bareiss:
+ * "Sylvester's identity and multistep integer-preserving Gaussian elimination"
+ */
+
+/*
+template< typename CoeffT >
+CoeffT det(Matrix<CoeffT> const& M)
+{
+    assert(M.rows() == M.columns());
+
+    Matrix<CoeffT> A(M);
+    CoeffT n;
+    CoeffT d = one<CoeffT>()();
+    for (size_t k = 1; k < A.rows(); ++k)
+    {
+        for (size_t i = k; i < A.rows(); ++i)
+        {
+            for (size_t j = k; j < A.columns(); ++j)
+            {
+                n = A(i,j) * A(k-1,k-1) - A(k-1,j) * A(i,k-1);
+//                std::cout << "k, i, j: "
+//                          << k << ", " << i << ",  " << j << std::endl;
+//                std::cout << "n = " << n << std::endl;
+//                std::cout << "d = " << d << std::endl;
+                A(i,j) = factor(n, d);
+            }
+        }
+        d = A(k-1,k-1);
+    }
+    return A(A.rows()-1, A.columns()-1);
+}
+*/
+
+
+
+} /*end namespace Geom*/  } /*end namespace SL*/
+
+
+#include <2geom/symbolic/determinant-minor.h>
+
+
+#endif // _GEOM_SL_MATRIX_H_
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/symbolic/multi-index.h b/include/2geom/symbolic/multi-index.h
new file mode 100644
index 0000000..311fae8
--- /dev/null
+++ b/include/2geom/symbolic/multi-index.h
@@ -0,0 +1,169 @@
+/*
+ * A multi-index is an ordered sequence of unsigned int
+ *
+ * Authors:
+ *      Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 2008  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef _GEOM_SL_MULTI_INDEX_H_
+#define _GEOM_SL_MULTI_INDEX_H_
+
+
+#include <2geom/exception.h>
+
+#include <valarray>
+
+#include <boost/preprocessor/cat.hpp>
+#include <boost/preprocessor/repetition/enum_params.hpp>
+#include <boost/preprocessor/repetition/repeat.hpp>
+#include <boost/preprocessor/repetition/repeat_from_to.hpp>
+
+
+
+
+/*
+ *  an helper macro for generating function with declaration:
+ *  multi_index_type make_multi_index (size_t i0, ..., size_t iN)
+ *  that is a facility to make up a multi-index from a list of values
+ */
+
+#define GEOM_SL_MAX_RANK 10
+
+#define GEOM_SL_ASSIGN_INDEX(z, k, unused)    I[k] = BOOST_PP_CAT(i, k);
+
+#define GEOM_SL_MAKE_MULTI_INDEX(z, N, unused) \
+inline                                                                         \
+multi_index_type make_multi_index (BOOST_PP_ENUM_PARAMS(N, size_t i))          \
+{                                                                              \
+    multi_index_type I(N);                                                     \
+    BOOST_PP_REPEAT(N, GEOM_SL_ASSIGN_INDEX, unused)                           \
+    return I;                                                                  \
+}
+// end macro GEOM_SL_MAKE_MULTI_INDEX
+
+
+
+
+namespace Geom { namespace SL {
+
+/*
+ *  A multi-index is an ordered sequence of unsigned int;
+ *  it's useful for representing exponent, degree and coefficient index
+ *  of a multi-variate polynomial;
+ *  example: given a monomial x_(0)^i_(0)*x_(1)^i_(1)*...*x_(N-1)^i_(N-1)
+ *  we can write it in the simpler form X^I where X=(x_(0), .., x_(N-1))
+ *  and I=(i_(0), .., i_(N-1)) is a multi-index
+ *  A multi-index is represented as a valarray this let us make simple
+ *  arithmetic operations on a multi-index
+ */
+
+typedef std::valarray<size_t> multi_index_type;
+
+
+// make up a multi-index of size N and fill it with zeroes
+inline
+multi_index_type multi_index_zero(size_t N)
+{
+    return multi_index_type(N);
+}
+
+// helper functions for generating a multi-index from a list of values
+// we create an amount of GEOM_SL_MAX_RANK of suzh functions
+BOOST_PP_REPEAT_FROM_TO(0, GEOM_SL_MAX_RANK, GEOM_SL_MAKE_MULTI_INDEX, unused)
+
+
+// helper function for generating a multi-index of size N
+// from a single index v that is placed at position i with i in [0,N[
+template <size_t N>
+inline
+multi_index_type make_multi_index(size_t i, size_t v)
+{
+    if (!(i < N))
+        THROW_RANGEERROR ("make_multi_index<N> from a single index: "
+                          "out of range position");
+    multi_index_type I(N);
+    I[i] = v;
+    return I;
+}
+
+// transform a N size multi-index in (N-1)-size multi-index
+// by removing the first index: (i1, i2,...,iN) -> (i2,..,iN)
+inline
+multi_index_type shift(multi_index_type const& I, size_t i = 1)
+{
+    size_t N = I.size() - i;
+    multi_index_type J = I[std::slice(i, N, 1)];
+    return J;
+}
+
+// valarray operator== returns a valarray of bool
+inline
+bool is_equal(multi_index_type const& I, multi_index_type const& J)
+{
+    if (I.size() != J.size())  return false;
+    for (size_t i = 0; i < I.size(); ++i)
+        if (I[i] != J[i])  return false;
+    return true;
+}
+
+// extended operator<< for printing a multi-index
+template <typename charT>
+inline
+std::basic_ostream<charT> &
+operator<< (std::basic_ostream<charT> & os,
+            const Geom::SL::multi_index_type & I)
+{
+    if (I.size() == 0 ) return os;
+    os << "[" << I[0];
+    for (unsigned int i = 1; i < I.size(); ++i)
+    {
+        os << ", " << I[i];
+    }
+    os << "]";
+    return os;
+}
+
+} /*end namespace Geom*/  } /*end namespace SL*/
+
+// argument dependent name lookup doesn't work with typedef
+using Geom::SL::operator<<;
+
+
+#endif // _GEOM_SL_MULTI_INDEX_
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/symbolic/multipoly.h b/include/2geom/symbolic/multipoly.h
new file mode 100644
index 0000000..ab3a5f4
--- /dev/null
+++ b/include/2geom/symbolic/multipoly.h
@@ -0,0 +1,684 @@
+/*
+ * MultiPoly<N, CoeffT> class template
+ *
+ * Authors:
+ *      Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 2008  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef _GEOM_SL_MULTIPOLY_H_
+#define _GEOM_SL_MULTIPOLY_H_
+
+#include <utility>
+
+#include <array>
+#include <functional>
+#include <type_traits>
+
+#include <2geom/symbolic/unity-builder.h>
+#include <2geom/symbolic/mvpoly-tools.h>
+
+
+namespace Geom { namespace SL {
+
+/*
+ * MultiPoly<N, CoeffT> class template
+ *
+ * It represents a multi-variate polynomial with N indeterminates
+ * and coefficients of type CoeffT, but it doesn't support explicit
+ * symbol attaching; the indeterminates should be thought as implicitly
+ * defined in an automatic enumerative style: x_(0),...,x_(N-1) .
+ *
+ */
+
+template <size_t N, typename CoeffT>
+class MultiPoly
+{
+public:
+    typedef typename mvpoly<N, CoeffT>::type poly_type;
+    typedef CoeffT coeff_type;
+    static const size_t rank = N;
+
+public:
+    MultiPoly()
+    {
+    }
+
+    MultiPoly(poly_type p)
+        : m_poly(std::move(p))
+    {
+    }
+
+    // create a mv polynomial of type c*X^I
+    MultiPoly(coeff_type c, multi_index_type const& I = multi_index_zero(N))
+        : m_poly(monomial<N, coeff_type>::make(I, c))
+    {
+    }
+
+    // create a mv polynomial p(x_(N-M),...,x_(N-1))*X'^I
+    // where I.size() == N-M and X'=(x_(0),...,x_(N-M-1))
+    template <size_t M>
+    MultiPoly (MultiPoly<M, CoeffT> const& p,
+               multi_index_type const& I = multi_index_zero(N-M),
+               typename std::enable_if_t<(M > 0) && (M < N)>* = 0)
+    {
+        Geom::SL::coefficient<N-M-1, poly_type>::set_safe(m_poly, I, p.m_poly);
+    }
+
+    /*
+     *  assignment operators
+     */
+    MultiPoly& operator=(poly_type const& p)
+    {
+        m_poly = p;
+        return (*this);
+    }
+
+    MultiPoly& operator=(coeff_type const& c)
+    {
+        multi_index_type I = multi_index_zero(N);
+        (*this) = MultiPoly(c);
+        return (*this);
+    }
+
+    // return the degree of the mv polynomial wrt the ordering OrderT
+    template <typename OrderT>
+    multi_index_type degree() const
+    {
+        return  Geom::SL::mvdegree<N, CoeffT, OrderT>::value(m_poly);
+    }
+
+    // return the coefficient of the term with the highest degree
+    // wrt the ordering OrderT
+    template <typename OrderT>
+    coeff_type const& leading_coefficient() const
+    {
+        return  (*this)(degree<OrderT>());
+    }
+
+    template <typename OrderT>
+    coeff_type & leading_coefficient()
+    {
+        return  (*this)(degree<OrderT>());
+    }
+
+    // return the coefficient of the term of degree 0 (wrt all indeterminates)
+    coeff_type const& trailing_coefficient() const
+    {
+        return (*this)(multi_index_zero(N));
+    }
+
+    coeff_type & trailing_coefficient()
+    {
+        return (*this)(multi_index_zero(N));
+    }
+
+    // access coefficient methods with no out-of-range checking
+    coeff_type const& operator() (multi_index_type const& I) const
+    {
+        return Geom::SL::coefficient<N-1, poly_type>::get(m_poly, I);
+    }
+
+    coeff_type & operator() (multi_index_type const& I)
+    {
+        return Geom::SL::coefficient<N-1, poly_type>::get(m_poly, I);
+    }
+
+    // safe coefficient get method
+    coeff_type const& coefficient(multi_index_type const& I) const
+    {
+        return Geom::SL::coefficient<N-1, poly_type>::get_safe(m_poly, I);
+    }
+
+    // safe coefficient set method
+    void coefficient(multi_index_type const& I, coeff_type const& c)
+    {
+        Geom::SL::coefficient<N-1, poly_type>::set_safe(m_poly, I, c);
+    }
+
+    // access the mv poly of rank N-1 with no out-of-range checking
+    typename poly_type::coeff_type const&
+    operator[] (size_t const& i) const
+    {
+        return m_poly[i];
+    }
+
+    typename poly_type::coeff_type &
+    operator[] (size_t const& i)
+    {
+        return m_poly[i];
+    }
+
+    // safe access to the mv poly of rank N-1
+    typename poly_type::coeff_type const&
+    coefficient(size_t const& i) const
+    {
+        return m_poly.coefficient(i);
+    }
+
+    void coefficient (size_t const& i,
+                      typename poly_type::coeff_type const& c)
+    {
+        m_poly.coefficient(i, c);
+    }
+
+    /*
+     * polynomail evaluation:
+     * T can be any type that is able to be + and * with the coefficient type
+     */
+    template <typename T>
+    T operator() (std::array<T, N> const& X) const
+    {
+        return Geom::SL::mvpoly<N, CoeffT>::evaluate(m_poly, X);
+    }
+
+    template <typename T>
+    typename std::enable_if_t<(N == 1), T>
+    operator() (T const& x0) const
+    {
+        std::array<T, N> X = {{x0}};
+        return Geom::SL::mvpoly<N, CoeffT>::evaluate(m_poly, X);
+    }
+
+    template <typename T>
+    typename std::enable_if_t<(N == 2), T>
+    operator() (T const& x0, T const& x1) const
+    {
+        std::array<T, N> X = {{x0, x1}};
+        return Geom::SL::mvpoly<N, CoeffT>::evaluate(m_poly, X);
+    }
+
+    template <typename T>
+    typename std::enable_if_t<(N == 3), T>
+    operator() (T const& x0, T const& x1, T const& x2) const
+    {
+        std::array<T, N> X = {{x0, x1, x2}};
+        return Geom::SL::mvpoly<N, CoeffT>::evaluate(m_poly, X);
+    }
+
+    /*
+     * trim leading zero coefficients
+     */
+    void normalize()
+    {
+        Geom::SL::mvpoly<N, CoeffT>::normalize(m_poly);
+    }
+
+    /*
+     *  select the sub multi-variate polynomial with rank M
+     *  which is unambiguously characterized by the multi-index I
+     *  requirements:
+     *  - M > 0 && M < N;
+     *  - multi-index size == N-M
+     */
+    template <size_t M>
+    typename mvpoly<M, CoeffT>::type const&
+    select (multi_index_type const& I= multi_index_zero(N-M),
+            typename std::enable_if_t<(M > 0) && (M < N)>* = 0) const
+    {
+        return Geom::SL::coefficient<N-M-1, poly_type>::get_safe(m_poly, I);
+    }
+
+    poly_type const& get_poly() const
+    {
+        return m_poly;
+    }
+
+    bool is_zero() const
+    {
+        return ((*this) == zero);
+    }
+
+    // return the opposite mv poly
+    MultiPoly operator-() const
+    {
+        MultiPoly r(-m_poly);
+        return r;
+    }
+
+    /*
+     * multipoly-multipoly mutating operators
+     */
+    MultiPoly& operator+=(MultiPoly const& p)
+    {
+        m_poly += p.m_poly;
+        return (*this);
+    }
+
+    MultiPoly& operator-=(MultiPoly const& p)
+    {
+        m_poly -= p.m_poly;
+        return (*this);
+    }
+
+    MultiPoly& operator*=(MultiPoly const& p)
+    {
+        m_poly *= p.m_poly;
+        return (*this);
+    }
+
+    MultiPoly& operator<<=(multi_index_type const& I)
+    {
+        Geom::SL::mvpoly<N, CoeffT>::shift(m_poly, I);
+        return (*this);
+    }
+
+    bool operator==(MultiPoly const& q) const
+    {
+        return (m_poly == q.m_poly);
+    }
+
+    bool operator!=(MultiPoly const& q) const
+    {
+        return !((*this) == q);
+    }
+
+    /*
+     * multipoly-coefficient mutating operators
+     */
+    MultiPoly& operator+=(CoeffT const& c)
+    {
+        trailing_coefficient() += c;
+        return (*this);
+    }
+
+    MultiPoly& operator-=(CoeffT const& c)
+    {
+        trailing_coefficient() -= c;
+        return (*this);
+    }
+
+    MultiPoly& operator*=(CoeffT const& c)
+    {
+        mvpoly<N, CoeffT>::template
+        for_each<0>(m_poly, std::bind(mvpoly<0, CoeffT>::multiply_to, std::placeholders::_1, c));
+        return (*this);
+    }
+
+    MultiPoly& operator/=(CoeffT const& c)
+    {
+        mvpoly<N, CoeffT>::template
+        for_each<0>(m_poly, std::bind(mvpoly<0, CoeffT>::divide_to, std::placeholders::_1, c));
+        return (*this);
+    }
+
+    /*
+     * multipoly-polynomial mutating operators
+     */
+    MultiPoly& operator+=(poly_type const& p)
+    {
+        m_poly += p;
+        return (*this);
+    }
+
+    MultiPoly& operator-=(poly_type const& p)
+    {
+        m_poly -= p;
+        return (*this);
+    }
+
+    MultiPoly& operator*=(poly_type const& p)
+    {
+        m_poly *= p;
+        return (*this);
+    }
+
+    /*
+     *  multipoly<N>-multipoly<M> mutating operators
+     *  requirements:
+     *  - M > 0 && M < N;
+     *  - they must have the same coefficient type.
+     */
+
+    template <size_t M>
+    typename std::enable_if_t<(M > 0) && (M < N), MultiPoly> &
+    operator+= (MultiPoly<M, CoeffT> const& p)
+    {
+        multi_index_type I = multi_index_zero(N-M);
+        Geom::SL::coefficient<N-M-1, poly_type>::get(m_poly, I) += p.m_poly;
+        return (*this);
+    }
+
+    template <size_t M>
+    typename std::enable_if_t<(M > 0) && (M < N), MultiPoly> &
+    operator-= (MultiPoly<M, CoeffT> const& p)
+    {
+        multi_index_type I = multi_index_zero(N-M);
+        Geom::SL::coefficient<N-M-1, poly_type>::get(m_poly, I) -= p.m_poly;
+        return (*this);
+    }
+
+    template <size_t M>
+    typename std::enable_if_t<(M > 0) && (M < N), MultiPoly> &
+    operator*= (MultiPoly<M, CoeffT> const& p)
+    {
+        mvpoly<N, CoeffT>::template
+        for_each<M>(m_poly, std::bind(mvpoly<M, CoeffT>::multiply_to, std::placeholders::_1, p.m_poly));
+        return (*this);
+    }
+
+    /*
+     *  we need MultiPoly instantiations to be each other friend
+     *  in order to be able of implementing operations between
+     *  MultiPoly instantiations with a different ranks
+     */
+    template<size_t M, typename C>
+    friend class MultiPoly;
+
+    template< typename charT, size_t M, typename C>
+    friend
+    std::basic_ostream<charT> &
+    operator<< (std::basic_ostream<charT> & os, const MultiPoly<M, C> & p);
+
+    static const MultiPoly zero;
+    static const MultiPoly one;
+    static const coeff_type zero_coeff;
+    static const coeff_type one_coeff;
+
+private:
+    poly_type m_poly;
+
+}; //  end class MultiPoly
+
+
+/*
+ *  zero and one element spezcialization for MultiPoly
+ */
+template <size_t N, typename CoeffT>
+struct zero<MultiPoly<N, CoeffT>, false>
+{
+    MultiPoly<N, CoeffT> operator() ()
+    {
+        CoeffT _0c = zero<CoeffT>()();
+        MultiPoly<N, CoeffT> _0(_0c);
+        return _0;
+    }
+};
+
+
+template <size_t N, typename CoeffT>
+struct one<MultiPoly<N, CoeffT>, false>
+{
+    MultiPoly<N, CoeffT> operator() ()
+    {
+        CoeffT _1c = one<CoeffT>()();
+        MultiPoly<N, CoeffT> _1(_1c);
+        return _1;
+    }
+};
+
+
+/*
+ * initialization of MultiPoly static data members
+ */
+template <size_t N, typename CoeffT>
+const MultiPoly<N, CoeffT> MultiPoly<N, CoeffT>::one
+    = Geom::SL::one< MultiPoly<N, CoeffT> >()();
+
+template <size_t N, typename CoeffT>
+const MultiPoly<N, CoeffT> MultiPoly<N, CoeffT>::zero
+    = Geom::SL::zero< MultiPoly<N, CoeffT> >()();
+
+template <size_t N, typename CoeffT>
+const typename MultiPoly<N, CoeffT>::coeff_type MultiPoly<N, CoeffT>::zero_coeff
+    = Geom::SL::zero<typename MultiPoly<N, CoeffT>::coeff_type>()();
+
+template <size_t N, typename CoeffT>
+const typename MultiPoly<N, CoeffT>::coeff_type MultiPoly<N, CoeffT>::one_coeff
+    = Geom::SL::one<typename MultiPoly<N, CoeffT>::coeff_type>()();
+
+
+/*
+ *  operator<< extended to print out a mv poly type
+ */
+template <typename charT, size_t N, typename CoeffT>
+inline
+std::basic_ostream<charT> &
+operator<< (std::basic_ostream<charT> & os, const MultiPoly<N, CoeffT> & p)
+{
+    return operator<<(os, p.m_poly);
+}
+
+/*
+ *  equivalent to multiply by X^I
+ */
+template <size_t N, typename CoeffT>
+inline
+MultiPoly<N, CoeffT>
+operator<< (MultiPoly<N, CoeffT> const& p, multi_index_type const& I)
+{
+    MultiPoly<N, CoeffT> r(p);
+    r <<= I;
+    return r;
+}
+
+/*
+ * MultiPoly<M, CoeffT> - MultiPoly<N, CoeffT> binary mathematical operators
+ */
+
+template <size_t M, size_t N, typename CoeffT>
+inline
+typename std::enable_if_t<(M > 0) && (M <= N), MultiPoly<N, CoeffT> >
+operator+ (MultiPoly<N, CoeffT> const& p,
+           MultiPoly<M, CoeffT> const& q )
+{
+    MultiPoly<N, CoeffT> r(p);
+    r += q;
+    return r;
+}
+
+template <size_t M, size_t N, typename CoeffT>
+inline
+typename std::enable_if_t<(N > 0) && (M > N), MultiPoly<M, CoeffT> >
+operator+ (MultiPoly<N, CoeffT> const& p,
+           MultiPoly<M, CoeffT> const& q )
+{
+    MultiPoly<M, CoeffT> r(q);
+    r += p;
+    return r;
+}
+
+template <size_t M, size_t N, typename CoeffT>
+inline
+typename std::enable_if_t<(M > 0) && (M <= N), MultiPoly<N, CoeffT> >
+operator- (MultiPoly<N, CoeffT> const& p,
+           MultiPoly<M, CoeffT> const& q )
+{
+    MultiPoly<N, CoeffT> r(p);
+    r -= q;
+    return r;
+}
+
+template <size_t M, size_t N, typename CoeffT>
+inline
+typename std::enable_if_t<(N > 0) && (M > N), MultiPoly<M, CoeffT> >
+operator- (MultiPoly<N, CoeffT> const& p,
+           MultiPoly<M, CoeffT> const& q )
+{
+    MultiPoly<M, CoeffT> r(-q);
+    r += p;
+    return r;
+}
+
+
+template <size_t M, size_t N, typename CoeffT>
+inline
+typename std::enable_if_t<(M > 0) && (M <= N), MultiPoly<N, CoeffT> >
+operator* (MultiPoly<N, CoeffT> const& p,
+           MultiPoly<M, CoeffT> const& q )
+{
+    MultiPoly<N, CoeffT> r(p);
+    r *= q;
+    return r;
+}
+
+template <size_t M, size_t N, typename CoeffT>
+inline
+typename std::enable_if_t<(N > 0) && (M > N), MultiPoly<M, CoeffT> >
+operator* (MultiPoly<N, CoeffT> const& p,
+           MultiPoly<M, CoeffT> const& q )
+{
+    MultiPoly<M, CoeffT> r(q);
+    r *= p;
+    return r;
+}
+
+/*
+ * MultiPoly-coefficient and coefficient-MultiPoly binary mathematical operators
+ */
+
+template <size_t N, typename CoeffT>
+inline
+MultiPoly<N, CoeffT> operator+(MultiPoly<N, CoeffT> const& p, CoeffT const& c)
+{
+    MultiPoly<N, CoeffT> r(p);
+    r += c;
+    return r;
+}
+
+template <size_t N, typename CoeffT>
+inline
+MultiPoly<N, CoeffT> operator+(CoeffT const& c, MultiPoly<N, CoeffT> const& p)
+{
+    MultiPoly<N, CoeffT> r(p);
+    r += c;
+    return r;
+}
+
+template <size_t N, typename CoeffT>
+inline
+MultiPoly<N, CoeffT> operator-(MultiPoly<N, CoeffT> const& p, CoeffT const& c)
+{
+    MultiPoly<N, CoeffT> r(p);
+    r -= c;
+    return r;
+}
+
+template <size_t N, typename CoeffT>
+inline
+MultiPoly<N, CoeffT> operator-(CoeffT const& c, MultiPoly<N, CoeffT> const& p)
+{
+    MultiPoly<N, CoeffT> r(-p);
+    r += c;
+    return r;
+}
+
+template <size_t N, typename CoeffT>
+inline
+MultiPoly<N, CoeffT> operator*(MultiPoly<N, CoeffT> const& p, CoeffT const& c)
+{
+    MultiPoly<N, CoeffT> r(p);
+    r *= c;
+    return r;
+}
+
+template <size_t N, typename CoeffT>
+inline
+MultiPoly<N, CoeffT> operator*(CoeffT const& c, MultiPoly<N, CoeffT> const& p)
+{
+    MultiPoly<N, CoeffT> r(p);
+    r *= c;
+    return r;
+}
+
+
+template <size_t N, typename CoeffT>
+inline
+MultiPoly<N, CoeffT> operator/(MultiPoly<N, CoeffT> const& p, CoeffT const& c)
+{
+    MultiPoly<N, CoeffT> r(p);
+    r /= c;
+    return r;
+}
+
+
+
+
+/*
+template< size_t N, typename CoeffT >
+MultiPoly<N, CoeffT>
+factor( MultiPoly<N, CoeffT> const& f,
+               MultiPoly<N, CoeffT> const& g )
+{
+    typedef MultiPoly<N, CoeffT> poly_type;
+
+    if (g == poly_type::one) return f;
+    poly_type h(g), q, r(f);
+    multi_index_type deg_r = r.template degree<ordering::lex>();
+    multi_index_type deg_g = g.template degree<ordering::lex>();
+    multi_index_type deg0 = multi_index_zero(deg_g.size());
+    CoeffT ltg = g(deg_g);
+    if (is_equal(deg_g, deg0)) return (f / ltg);
+    //h(deg_g) = 0;
+//    std::cout << "deg_g = " << deg_g << std::endl;
+//    std::cout << "ltg = " << ltg << std::endl;
+    CoeffT lt, ltr;
+    multi_index_type deg(1, deg_g.size());
+    size_t iter = 0;
+    while (!is_equal(deg, deg0) && iter < 10000)
+    {
+        ++iter;
+        deg = deg_r - deg_g;
+        ltr = r(deg_r);
+        lt = ltr / ltg;
+        q.coefficient(deg, lt);
+        //r(deg_r) = 0;
+        r -= ((lt * g) << deg);
+        deg_r = r.template degree<ordering::lex>();
+//        std::cout << "deg_r = " << deg_r << std::endl;
+//        std::cout << "ltr = " << ltr << std::endl;
+//        std::cout << "deg = " << deg << std::endl;
+//        std::cout << "lt = " << lt << std::endl;
+//        std::cout << "q = " << q << std::endl;
+//        std::cout << "r = " << r << std::endl;
+
+//        break;
+    }
+    //std::cout << "iter = " << iter << std::endl;
+    return q;
+}
+*/
+
+
+} /*end namespace Geom*/  } /*end namespace SL*/
+
+
+
+
+#endif /* _MULTIPOLY_H_ */
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/symbolic/mvpoly-tools.h b/include/2geom/symbolic/mvpoly-tools.h
new file mode 100644
index 0000000..34dece7
--- /dev/null
+++ b/include/2geom/symbolic/mvpoly-tools.h
@@ -0,0 +1,690 @@
+/*
+ * Routines that extend univariate polynomial functions
+ * to multi-variate polynomial exploiting recursion at compile time
+ *
+ * Authors:
+ *      Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 2008  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef _GEOM_SL_MVPOLY_TOOLS_H_
+#define _GEOM_SL_MVPOLY_TOOLS_H_
+
+
+#include <2geom/exception.h>
+
+#include <2geom/symbolic/multi-index.h>
+#include <2geom/symbolic/unity-builder.h>
+#include <2geom/symbolic/polynomial.h>
+
+#include <array>
+#include <functional>
+#include <iostream>
+#include <type_traits>
+
+
+namespace Geom { namespace SL {
+
+/*
+ *  rank<PolyT>::value == total amount of indeterminates
+ *  x_(0),x_(1),...,x_(rank-1) that belong to type PolyT
+ */
+
+template <typename T>
+struct rank
+{
+    static const size_t value = 0;
+};
+
+template <typename CoeffT>
+struct rank< Polynomial<CoeffT> >
+{
+    static const size_t value = rank<CoeffT>::value + 1;
+};
+
+
+/*
+ *  mvpoly<N, CoeffT> creates a multi-variate polynomial type
+ *  by nesting N-1 Polynomial class template and setting
+ *  the coefficient type of the most nested Polynomial to CoeffT
+ *  example: mvpoly<3, double>::type is the same than
+ *  Polynomial< Polynomial< Polynomial<double> > >
+ */
+
+template <size_t N, typename CoeffT>
+struct mvpoly
+{
+    typedef Polynomial<typename mvpoly<N-1, CoeffT>::type> type;
+    typedef CoeffT coeff_type;
+    static const size_t rank = N;
+
+    /*
+     * computes the lexicographic degree of the mv polynomial p
+     */
+    static
+    multi_index_type lex_degree (type const& p)
+    {
+        multi_index_type D(N);
+        lex_degree_impl<0>(p, D);
+        return D;
+    }
+
+    /*
+     *  Returns in the out-parameter D an N-sequence where each entry value
+     *  represents the max degree of the polynomial related to the passed
+     *  index I, if one index value in I is greater than the related max degree
+     *  the routine returns false otherwise it returns true.
+     *  This routine can be used to test if a given multi-index I is related
+     *  to an actual initialized coefficient.
+     */
+    static
+    bool max_degree (type const& p,
+                     multi_index_type& D,
+                     multi_index_type const& I)
+    {
+        if (I.size() != N)
+            THROW_RANGEERROR ("multi-index with wrong length");
+        D.resize(N);
+        return max_degree_impl<0>(p, D, I);
+    }
+
+    /*
+     *  Returns in the out-parameter D an N-sequence where each entry value
+     *  represents the real degree of the polynomial related to the passed
+     *  index I, if one index value in I is greater than the related real degree
+     *  the routine returns false otherwise it returns true.
+     *  This routine can be used to test if a given multi-index I is related
+     *  to an actual initialized and non-zero coefficient.
+     */
+
+    static
+    bool real_degree (type const& p,
+                      multi_index_type& D,
+                      multi_index_type const& I)
+    {
+        if (I.size() != N)
+            THROW_RANGEERROR ("multi-index with wrong length");
+        D.resize(N);
+        return real_degree_impl<0>(p, D, I);
+    }
+
+    /*
+     *  Multiplies p by X^I
+     */
+    static
+    void shift(type & p, multi_index_type const& I)
+    {
+        if (I.size() != N)
+            THROW_RANGEERROR ("multi-index with wrong length");
+        shift_impl<0>(p, I);
+    }
+
+    /*
+     * mv poly evaluation:
+     * T can be any type that is able to be += with the coefficient type
+     * and that can be *= with the same type T moreover a specialization
+     * of zero struct for the type T is needed
+     */
+    template <typename T>
+    static
+    T evaluate(type const& p, std::array<T, N> const& X)
+    {
+        return evaluate_impl<T, 0>(p, X);
+    }
+
+    /*
+     * trim leading zero coefficients
+     */
+    static
+    void normalize(type & p)
+    {
+        p.normalize();
+        for (size_t k = 0; k < p.size(); ++k)
+            mvpoly<N-1, CoeffT>::normalize(p[k]);
+    }
+
+    /*
+     * Applies the unary operator "op" to each coefficient of p with rank M.
+     * For instance when M = 0 op is applied to each coefficient
+     * of the multi-variate polynomial p.
+     * When M < N the function call recursively the for_each routine
+     * for p.real_degree() times, when M == N the operator "op" is invoked on p;
+     */
+    template <size_t M>
+    static
+    void for_each
+        (type & p,
+         std::function<void (typename mvpoly<M, CoeffT>::type &)> const& op,
+         typename std::enable_if_t<(M < N)>* = 0)
+    {
+        for (size_t k = 0; k <= p.real_degree(); ++k)
+        {
+            mvpoly<N-1, CoeffT>::template for_each<M>(p[k], op);
+        }
+    }
+
+    template <size_t M>
+    static
+    void for_each
+        (type & p,
+         std::function<void (typename mvpoly<M, CoeffT>::type &)> const& op,
+         typename std::enable_if_t<(M == N)>* = 0)
+    {
+        op(p);
+    }
+
+    // this is only an helper function to be passed to the for_each routine
+    static
+    void multiply_to (type& p, type const& q)
+    {
+        p *= q;
+    }
+
+  private:
+    template <size_t i>
+    static
+    void lex_degree_impl (type const& p, multi_index_type& D)
+    {
+        D[i] = p.real_degree();
+        mvpoly<N-1, CoeffT>::template lex_degree_impl<i+1>(p[D[i]], D);
+    }
+
+    template <size_t i>
+    static
+    bool max_degree_impl (type const& p,
+                          multi_index_type& D,
+                          multi_index_type const& I)
+    {
+        D[i] = p.max_degree();
+        if (I[i] > D[i]) return false;
+        return
+          mvpoly<N-1, CoeffT>::template max_degree_impl<i+1>(p[I[i]], D, I);
+    }
+
+    template <size_t i>
+    static
+    bool real_degree_impl (type const& p,
+                           multi_index_type& D,
+                           multi_index_type const& I)
+    {
+        D[i] = p.real_degree();
+        if (I[i] > D[i]) return false;
+        return
+          mvpoly<N-1, CoeffT>::template real_degree_impl<i+1>(p[I[i]], D, I);
+    }
+
+    template <size_t i>
+    static
+    void shift_impl(type & p, multi_index_type const& I)
+    {
+        p <<= I[i];
+        for (size_t k = 0; k < p.size(); ++k)
+        {
+            mvpoly<N-1, CoeffT>::template shift_impl<i+1>(p[k], I);
+        }
+    }
+
+    template <typename T, size_t i>
+    static
+    T evaluate_impl(type const& p, std::array<T, N+i> const& X)
+    {
+//        T r = zero<T>()();
+//        for (size_t k = p.max_degree(); k > 0; --k)
+//        {
+//            r += mvpoly<N-1, CoeffT>::template evaluate_impl<T, i+1>(p[k], X);
+//            r *= X[i];
+//        }
+//        r += mvpoly<N-1, CoeffT>::template evaluate_impl<T, i+1>(p[0], X);
+
+        int n = p.max_degree();
+        T r = mvpoly<N-1, CoeffT>::template evaluate_impl<T, i+1>(p[n], X);
+        for (int k = n - 1; k >= 0; --k)
+        {
+            r *= X[i];
+            r += mvpoly<N-1, CoeffT>::template evaluate_impl<T, i+1>(p[k], X);
+        }
+        return r;
+    }
+
+    template <size_t M, typename C>
+    friend struct mvpoly;
+};
+
+/*
+ * rank 0 mv poly, that is a scalar value (usually a numeric value),
+ * the routines implemented here are used only to stop recursion
+ * (but for_each)
+ */
+template< typename CoeffT >
+struct mvpoly<0, CoeffT>
+{
+    typedef CoeffT type;
+    typedef CoeffT coeff_type;
+    static const size_t rank = 0;
+
+    template <size_t M>
+    static
+    void for_each
+        (type & p,
+         std::function<void (typename mvpoly<M, CoeffT>::type &)> const& op,
+         typename std::enable_if_t<(M == 0)>* = 0)
+    {
+        op(p);
+    }
+
+    // multiply_to and divide_to are only helper functions
+    // to be passed to the for_each routine
+    static
+    void multiply_to (type& p, type const& q)
+    {
+        p *= q;
+    }
+
+    static
+    void divide_to (type& p, type const& c)
+    {
+        p /= c;
+    }
+
+  private:
+    template <size_t i>
+    static
+    void lex_degree_impl (type const &/*p*/, multi_index_type&/*D*/)
+    {
+        return;
+    }
+
+    template <size_t i>
+    static
+    bool max_degree_impl (type const &/*p*/,
+                          multi_index_type &/*D*/,
+                          multi_index_type const &/*I*/)
+    {
+        return true;
+    }
+
+    template <size_t i>
+    static
+    bool real_degree_impl (type const &/*p*/,
+                           multi_index_type &/*D*/,
+                           multi_index_type const &/*I*/)
+    {
+        return true;
+    }
+
+    template <size_t i>
+    static
+    void shift_impl(type &/*p*/, multi_index_type const &/*I*/)
+    {}
+
+    template <typename T, size_t i>
+    static
+    T evaluate_impl(type const &p, std::array<T, i> const &/*X*/)
+    {
+        return p;
+    }
+
+    static
+    void normalize(type &/*p*/)
+    {}
+
+
+    template <size_t M, typename C>
+    friend struct mvpoly;
+};
+
+
+/*
+ *  monomial::make generate a mv-poly made up by a single term:
+ *  monomial::make<N>(I,c) == c*X^I, where X=(x_(0), .., x_(N-1))
+ */
+
+template <size_t N, typename CoeffT>
+struct monomial
+{
+    typedef typename mvpoly<N, CoeffT>::type poly_type;
+
+    static inline
+    poly_type make(multi_index_type const& I, CoeffT c)
+    {
+        if (I.size() != N)      // an exponent for each indeterminate
+            THROW_RANGEERROR ("multi-index with wrong length");
+
+        return make_impl<0>(I, c);
+    }
+
+  private:
+    // at i-th level of recursion I need to pick up the i-th exponent in "I"
+    // so I pass i as a template parameter, this trick is needed to avoid
+    // to create a new multi-index at each recursion level:
+    // (J = I[std::slice[1, I.size()-1, 1)]) that will be more expensive
+    template <size_t i>
+    static
+    poly_type make_impl(multi_index_type const& I, CoeffT c)
+    {
+        poly_type p(monomial<N-1,CoeffT>::template make_impl<i+1>(I, c), I[i]);
+        return p;
+    }
+
+    // make_impl private require that monomial classes to be each other friend
+    template <size_t M, typename C>
+    friend struct monomial;
+};
+
+
+// case N = 0 for stopping recursion
+template <typename CoeffT>
+struct monomial<0, CoeffT>
+{
+  private:
+    template <size_t i>
+    static
+    CoeffT make_impl(multi_index_type const &/*I*/, CoeffT c)
+    {
+        return c;
+    }
+
+    template<size_t N, typename C>
+    friend struct monomial;
+};
+
+
+/*
+ *  coefficient<N, PolyT>
+ *
+ *  N should be in the range [0, rank<PolyT>-1]
+ *
+ *  "type" == the type of the coefficient of the polynomial with
+ *  rank = rank<PolyT> - N - 1, that is it is the type of the object returned
+ *  by applying the operator[] of a Polynomial object N+1 times;
+ *
+ *  "zero" represents the zero element (in the group theory meaning)
+ *  for the coefficient type "type"; having it as a static class member
+ *  allows to return always a (const) reference by the "get_safe" method
+ *
+ *  get(p, I) returns the coefficient of the monomial X^I
+ *  this method doesn't check if such a coefficient really exists,
+ *  so it's up to the user checking that the passed multi-index I is
+ *  not out of range
+ *
+ *  get_safe(p, I) returns the coefficient of the monomial X^I
+ *  in case such a coefficient doesn't really exist  "zero" is returned
+ *
+ *  set_safe(p, I, c) set the coefficient of the monomial X^I to "c"
+ *  in case such a coefficient doesn't really exist this method creates it
+ *  and creates all monomials X^J with J < I that don't exist yet, setting
+ *  their coefficients to "zero";
+ *  (with J < I we mean "<" wrt the lexicographic order)
+ *
+ */
+
+template <size_t N, typename T>
+struct coefficient
+{
+};
+
+
+template <size_t N, typename CoeffT>
+struct coefficient< N, Polynomial<CoeffT> >
+{
+    typedef typename coefficient<N-1, CoeffT>::type type;
+    typedef Polynomial<CoeffT> poly_type;
+
+    static const type zero;
+
+    static
+    type const& get(poly_type const& p, multi_index_type const& I)
+    {
+        if (I.size() != N+1)
+            THROW_RANGEERROR ("multi-index with wrong length");
+
+        return get_impl<0>(p, I);
+    }
+
+    static
+    type & get(poly_type & p, multi_index_type const& I)
+    {
+        if (I.size() != N+1)
+            THROW_RANGEERROR ("multi-index with wrong length");
+
+        return get_impl<0>(p, I);
+    }
+
+    static
+    type const& get_safe(poly_type const& p, multi_index_type const& I)
+    {
+        if (I.size() != N+1)
+            THROW_RANGEERROR ("multi-index with wrong length");
+
+        return get_safe_impl<0>(p, I);
+    }
+
+    static
+    void set_safe(poly_type & p, multi_index_type const& I, type const& c)
+    {
+        if (I.size() != N+1)
+            THROW_RANGEERROR ("multi-index with wrong length");
+
+        return set_safe_impl<0>(p, I, c);
+    }
+
+  private:
+    template <size_t i>
+    static
+    type const& get_impl(poly_type const& p, multi_index_type const& I)
+    {
+        return coefficient<N-1, CoeffT>::template get_impl<i+1>(p[I[i]], I);
+    }
+
+    template <size_t i>
+    static
+    type & get_impl(poly_type & p, multi_index_type const& I)
+    {
+        return coefficient<N-1, CoeffT>::template get_impl<i+1>(p[I[i]], I);
+    }
+
+    template <size_t i>
+    static
+    type const& get_safe_impl(poly_type const& p, multi_index_type const& I)
+    {
+        if (I[i] > p.max_degree())
+        {
+            return zero;
+        }
+        else
+        {
+            return
+            coefficient<N-1, CoeffT>::template get_safe_impl<i+1>(p[I[i]], I);
+        }
+    }
+
+    template <size_t i>
+    static
+    void set_safe_impl(poly_type & p, multi_index_type const& I, type const& c)
+    {
+        if (I[i] > p.max_degree())
+        {
+            multi_index_type J = shift(I, i+1);
+            CoeffT m = monomial<N, type>::make(J, c);
+            p.coefficient(I[i], m);
+        }
+        else
+        {
+            coefficient<N-1, CoeffT>::template set_safe_impl<i+1>(p[I[i]], I, c);
+        }
+    }
+
+    template<size_t M, typename T>
+    friend struct coefficient;
+
+};
+
+// initialization of static member zero
+template <size_t N, typename CoeffT>
+const typename coefficient< N, Polynomial<CoeffT> >::type
+coefficient< N, Polynomial<CoeffT> >::zero
+    = Geom::SL::zero<typename coefficient< N, Polynomial<CoeffT> >::type >()();
+
+
+// case N = 0 for stopping recursion
+template <typename CoeffT>
+struct coefficient< 0, Polynomial<CoeffT> >
+{
+    typedef CoeffT type;
+    typedef Polynomial<CoeffT> poly_type;
+
+    static const type zero;
+
+    static
+    type const& get(poly_type const& p, multi_index_type const& I)
+    {
+        if (I.size() != 1)
+            THROW_RANGEERROR ("multi-index with wrong length");
+
+        return p[I[0]];
+    }
+
+    static
+    type & get(poly_type & p, multi_index_type const& I)
+    {
+        if (I.size() != 1)
+            THROW_RANGEERROR ("multi-index with wrong length");
+
+        return p[I[0]];
+    }
+
+    static
+    type const& get_safe(poly_type const& p, multi_index_type const& I)
+    {
+        if (I.size() != 1)
+            THROW_RANGEERROR ("multi-index with wrong length");
+
+        return p.coefficient(I[0]);
+    }
+
+    static
+    void set_safe(poly_type & p, multi_index_type const& I, type const& c)
+    {
+        if (I.size() != 1)
+            THROW_RANGEERROR ("multi-index with wrong length");
+
+         p.coefficient(I[0], c);
+    }
+
+  private:
+    template <size_t i>
+    static
+    type const& get_impl(poly_type const& p, multi_index_type const& I)
+    {
+        return p[I[i]];
+    }
+
+    template <size_t i>
+    static
+    type & get_impl(poly_type & p, multi_index_type const& I)
+    {
+        return p[I[i]];
+    }
+
+    template <size_t i>
+    static
+    type const& get_safe_impl(poly_type const& p, multi_index_type const& I)
+    {
+        return p.coefficient(I[i]);
+    }
+
+    template <size_t i>
+    static
+    void set_safe_impl(poly_type & p, multi_index_type const& I, type const& c)
+    {
+        p.coefficient(I[i], c);
+    }
+
+    template<size_t M, typename T>
+    friend struct coefficient;
+};
+
+// initialization of static member zero
+template <typename CoeffT>
+const typename coefficient< 0, Polynomial<CoeffT> >::type
+coefficient< 0, Polynomial<CoeffT> >::zero
+    = Geom::SL::zero<typename coefficient< 0, Polynomial<CoeffT> >::type >()();
+
+
+/*
+ * ordering types:
+ * lex  : lexicographic ordering
+ * ilex : inverse lexicographic ordering
+ * max_lex : max degree + lexicographic ordering for disambiguation
+ *
+ */
+
+namespace ordering
+{
+    struct lex;         // WARNING: at present only lex ordering is supported
+    struct ilex;
+    struct max_lex;
+}
+
+
+/*
+ *  degree of a mv poly wrt a given ordering
+ */
+
+template <size_t N, typename CoeffT, typename OrderT = ordering::lex>
+struct mvdegree
+{};
+
+template <size_t N, typename CoeffT>
+struct mvdegree<N, CoeffT, ordering::lex>
+{
+    typedef typename mvpoly<N, CoeffT>::type poly_type;
+    typedef ordering::lex ordering;
+
+    static
+    multi_index_type value(poly_type const& p)
+    {
+        return Geom::SL::mvpoly<N, CoeffT>::lex_degree(p);
+    }
+};
+
+} /*end namespace Geom*/  } /*end namespace SL*/
+
+
+#endif // _GEOM_SL_MVPOLY_TOOLS_H_
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/symbolic/polynomial.h b/include/2geom/symbolic/polynomial.h
new file mode 100644
index 0000000..fea7e6c
--- /dev/null
+++ b/include/2geom/symbolic/polynomial.h
@@ -0,0 +1,569 @@
+/*
+ * Polynomial<CoeffT> class template
+ *
+ * Authors:
+ *      Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 2008  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef _GEOM_SL_POLYNOMIAL_H_
+#define _GEOM_SL_POLYNOMIAL_H_
+
+
+#include <2geom/symbolic/unity-builder.h>
+
+#include <vector>
+#include <string>
+
+#include <2geom/exception.h>
+
+
+
+
+namespace Geom { namespace SL {
+
+/*
+ * Polynomial<CoeffT> class template
+ *
+ * It represents a generic univariate polynomial with coefficients
+ * of type CoeffT. One way to get a multi-variate polynomial is
+ * to utilize a Polynomial instantiation as coefficient type
+ * in a recursive style.
+ *
+ */
+
+template< typename CoeffT >
+class Polynomial
+{
+  public:
+    typedef CoeffT coeff_type;
+    typedef std::vector<coeff_type> coeff_container_t;
+    typedef typename coeff_container_t::iterator iterator;
+    typedef typename coeff_container_t::const_iterator const_iterator;
+
+    /*
+     * a Polynomial should be never empty
+     */
+    Polynomial()
+    {
+        m_coeff.push_back(zero_coeff);
+    }
+
+    explicit
+    Polynomial(CoeffT const& c, size_t i = 0)
+    {
+        m_coeff.resize(i, zero_coeff);
+        m_coeff.push_back(c);
+    }
+
+    /*
+     *  forwarding of some std::vector methods
+     */
+
+    size_t size() const
+    {
+        return m_coeff.size();
+    }
+
+    const_iterator begin() const
+    {
+        return m_coeff.begin();
+    }
+
+    const_iterator end() const
+    {
+        return m_coeff.end();
+    }
+
+    iterator begin()
+    {
+        return m_coeff.begin();
+    }
+
+    iterator end()
+    {
+        return m_coeff.end();
+    }
+
+    void reserve(size_t n)
+    {
+        m_coeff.reserve(n);
+    }
+
+    size_t capacity() const
+    {
+        return m_coeff.capacity();
+    }
+
+    /*
+     *  degree of the term with the highest degree
+     *  and an initialized coefficient (even if zero)
+     */
+    size_t max_degree() const
+    {
+        if (size() == 0)
+            THROW_INVARIANTSVIOLATION (0);
+
+        return (size() - 1);
+    }
+
+    void max_degree(size_t n)
+    {
+        m_coeff.resize(n+1, zero_coeff);
+    }
+
+    /*
+     *  degree of the term with the highest degree
+     *  and an initialized coefficient that is not null
+     */
+    size_t real_degree() const
+    {
+        if (size() == 0)
+            THROW_INVARIANTSVIOLATION (0);
+
+        const_iterator it = end() - 1;
+        for (; it != begin(); --it)
+        {
+            if (*it != zero_coeff) break;
+        }
+        size_t i = static_cast<size_t>(it - begin());
+        return i;
+    }
+
+    bool is_zero() const
+    {
+        if (size() == 0)
+            THROW_INVARIANTSVIOLATION (0);
+
+        if (real_degree() != 0) return false;
+        if (m_coeff[0] != zero_coeff) return false;
+        return true;
+    }
+
+    /*
+     * trim leading zero coefficients
+     * after calling normalize max_degree == real_degree
+     */
+    void normalize()
+    {
+        size_t rd = real_degree();
+        if (rd != max_degree())
+        {
+            m_coeff.erase(begin() + rd + 1, end());
+        }
+    }
+
+    coeff_type const& operator[] (size_t i) const
+    {
+        return m_coeff[i];
+    }
+
+    coeff_type & operator[] (size_t i)
+    {
+        return m_coeff[i];
+    }
+
+    // safe coefficient getter routine
+    coeff_type const& coefficient(size_t i) const
+    {
+        if (i > max_degree())
+        {
+            return zero_coeff;
+        }
+        else
+        {
+            return m_coeff[i];
+        }
+    }
+
+    // safe coefficient setter routine
+    void coefficient(size_t i, coeff_type const& c)
+    {
+        //std::cerr << "i: " << i << " c: " << c << std::endl;
+        if (i > max_degree())
+        {
+            if (c == zero_coeff) return;
+            reserve(i+1);
+            m_coeff.resize(i, zero_coeff);
+            m_coeff.push_back(c);
+        }
+        else
+        {
+            m_coeff[i] = c;
+        }
+    }
+
+    coeff_type const& leading_coefficient() const
+    {
+        return m_coeff[real_degree()];
+    }
+
+    coeff_type & leading_coefficient()
+    {
+        return m_coeff[real_degree()];
+    }
+
+    /*
+     * polynomail evaluation:
+     * T can be any type that is able to be + and * with the coefficient type
+     */
+    template <typename T>
+    T operator() (T const& x) const
+    {
+        T r = zero<T>()();
+        for(size_t i = max_degree(); i > 0; --i)
+        {
+            r += (*this)[i];
+            r *= x;
+        }
+        r += (*this)[0];
+        return r;
+    }
+
+    // opposite polynomial
+    Polynomial operator-() const
+    {
+        Polynomial r;
+        // we need r.m_coeff to be empty so we can utilize push_back
+        r.m_coeff.pop_back();
+        r.reserve(size());
+        for(size_t i = 0; i < size(); ++i)
+        {
+            r.m_coeff.push_back( -(*this)[i] );
+        }
+        return r;
+    }
+
+    /*
+     *  polynomial-polynomial mutating operators
+     */
+
+    Polynomial& operator+=(Polynomial const& p)
+    {
+        size_t sz = std::min(size(), p.size());
+        for (size_t i = 0; i < sz; ++i)
+        {
+            (*this)[i] += p[i];
+        }
+        if (size() < p.size())
+        {
+            m_coeff.insert(end(), p.begin() + size(), p.end());
+        }
+        return (*this);
+    }
+
+    Polynomial& operator-=(Polynomial const& p)
+    {
+        size_t sz = std::min(size(), p.size());
+        for (size_t i = 0; i < sz; ++i)
+        {
+            (*this)[i] -= p[i];
+        }
+        reserve(p.size());
+        for(size_t i = sz; i < p.size(); ++i)
+        {
+            m_coeff.push_back( -p[i] );
+        }
+        return (*this);
+    }
+
+    Polynomial& operator*=(Polynomial const& p)
+    {
+        Polynomial r;
+        r.m_coeff.resize(size() + p.size() - 1, zero_coeff);
+
+        for (size_t i = 0; i < size(); ++i)
+        {
+            for (size_t j = 0; j < p.size(); ++j)
+            {
+                r[i+j] += (*this)[i] * p[j];
+            }
+        }
+        (*this) = r;
+        return (*this);
+    }
+
+    /*
+     *  equivalent to multiply by x^n
+     */
+    Polynomial& operator<<=(size_t n)
+    {
+        m_coeff.insert(begin(), n, zero_coeff);
+        return (*this);
+    }
+
+    /*
+     *  polynomial-coefficient mutating operators
+     */
+
+    Polynomial& operator=(coeff_type const& c)
+    {
+        m_coeff[0] = c;
+        return (*this);
+    }
+
+    Polynomial& operator+=(coeff_type const& c)
+    {
+        (*this)[0] += c;
+        return (*this);
+    }
+
+    Polynomial& operator-=(coeff_type const& c)
+    {
+        (*this)[0] -= c;
+        return (*this);
+    }
+
+    Polynomial& operator*=(coeff_type const& c)
+    {
+        for (size_t i = 0; i < size(); ++i)
+        {
+            (*this)[i] *= c;
+        }
+        return (*this);
+    }
+
+    // return the poly in a string form
+    std::string str() const;
+
+  private:
+    // with zero_coeff defined as a static data member
+    // coefficient(size_t i) safe get method can always
+    // return a (const) reference
+    static const coeff_type zero_coeff;
+    coeff_container_t m_coeff;
+
+}; // end class Polynomial
+
+
+/*
+ *  zero and one element spezcialization for Polynomial
+ */
+
+template< typename CoeffT >
+struct zero<Polynomial<CoeffT>, false>
+{
+    Polynomial<CoeffT> operator() () const
+    {
+        CoeffT zc = zero<CoeffT>()();
+        Polynomial<CoeffT> z(zc);
+        return z;
+    }
+};
+
+template< typename CoeffT >
+struct one<Polynomial<CoeffT>, false>
+{
+    Polynomial<CoeffT> operator() ()
+    {
+        CoeffT _1c = one<CoeffT>()();
+        Polynomial<CoeffT> _1(_1c);
+        return _1;
+    }
+};
+
+
+/*
+ * initialization of Polynomial static data members
+ */
+
+template< typename CoeffT >
+const typename Polynomial<CoeffT>::coeff_type Polynomial<CoeffT>::zero_coeff
+    = zero<typename Polynomial<CoeffT>::coeff_type>()();
+
+/*
+ * Polynomial - Polynomial binary mathematical operators
+ */
+
+template< typename CoeffT >
+inline
+bool operator==(Polynomial<CoeffT> const& p, Polynomial<CoeffT> const& q)
+{
+    size_t d = p.real_degree();
+    if (d != q.real_degree()) return false;
+    for (size_t i = 0; i <= d; ++i)
+    {
+        if (p[i] != q[i]) return false;
+    }
+    return true;
+}
+
+template< typename CoeffT >
+inline
+bool operator!=(Polynomial<CoeffT> const& p, Polynomial<CoeffT> const& q)
+{
+    return !(p == q);
+}
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT>
+operator+( Polynomial<CoeffT> const& p, Polynomial<CoeffT> const& q )
+{
+    Polynomial<CoeffT> r(p);
+    r += q;
+    return r;
+}
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT>
+operator-( Polynomial<CoeffT> const& p, Polynomial<CoeffT> const& q )
+{
+    Polynomial<CoeffT> r(p);
+    r -= q;
+    return r;
+}
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT>
+operator*( Polynomial<CoeffT> const& p, Polynomial<CoeffT> const& q )
+{
+    Polynomial<CoeffT> r(p);
+    r *= q;
+    return r;
+}
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT> operator<<(Polynomial<CoeffT> const& p, size_t n)
+{
+    Polynomial<CoeffT> r(p);
+    r <<= n;
+    return r;
+}
+
+
+/*
+ *  polynomial-coefficient and coefficient-polynomial mathematical operators
+ */
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT>
+operator+( Polynomial<CoeffT> const& p, CoeffT const& c )
+{
+    Polynomial<CoeffT> r(p);
+    r += c;
+    return r;
+}
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT>
+operator+( CoeffT const& c, Polynomial<CoeffT> const& p)
+{
+    return (p + c);
+}
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT>
+operator-( Polynomial<CoeffT> const& p, CoeffT const& c )
+{
+    Polynomial<CoeffT> r(p);
+    r -= c;
+    return r;
+}
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT>
+operator-( CoeffT const& c, Polynomial<CoeffT> const& p)
+{
+    return (p - c);
+}
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT>
+operator*( Polynomial<CoeffT> const& p, CoeffT const& c )
+{
+    Polynomial<CoeffT> r(p);
+    r *= c;
+    return r;
+}
+
+template< typename CoeffT >
+inline
+Polynomial<CoeffT>
+operator*( CoeffT const& c, Polynomial<CoeffT> const& p)
+{
+    return (p * c);
+}
+
+
+/*
+ *  operator<< extension for printing Polynomial
+ *  and str() method for transforming a Polynomial into a string
+ */
+
+template< typename charT, typename CoeffT >
+inline
+std::basic_ostream<charT> &
+operator<< (std::basic_ostream<charT> & os, const Polynomial<CoeffT> & p)
+{
+    if (p.size() == 0) return os;
+    os << "{" << p[0];
+    for (size_t i = 1; i < p.size(); ++i)
+    {
+        os << ", " << p[i];
+    }
+    os << "}";
+    return os;
+}
+
+
+template< typename CoeffT >
+inline
+std::string Polynomial<CoeffT>::str() const
+{
+    std::ostringstream oss;
+    oss << (*this);
+    return oss.str();
+}
+
+
+} /*end namespace Geom*/  } /*end namespace SL*/
+
+
+
+
+#endif // _GEOM_SL_POLYNOMIAL_H_
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/symbolic/unity-builder.h b/include/2geom/symbolic/unity-builder.h
new file mode 100644
index 0000000..cb8046f
--- /dev/null
+++ b/include/2geom/symbolic/unity-builder.h
@@ -0,0 +1,102 @@
+/*
+ * Routines to make up "zero" and "one" elements of a ring
+ *
+ * Authors:
+ *      Marco Cecchetti <mrcekets at gmail.com>
+ *
+ * Copyright 2008  authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef _GEOM_SL_UNITY_BUILDER_H_
+#define _GEOM_SL_UNITY_BUILDER_H_
+
+
+#include <type_traits>
+
+
+
+namespace Geom { namespace SL {
+
+
+/*
+ *  zero builder function class type
+ *
+ *  made up a zero element, in the algebraic ring theory meaning,
+ *  for the type T
+ */
+
+template< typename T, bool numeric = std::is_arithmetic<T>::value >
+struct zero
+{};
+
+// specialization for basic numeric type
+template< typename T >
+struct zero<T, true>
+{
+    T operator() () const
+    {
+        return 0;
+    }
+};
+
+
+/*
+ *  one builder function class type
+ *
+ *  made up a one element, in the algebraic ring theory meaning,
+ *  for the type T
+ */
+
+template< typename T, bool numeric = std::is_arithmetic<T>::value >
+struct one
+{};
+
+// specialization for basic numeric type
+template< typename T >
+struct one<T, true>
+{
+    T operator() ()
+    {
+        return 1;
+    }
+};
+
+} /*end namespace Geom*/  } /*end namespace SL*/
+
+
+#endif // _GEOM_SL_UNITY_BUILDER_H_
+
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/transforms.h b/include/2geom/transforms.h
new file mode 100644
index 0000000..cc55e29
--- /dev/null
+++ b/include/2geom/transforms.h
@@ -0,0 +1,370 @@
+/**
+ * @file
+ * @brief Affine transformation classes
+ *//*
+ * Authors:
+ *   ? <?@?.?>
+ *   Krzysztof Kosiński <tweenk.pl@gmail.com>
+ *   Johan Engelen
+ * 
+ * Copyright ?-2012 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_TRANSFORMS_H
+#define LIB2GEOM_SEEN_TRANSFORMS_H
+
+#include <cmath>
+#include <2geom/forward.h>
+#include <2geom/affine.h>
+#include <2geom/angle.h>
+#include <boost/concept/assert.hpp>
+
+namespace Geom {
+
+/** @brief Type requirements for transforms.
+ * @ingroup Concepts */
+template <typename T>
+struct TransformConcept {
+    T t, t2;
+    Affine m;
+    Point p;
+    bool bool_;
+    Coord epsilon;
+    void constraints() {
+        m = t;  //implicit conversion
+        m *= t;
+        m = m * t;
+        m = t * m;
+        p *= t;
+        p = p * t;
+        t *= t;
+        t = t * t;
+        t = pow(t, 3);
+        bool_ = (t == t);
+        bool_ = (t != t);
+        t = T::identity();
+        t = t.inverse();
+        bool_ = are_near(t, t2);
+        bool_ = are_near(t, t2, epsilon);
+    }
+};
+
+/** @brief Base template for transforms.
+ * This class is an implementation detail and should not be used directly. */
+template <typename T>
+class TransformOperations
+    : boost::equality_comparable< T
+    , boost::multipliable< T
+      > >
+{
+public:
+    template <typename T2>
+    Affine operator*(T2 const &t) const {
+        Affine ret(*static_cast<T const*>(this)); ret *= t; return ret;
+    }
+};
+
+/** @brief Integer exponentiation for transforms.
+ * Negative exponents will yield the corresponding power of the inverse. This function
+ * can also be applied to matrices.
+ * @param t Affine or transform to exponantiate
+ * @param n Exponent
+ * @return \f$A^n\f$ if @a n is positive, \f$(A^{-1})^n\f$ if negative, identity if zero.
+ * @ingroup Transforms */
+template <typename T>
+T pow(T const &t, int n) {
+    BOOST_CONCEPT_ASSERT((TransformConcept<T>));
+    if (n == 0) return T::identity();
+    T result(T::identity());
+    T x(n < 0 ? t.inverse() : t);
+    if (n < 0) n = -n;
+    while ( n ) { // binary exponentiation - fast
+        if ( n & 1 ) { result *= x; --n; }
+        x *= x; n /= 2;
+    }
+    return result;
+}
+
+/** @brief Translation by a vector.
+ * @ingroup Transforms */
+class Translate
+    : public TransformOperations< Translate >
+{
+    Point vec;
+public:
+    /// Create a translation that doesn't do anything.
+    Translate() : vec(0, 0) {}
+    /// Construct a translation from its vector.
+    Translate(Point const &p) : vec(p) {}
+    /// Construct a translation from its coordinates.
+    Translate(Coord x, Coord y) : vec(x, y) {}
+
+    operator Affine() const { Affine ret(1, 0, 0, 1, vec[X], vec[Y]); return ret; }    
+    Coord operator[](Dim2 dim) const { return vec[dim]; }
+    Coord operator[](unsigned dim) const { return vec[dim]; }
+    Translate &operator*=(Translate const &o) { vec += o.vec; return *this; }
+    bool operator==(Translate const &o) const { return vec == o.vec; }
+
+    Point vector() const { return vec; }
+    /// Get the inverse translation.
+    Translate inverse() const { return Translate(-vec); }
+    /// Get a translation that doesn't do anything.
+    static Translate identity() { Translate ret; return ret; }
+
+    friend class Point;
+};
+
+inline bool are_near(Translate const &a, Translate const &b, Coord eps=EPSILON) {
+    return are_near(a[X], b[X], eps) && are_near(a[Y], b[Y], eps);
+}
+
+/** @brief Scaling from the origin.
+ * During scaling, the point (0,0) will not move. To obtain a scale with a different
+ * invariant point, combine with translation to the origin and back.
+ * @ingroup Transforms */
+class Scale
+    : public TransformOperations< Scale >
+{
+    Point vec;
+public:
+    /// Create a scaling that doesn't do anything.
+    Scale() : vec(1, 1) {}
+    /// Create a scaling from two scaling factors given as coordinates of a point.
+    explicit Scale(Point const &p) : vec(p) {}
+    /// Create a scaling from two scaling factors.
+    Scale(Coord x, Coord y) : vec(x, y) {}
+    /// Create an uniform scaling from a single scaling factor.
+    explicit Scale(Coord s) : vec(s, s) {}
+    inline operator Affine() const { Affine ret(vec[X], 0, 0, vec[Y], 0, 0); return ret; }
+
+    Coord operator[](Dim2 d) const { return vec[d]; }
+    Coord operator[](unsigned d) const { return vec[d]; }
+    //TODO: should we keep these mutators? add them to the other transforms?
+    Coord &operator[](Dim2 d) { return vec[d]; }
+    Coord &operator[](unsigned d) { return vec[d]; }
+    Scale &operator*=(Scale const &b) { vec[X] *= b[X]; vec[Y] *= b[Y]; return *this; }
+    bool operator==(Scale const &o) const { return vec == o.vec; }
+
+    Point vector() const { return vec; }
+    Scale inverse() const { return Scale(1./vec[0], 1./vec[1]); }
+    static Scale identity() { Scale ret; return ret; }
+
+    friend class Point;
+};
+
+inline bool are_near(Scale const &a, Scale const &b, Coord eps=EPSILON) {
+    return are_near(a[X], b[X], eps) && are_near(a[Y], b[Y], eps);
+}
+
+/** @brief Rotation around the origin.
+ * Combine with translations to the origin and back to get a rotation around a different point.
+ * @ingroup Transforms */
+class Rotate
+    : public TransformOperations< Rotate >
+{
+    Point vec; ///< @todo Convert to storing the angle, as it's more space-efficient.
+public:
+    /// Construct a zero-degree rotation.
+    Rotate() : vec(1, 0) {}
+    /** @brief Construct a rotation from its angle in radians.
+     * Positive arguments correspond to counter-clockwise rotations (if Y grows upwards). */
+    explicit Rotate(Coord theta) : vec(Point::polar(theta)) {}
+    /// Construct a rotation from its characteristic vector.
+    explicit Rotate(Point const &p) : vec(unit_vector(p)) {}
+    /// Construct a rotation from the coordinates of its characteristic vector.
+    explicit Rotate(Coord x, Coord y) { Rotate(Point(x, y)); }
+    operator Affine() const { Affine ret(vec[X], vec[Y], -vec[Y], vec[X], 0, 0); return ret; }
+
+    /** @brief Get the characteristic vector of the rotation.
+     * @return A vector that would be obtained by applying this transform to the X versor. */
+    Point vector() const { return vec; }
+    Coord angle() const { return atan2(vec); }
+    Coord operator[](Dim2 dim) const { return vec[dim]; }
+    Coord operator[](unsigned dim) const { return vec[dim]; }
+    Rotate &operator*=(Rotate const &o) { vec *= o; return *this; }
+    bool operator==(Rotate const &o) const { return vec == o.vec; }
+    Rotate inverse() const {
+        Rotate r;
+        r.vec = Point(vec[X], -vec[Y]); 
+        return r;
+    }
+    /// @brief Get a zero-degree rotation.
+    static Rotate identity() { Rotate ret; return ret; }
+    /** @brief Construct a rotation from its angle in degrees.
+     * Positive arguments correspond to clockwise rotations if Y grows downwards. */
+    static Rotate from_degrees(Coord deg) {
+        Coord rad = (deg / 180.0) * M_PI;
+        return Rotate(rad);
+    }
+    static Affine around(Point const &p, Coord angle);
+
+    friend class Point;
+};
+
+inline bool are_near(Rotate const &a, Rotate const &b, Coord eps=EPSILON) {
+    return are_near(a[X], b[X], eps) && are_near(a[Y], b[Y], eps);
+}
+
+/** @brief Common base for shearing transforms.
+ * This class is an implementation detail and should not be used directly.
+ * @ingroup Transforms */
+template <typename S>
+class ShearBase
+    : public TransformOperations< S >
+{
+protected:
+    Coord f;
+    ShearBase(Coord _f) : f(_f) {}
+public:
+    Coord factor() const { return f; }
+    void setFactor(Coord nf) { f = nf; }
+    S &operator*=(S const &s) { f += s.f; return static_cast<S &>(*this); }
+    bool operator==(S const &s) const { return f == s.f; }
+    S inverse() const { S ret(-f); return ret; }
+    static S identity() { S ret(0); return ret; }
+
+    friend class Point;
+    friend class Affine;
+};
+
+/** @brief Horizontal shearing.
+ * Points on the X axis will not move. Combine with translations to get a shear
+ * with a different invariant line.
+ * @ingroup Transforms */
+class HShear
+    : public ShearBase<HShear>
+{
+public:
+    explicit HShear(Coord h) : ShearBase<HShear>(h) {}
+    operator Affine() const { Affine ret(1, 0, f, 1, 0, 0); return ret; }
+};
+
+inline bool are_near(HShear const &a, HShear const &b, Coord eps=EPSILON) {
+    return are_near(a.factor(), b.factor(), eps);
+}
+
+/** @brief Vertical shearing.
+ * Points on the Y axis will not move. Combine with translations to get a shear
+ * with a different invariant line.
+ * @ingroup Transforms */
+class VShear
+    : public ShearBase<VShear>
+{
+public:
+    explicit VShear(Coord h) : ShearBase<VShear>(h) {}
+    operator Affine() const { Affine ret(1, f, 0, 1, 0, 0); return ret; }
+};
+
+inline bool are_near(VShear const &a, VShear const &b, Coord eps=EPSILON) {
+    return are_near(a.factor(), b.factor(), eps);
+}
+
+/** @brief Combination of a translation and uniform scale.
+ * The translation part is applied first, then the result is scaled from the new origin.
+ * This way when the class is used to accumulate a zoom transform, trans always points
+ * to the new origin in original coordinates.
+ * @ingroup Transforms */
+class Zoom
+    : public TransformOperations< Zoom >
+{
+    Coord _scale;
+    Point _trans;
+    Zoom() : _scale(1), _trans() {}
+public:
+    /// Construct a zoom from a scaling factor.
+    explicit Zoom(Coord s) : _scale(s), _trans() {}
+    /// Construct a zoom from a translation.
+    explicit Zoom(Translate const &t) : _scale(1), _trans(t.vector()) {}
+    /// Construct a zoom from a scaling factor and a translation.
+    Zoom(Coord s, Translate const &t) : _scale(s), _trans(t.vector()) {}
+
+    operator Affine() const {
+        Affine ret(_scale, 0, 0, _scale, _trans[X] * _scale, _trans[Y] * _scale);
+        return ret;
+    }
+    Zoom &operator*=(Zoom const &z) {
+        _trans += z._trans / _scale;
+        _scale *= z._scale;
+        return *this;
+    }
+    bool operator==(Zoom const &z) const { return _scale == z._scale && _trans == z._trans; }
+
+    Coord scale() const { return _scale; }
+    void setScale(Coord s) { _scale = s; }
+    Point translation() const { return _trans; }
+    void setTranslation(Point const &p) { _trans = p; }
+    Zoom inverse() const { Zoom ret(1/_scale, Translate(-_trans*_scale)); return ret; }
+    static Zoom identity() { Zoom ret(1.0); return ret; }
+    static Zoom map_rect(Rect const &old_r, Rect const &new_r);
+    
+    friend class Point;
+    friend class Affine;
+};
+
+inline bool are_near(Zoom const &a, Zoom const &b, Coord eps=EPSILON) {
+    return are_near(a.scale(), b.scale(), eps) &&
+           are_near(a.translation(), b.translation(), eps);
+}
+
+/** @brief Specialization of exponentiation for Scale.
+ * @relates Scale */
+template<>
+inline Scale pow(Scale const &s, int n) {
+    Scale ret(::pow(s[X], n), ::pow(s[Y], n));
+    return ret;
+}
+/** @brief Specialization of exponentiation for Translate.
+ * @relates Translate */
+template<>
+inline Translate pow(Translate const &t, int n) {
+    Translate ret(t[X] * n, t[Y] * n);
+    return ret;
+}
+
+
+/** @brief Reflects objects about line.
+ * The line, defined by a vector along the line and a point on it, acts as a mirror.
+ * @ingroup Transforms
+ * @see Line::reflection()
+ */
+Affine reflection(Point const & vector, Point const & origin);
+
+//TODO: decomposition of Affine into some finite combination of the above classes
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_TRANSFORMS_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/include/2geom/utils.h b/include/2geom/utils.h
new file mode 100644
index 0000000..87f49bb
--- /dev/null
+++ b/include/2geom/utils.h
@@ -0,0 +1,114 @@
+/**
+ * \file
+ * \brief  Various utility functions.
+ *//*
+ * Copyright 2007 Johan Engelen <goejendaagh@zonnet.nl>
+ * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_UTILS_H
+#define LIB2GEOM_SEEN_UTILS_H
+
+#include <cstddef>
+#include <vector>
+#include <boost/operators.hpp>
+
+namespace Geom {
+
+// Throw these errors instead of aserting so code can handle them if needed.
+using ErrorCode = int;
+enum Errors : ErrorCode {
+    GEOM_ERR_INTERSECGRAPH,
+};
+
+void binomial_coefficients(std::vector<size_t>& bc, std::size_t n);
+
+struct EmptyClass {};
+
+/**
+ * @brief Noncommutative multiplication helper.
+ * Generates operator*(T, U) from operator*=(T, U). Does not generate operator*(U, T)
+ * like boost::multipliable does. This makes it suitable for noncommutative cases,
+ * such as transforms.
+ */
+template <class T, class U, class B = EmptyClass>
+struct MultipliableNoncommutative : B
+{
+    friend T operator*(T const &lhs, U const &rhs) {
+        T nrv(lhs); nrv *= rhs; return nrv;
+    }
+};
+
+/** @brief Null output iterator
+ * Use this if you want to discard a result returned through an output iterator. */
+struct NullIterator
+    : public boost::output_iterator_helper<NullIterator>
+{
+    NullIterator() {}
+
+    template <typename T>
+    void operator=(T const &) {}
+};
+
+/** @brief Get the next iterator in the container with wrap-around.
+ * If the iterator would become the end iterator after incrementing,
+ * return the begin iterator instead. */
+template <typename Iter, typename Container>
+Iter cyclic_next(Iter i, Container &c) {
+    ++i;
+    if (i == c.end()) {
+        i = c.begin();
+    }
+    return i;
+}
+
+/** @brief Get the previous iterator in the container with wrap-around.
+ * If the passed iterator is the begin iterator, return the iterator
+ * just before the end iterator instead. */
+template <typename Iter, typename Container>
+Iter cyclic_prior(Iter i, Container &c) {
+    if (i == c.begin()) {
+        i = c.end();
+    }
+    --i;
+    return i;
+}
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_UTILS_H
+
+/*
+  Local Variables:
+  mode:c++
+  c-file-style:"stroustrup"
+  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+  indent-tabs-mode:nil
+  fill-column:99
+  End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
-- 
cgit v1.2.3