[Pkg-octave-commit] [octave-level-set] 01/02: Imported Upstream version 0.3.0

Rafael Laboissière rlaboiss-guest at moszumanska.debian.org
Wed Sep 7 23:58:52 UTC 2016


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

rlaboiss-guest pushed a commit to branch master
in repository octave-level-set.

commit 5122f454165a1f506c45cb7a21a288e043719776
Author: Rafael Laboissiere <rafael at debian.org>
Date:   Sat Aug 20 16:19:21 2016 -0300

    Imported Upstream version 0.3.0
---
 CITATION                          |   17 +
 COPYING                           |  674 +++++++
 DESCRIPTION                       |   10 +
 INDEX                             |   49 +
 NEWS                              |   35 +
 autogen.sh                        |   25 +
 inst/fastmarching.m               |  498 ++++++
 inst/ls_absolute_geom.m           |   82 +
 inst/ls_animate_evolution.m       |   71 +
 inst/ls_build_mesh.m              |  271 +++
 inst/ls_check.m                   |   98 +
 inst/ls_complement.m              |   66 +
 inst/ls_disjoint.m                |   83 +
 inst/ls_distance_fcn.m            |  133 ++
 inst/ls_enforce.m                 |  107 ++
 inst/ls_enforce_speed.m           |  148 ++
 inst/ls_equal.m                   |   67 +
 inst/ls_extract_solution.m        |  174 ++
 inst/ls_find_geometry.m           |  572 ++++++
 inst/ls_genbasic.m                |  309 ++++
 inst/ls_get_tests.m               |   80 +
 inst/ls_hausdorff_dist.m          |  132 ++
 inst/ls_init_narrowband.m         |  154 ++
 inst/ls_inside.m                  |   60 +
 inst/ls_intersect.m               |   90 +
 inst/ls_isempty.m                 |   54 +
 inst/ls_issubset.m                |   61 +
 inst/ls_nb_from_geom.m            |  174 ++
 inst/ls_normalise.m               |   74 +
 inst/ls_setdiff.m                 |  122 ++
 inst/ls_setxor.m                  |   95 +
 inst/ls_sign_colourmap.m          |  244 +++
 inst/ls_signed_distance.m         |  144 ++
 inst/ls_solve_stationary.m        |  299 ++++
 inst/ls_time_step.m               |  253 +++
 inst/ls_union.m                   |   86 +
 inst/maze.png                     |  Bin 0 -> 54138 bytes
 inst/private/ls_copy_sign.m       |   81 +
 inst/private/ls_union_intersect.m |   43 +
 inst/private/so_load_compressed.m |   35 +
 inst/so_example_problem.m         |  309 ++++
 inst/so_explore_descent.m         |  362 ++++
 inst/so_init_params.m             |   82 +
 inst/so_replay_descent.m          |  479 +++++
 inst/so_run_descent.m             |  458 +++++
 inst/so_save_descent.m            |  190 ++
 inst/so_step_armijo.m             |  311 ++++
 inst/upwind_gradient_norm.m       |  140 ++
 src/FastMarching.cpp              |  417 +++++
 src/FastMarching.hpp              |  616 +++++++
 src/FastMarching.tpp              |   82 +
 src/Heap.hpp                      |  262 +++
 src/Heap.tpp                      |  238 +++
 src/Makefile.in                   |   63 +
 src/Utils.cpp                     |   82 +
 src/Utils.hpp                     |   78 +
 src/Utils.tpp                     |   83 +
 src/configure                     | 3584 +++++++++++++++++++++++++++++++++++++
 src/configure.ac                  |   31 +
 src/geomBoundary.cpp              |  376 ++++
 src/geomElements.cpp              |  130 ++
 src/geomGamma.cpp                 |  378 ++++
 src/heapsort.cpp                  |   87 +
 src/internal_fastmarching.cpp     |  131 ++
 src/internal_init_narrowband.cpp  |  145 ++
 src/internal_mesh.cpp             |  525 ++++++
 src/marching.cpp                  |  103 ++
 src/nbFromGeom.cpp                |  166 ++
 src/upwindGrad.cpp                |  152 ++
 69 files changed, 15830 insertions(+)

diff --git a/CITATION b/CITATION
new file mode 100644
index 0000000..b9c4328
--- /dev/null
+++ b/CITATION
@@ -0,0 +1,17 @@
+The level-set package was created for a PhD project at the University of Graz
+in the International Research Training Group IGDK 1754.  To cite the package
+in publications, use:
+
+  Daniel Kraft.  A Level-Set Framework for Shape Optimisation.
+  PhD thesis, University of Graz, 2015.
+  http://www.domob.eu/research/phd/thesis.pdf
+
+A BibTeX entry for LaTeX users is:
+
+  @phdthesis{phdKraft,
+    author = {Daniel Kraft},
+    title = {A Level-Set Framework for Shape Optimisation},
+    school = {University of Graz},
+    year = {2015},
+    url = {http://www.domob.eu/research/phd/thesis.pdf}
+  }
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/DESCRIPTION b/DESCRIPTION
new file mode 100644
index 0000000..552847e
--- /dev/null
+++ b/DESCRIPTION
@@ -0,0 +1,10 @@
+Name: level-set
+Version: 0.3.0
+Date: 2015-12-17
+Author: Daniel Kraft <daniel.kraft at uni-graz.at>
+Maintainer: Daniel Kraft <d at domob.eu>
+Title: Level Set
+Description: Routines for calculating the time-evolution of the level-set equation and extracting geometric information from the level-set function.
+Depends: octave (>= 3.6.0)
+Autoload: no
+License: GPLv3+
diff --git a/INDEX b/INDEX
new file mode 100644
index 0000000..5ac4e4c
--- /dev/null
+++ b/INDEX
@@ -0,0 +1,49 @@
+level-set >> Level Set
+The Fast Marching Algorithm
+  fastmarching
+Distance Functions
+  ls_init_narrowband
+  ls_distance_fcn
+  ls_signed_distance
+  ls_hausdorff_dist
+Time Evolution of Level-Sets
+  ls_solve_stationary
+  ls_extract_solution
+Time Stepping of the Level-Set Equation
+  upwind_gradient_norm
+  ls_time_step
+Geometric Constraints
+  ls_check
+  ls_enforce
+  ls_enforce_speed
+Set Predicates
+  ls_inside
+  ls_issubset
+  ls_isempty
+  ls_equal
+  ls_disjoint
+Set Operations and Construction of Shapes
+  ls_genbasic
+  ls_complement
+  ls_union
+  ls_intersect
+  ls_setdiff
+  ls_setxor
+Geometry of Level-Sets in 2D
+  ls_find_geometry
+  ls_absolute_geom
+  ls_nb_from_geom
+  ls_build_mesh
+Miscellaneous Routines
+  ls_animate_evolution
+  ls_get_tests
+  ls_normalise
+  ls_sign_colourmap
+Shape Optimisation with Level Sets
+  so_init_params
+  so_step_armijo
+  so_run_descent
+  so_save_descent
+  so_replay_descent
+  so_explore_descent
+  so_example_problem
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..51ce305
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,35 @@
+Version 0.3.0 (2015-12-17):
+  * Changed the build system to use autoconf.
+  * ls_genbasic can now be called with arbitrary coordinates, they need
+    not correspond to the full grid.  In particular, single points
+    can be passed as arguments as well.
+  * Add new routines for shape optimisation with level sets.  The main
+    one is so_run_descent.
+  * Allow saving and replaying shape optimisation descents.  This is
+    accomplished with so_save_descent and so_replay_descent.  Also
+    so_explore_descent can be used to interactively explore a descent log.
+
+Version 0.2.0 (2015-02-18):
+  * The .m file functions don't check nargout() any longer.  This is checked
+    by the interpreter anyway.  Also, usage() as replaced by either
+    print_usage() or error().
+  * New function 'ls_normalise' that does the normalisation of exactly zero
+    values that was previously done only in ls_find_geometry.
+  * 'ls_inside' as well as 'ls_equal' (and all other set functions that use
+    them internally) now consider negative zeros to be part of the level-set
+    domain.  This is consistent with the behaviour of 'ls_normalise'.
+  * 'ls_animate_evolution' only clears the contour between frames now,
+    so that the "jitter" is drastically reduced.
+  * The usage of 'ls_sign_colourmap' was slightly changed.  It allows
+    now to specify the colours making up the gradients.
+  * New feature: Time stepping of the level-set equation as alternative
+    method for evolution.  New methods 'upwind_gradient_norm' and
+    'ls_time_step'.
+  * New function 'ls_distance_fcn'.  It calculates the non-signed distance
+    function, and is faster than 'ls_signed_distance'.  Only exterior points
+    are processed with Fast Marching.
+  * New function 'ls_hausdorff_dist' to approximate the Hausdorff
+    distance between two sets from their distance functions.
+
+Version 0.1.0 (2014-06-09):
+  * Initial release.
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..6ea4272
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+#   GNU Octave level-set package.
+#   Copyright (C) 2015  Daniel Kraft <d at domob.eu>
+#
+#   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 3 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, see <http://www.gnu.org/licenses/>.
+
+# Perform the autoconf initialisation in src.
+
+set -e
+
+cd src/
+aclocal
+autoconf
diff --git a/inst/fastmarching.m b/inst/fastmarching.m
new file mode 100644
index 0000000..f4489fd
--- /dev/null
+++ b/inst/fastmarching.m
@@ -0,0 +1,498 @@
+##  Copyright (C) 2013-2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{u} =} fastmarching (@var{u0}, @var{f})
+## @deftypefnx {Function File} {[@var{u}, @var{g}] =} fastmarching (@var{u0}, @var{g0}, @var{f})
+## 
+## Solve the Eikonal equation using the Fast-Marching Method.  In particular,
+## the equation
+## @tex
+## \begin{equation*}
+##   \left| \nabla u \right| = f
+## \end{equation*}
+## @end tex
+## @ifnottex
+##
+##
+## @example
+## | grad u | = f
+## @end example
+##
+## @end ifnottex
+## is solved on a multi-dimensional domain.  @var{u0} and @var{f} must be
+## multi-dimensional arrays of the same dimension and size, and the returned
+## solution @var{u} will be also of this size.  @var{f} should be positive,
+## and we assume the grid spacing is unity (or, equivalently, has been
+## absorbed into f already).
+## 
+## @var{u0} defines the initial setting and the domain.  It should contain the
+## initial distances for points that are used as initially alive points,
+## be @code{Inf} for points not part of the domain, and @code{NA} at all
+## other points.  The latter are considered as far-away at the beginning
+## and their distances will be calculated.  @var{f} is ignored at all but
+## the far-away points.
+## 
+## If @var{g0} is also given, it should contain function values on initially
+## alive points that will be extended onto the domain along the
+## ``normal directions''.  This means that
+## @tex
+## \begin{equation*}
+##  \nabla g \cdot \nabla u = 0
+## \end{equation*}
+## @end tex
+## @ifnottex
+##
+##
+## @example
+## grad g * grad u = 0
+## @end example
+##
+## @end ifnottex
+## will be fulfilled for the solution @var{u} of the Eikonal equation.
+## @var{g} will be @code{NA} at points not in the domain or not reached by the
+## fast marching.
+## 
+## At return, @var{u} will contain the calculated distances.
+## @code{Inf} values will be retained, and it may still contain @code{NA}
+## at points to which no suitable path can be found.
+##
+## @seealso{ls_signed_distance, ls_solve_stationary}
+## @end deftypefn
+
+function [u, g] = fastmarching (u0, arg2, arg3)
+  if (nargin () == 2)
+    f = arg2;
+    g0 = NA (size (u0));
+    if (nargout () > 1)
+      print_usage ();
+    endif
+  elseif (nargin () ~= 3)
+    print_usage ();
+  else
+    assert (nargin () == 3)
+    f = arg3;
+    g0 = arg2;
+  endif
+
+  where = isna (u0);
+  if (~all (f(where) > 0))
+    error ("F must be positive");
+  endif
+
+  sz = size (u0);
+  if (~all (sz == size (f)))
+    error ("U0 and F must be of the same size");
+  endif
+  if (~all (sz == size (g0)))
+    error ("U0 and G0 must be of the same size");
+  endif
+
+  domain = ~isinf (u0);
+  alive = ~isna (u0);
+  if (~all (alive(domain) == (isfinite (u0(domain)))))
+    error ("U0 shold only contain Inf, NA and finite values");
+  endif
+  
+  [u, g, reached] = __levelset_internal_fastmarching (domain, u0, g0, alive, f);
+  u(~reached) = NA;
+  u(~domain) = Inf;
+  g(~reached | ~domain) = NA;
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  fastmarching ()
+%!error <Invalid call to>
+%!  fastmarching (1, 2, 3, 4)
+%!error <Invalid call to>
+%!  [a, b] = fastmarching (1, 2)
+%!error <U0 and F must be of the same size>
+%!  fastmarching ([1, 2], [1; 2])
+%!error <U0 and G0 must be of the same size>
+%!  fastmarching ([1, 2], [1; 2], [1, 2])
+%!error <F must be positive>
+%!  fastmarching ([NA, 1], [0, 5]);
+
+% Basic test for obstacle with a square for which propagation is only
+% allowed along the outer edges.
+%!test
+%!  n = 600;
+%!  x = linspace (0, 1, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!  u0 = NA (n, n);
+%!  u0(2 : end - 1, 2 : end - 1) = Inf;
+%!  u0(1, 1) = 0;
+%!  f = 1 + YY;
+%!  u = fastmarching (u0, h * f);
+%!
+%!  % Length of vertical side: integrate (1 + y, y, 0, x)
+%!  vertLength = @(x) x/2 .* (x + 2);
+%!
+%!  % Speed difference at top and bottom, gives where the
+%!  % paths will meet on the bottom side.
+%!  %   solve (x * fBot = fTop + (1 - x) * fBot, x)
+%!  fTop = mean (f(1, :));
+%!  fBot = mean (f(end, :));
+%!  xMeet = (fTop + fBot) / 2 / fBot;
+%!  botRow = vertLength (1) + fBot * x;
+%!  where = (x > xMeet);
+%!  botRow(where) = vertLength (1) + 1 + fBot * (1 - x(where));
+%!
+%!  assert (u(1, :), x, sqrt (eps));
+%!  assert (u(:, 1), vertLength (x'), 1e-3);
+%!  assert (u(:, end), 1 + vertLength (x'), 1e-3);
+%!  assert (u(end, :), botRow, 1e-3);
+
+% Check 1D situation.  Also verifies that it works with non-zero initial values.
+%!test
+%!  n = 1100;
+%!  x = linspace (0, 1, n);
+%!  h = x(2) - x(1);
+%!
+%!  obst1 = round (0.5 / h);
+%!  obst2 = round (0.75 / h);
+%!  s1 = round (0.2 / h);
+%!  s1val = 1;
+%!  s2 = round (0.9 / h);
+%!  s2val = -1;
+%!
+%!  u0 = NA (n, 1);
+%!  u0([obst1, obst2]) = Inf;
+%!  u0([s1, s2]) = [s1val, s2val];
+%!  f = ones (size (u0));
+%!  g0 = NA (size (u0));
+%!  g0([s1, s2]) = [s1val, s2val];
+%!
+%!  [u, g] = fastmarching (u0, g0, f * h);
+%!
+%!  gDesired = NA (size (u0));
+%!  gDesired(1 : obst1 - 1) = s1val;
+%!  gDesired(obst2 + 1 : end) = s2val;
+%!  assert (g, gDesired, sqrt (eps));
+%!
+%!  uDesired = NA (size (u0));
+%!  uDesired([obst1, obst2]) = Inf;
+%!  uDesired(1 : obst1 - 1) = s1val + abs (s1 * h - x(1 : obst1 - 1));
+%!  uDesired(obst2 + 1 : end) = s2val + abs (s2 * h - x(obst2 + 1 : end));
+%!  assert (u, uDesired, 1e-3);
+
+% Check 0D situation (which is trivial but nevertheless).
+%!test
+%!  assert (fastmarching ([], []), []);
+
+% Extend from a circular initial region outwards, also including a function
+% value which is the angle.  For this configuration, the expected result
+% is easy to calculate.  Test in 3D.
+%!test
+%!  n = 42;
+%!  r = 1;
+%!  R = 2;
+%!  x = linspace (-R, R, n);
+%!  h = x(2) - x(1);
+%!
+%!  [XX, YY, ZZ] = ndgrid (x);
+%!  RR = sqrt (XX.^2 + YY.^2 + ZZ.^2);
+%!  PPhi = NA (size (XX));
+%!  for i = 1 : numel (PPhi)
+%!    pt = [XX(i), YY(i), ZZ(i)];
+%!    PPhi(i) = acos (dot (pt, [0, 0, 1]) / norm (pt));
+%!  endfor
+%!
+%!  initial = (RR < r);
+%!  u0 = NA (size (XX));
+%!  u0(initial) = 0;
+%!  g0 = NA (size (XX));
+%!  g0(initial) = PPhi(initial);
+%!  f = ones (size (u0));
+%!  [u, g] = fastmarching (u0, g0, h * f);
+%!
+%!  assert (u(~initial), RR(~initial) - r, 1e-1);
+%!  assert (g(~initial), PPhi(~initial), 2e-1);
+
+% Function for calculating the approximate gradient of a given function
+% using central differences.  This is used to verify grad g * grad u = 0.
+%!function [dx, dy] = gradient (fcn, h)
+%!  ext = NA (size (fcn) + 2);
+%!  ext(2 : end - 1, 2 : end - 1) = fcn;
+%!  ext(:, 1) = 2 * ext(:, 2) - ext(:, 3);
+%!  ext(:, end) = 2 * ext(:, end - 1) - ext(:, end - 2);
+%!  ext(1, :) = 2 * ext(2, :) - ext(3, :);
+%!  ext(end, :) = 2 * ext(end - 1, :) - ext(end - 2, :);
+%!  dx = (ext(2 : end - 1, 3 : end) - ext(2 : end - 1, 1 : end - 2)) / (2 * h);
+%!  dy = (ext(3 : end, 2 : end - 1) - ext(1 : end - 2, 2 : end - 1)) / (2 * h);
+%!endfunction
+
+% Test extending of function values in a more general setting.  We verify
+% that the "defining equation"
+%
+%   grad g * grad u = 0
+%
+% is indeed valid.
+%!test
+%!  n = 1000;
+%!  x = linspace (-2, 2, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi0 = 2 * XX.^2 + 0.4 * YY.^2 - 1;
+%!  initial = (phi0 < 0);
+%!  %contour (XX, YY, phi0, [0, 0], "r");
+%!
+%!  f = ones (size (XX));
+%!  u0 = NA (size (XX));
+%!  u0(initial) = 0;
+%!  g0 = NA (size (XX));
+%!  g0(initial) = sin (atan2 (YY(initial), XX(initial)));
+%!
+%!  [u, g] = fastmarching (u0, g0, h * f);
+%!
+%!  [dxu, dyu] = gradient (u, h);
+%!  [dxg, dyg] = gradient (g, h);
+%!
+%!  % We only verify the mean error here, since there tend
+%!  % to be lower-dimensional lines on which the error is
+%!  % large while it is very low otherwise.
+%!  err = abs (dxu .* dxg + dyu .* dyg);
+%!  assert (mean (err(initial)), 0, 1e-3);
+
+% Particular test situation where during the fast marching
+% procedure the distance of a point is later calculated to
+% be further from the initial set.
+%!test
+%!  u0 = [1.257221, NA, NA; Inf, 0.275128, 0.433001];
+%!  f = [NA, 0.85714, 0.66667; NA, NA, NA];
+%!  u = fastmarching (u0, f);
+
+% Test the case of far-away alive point.
+%!test
+%!  u0 = [0, NA; Inf, 2];
+%!  f = ones (size (u0));
+%!  u = fastmarching (u0, f);
+%!  assert (u(1, 2), 1, sqrt (eps));
+
+% Test for the case with multiple alive neighbours along a single dimension.
+%!test
+%!  u0 = [0, NA, NA, NA, 0.1; ...
+%!        Inf, Inf, Inf, Inf, Inf; ...
+%!        0.1, NA, NA, NA, 0];
+%!  f = ones (size (u0));
+%!  u = fastmarching (u0, f);
+%!
+%!  uExpected = [0, 1, 2, 1.1, 0.1; ...
+%!               Inf, Inf, Inf, Inf, Inf; ...
+%!               0.1, 1.1, 2, 1, 0];
+%!  assert (u, uExpected, sqrt (eps));
+
+% Estimate the runtime complexity.
+%!test
+%!  ns = [101, 201, 501, 1001, 2001];
+%!  realNs = NA (size (ns));
+%!  times = NA (size (ns));
+%!  for i = 1 : length (ns)
+%!    nMid = (ns(i) + 1) / 2;
+%!    assert (nMid, round (nMid));
+%!    u0 = NA (ns(i), ns(i));
+%!    u0(nMid, nMid) = 0;
+%!    f = ones (size (u0));
+%!    realNs(i) = numel (u0);
+%!
+%!    old = cputime ();
+%!    u = fastmarching (u0, f);
+%!    new = cputime ();
+%!    times(i) = new - old;
+%!
+%!    printf ("n = %4d: t = %6.3f s\n", ns(i), times(i));
+%!  endfor
+%!
+%!  nLogN = realNs .* log (realNs);
+%!  p = polyfit (log (realNs), log (times), 1);
+%!  pLogN = polyfit (log(nLogN), log (times), 1);
+%!  printf ("Exponents:\n        O(n): %4.2f\n  O(n log n): %4.2f\n", ...
+%!          p(1), pLogN(1));
+%!  assert (p(1), 1, 1e-1);
+%!  assert (pLogN(1), 1, 1e-1);
+
+% Compare the solution of the level-set equation using finite differences
+% and a viscosity term to our formula.  If the initial profile is constructed
+% by fastmarching to satisfy F | grad phi0 | = 1, then it should just
+% be shifted down in time.
+%!test
+%!  T = 1.6;
+%!  timeSteps = 10000;
+%!  L = 2;
+%!  gridSteps = 10000;
+%!  eps = 1e-4;
+%!  floorValue = -1/2;
+%!  tol = 7e-2;
+%!  
+%!  t = linspace (0, T, timeSteps);
+%!  dt = t(2) - t(1);
+%!  x = linspace (-L/2, L/2, gridSteps);
+%!  h = x(2) - x(1);
+%!  
+%!  F = sqrt (abs (x));
+%!  phi0 = NA (size (x));
+%!  phi0(1 : end/4) = floorValue;
+%!  phi0(3*end/4 : end) = floorValue;
+%!  phi0 = fastmarching (phi0, h ./ F);
+%!  
+%!  % Plot the speed field and initial data.
+%!  %{
+%!  figure ();
+%!  plot (x, F, "r", x, phi0, "k");
+%!  legend ("F", "\\phi_0");
+%!  title ("Speed Field and Initial Data");
+%!  ax = [-L/2, L/2, -1, 1];
+%!  axis (ax);
+%!  %}
+%!  
+%!  % Create the Laplacian as matrix.  We assume homogeneous Neumann boundary
+%!  % conditions so that the initial data can be chosen as we like.
+%!  Delta = spdiags (repmat ([1, -2, 1], gridSteps, 1), [-1, 0, 1], ...
+%!                   gridSteps, gridSteps);
+%!  Delta(1, 1) += Delta(1, 2);
+%!  Delta(end, end) += Delta(end - 1, end);
+%!  Delta /= h^2;
+%!  
+%!  % Perform the calculation.  The Laplacian is done implicitly, the
+%!  % non-linear term explicitly:
+%!  %
+%!  %   phi+ - phi0 = dt (eps Delta phi+ - F |grad phi0|)
+%!  %   => (I - dt eps Delta) phi+ = phi0 - F |grad phi0|
+%!  
+%!  %figure ();
+%!  phi = phi0';
+%!  M = eye (gridSteps) - dt * eps * Delta;
+%!  for tInd = 2 : timeSteps
+%!  
+%!    % Calculate derivatives as symmetric differences, and zero on the
+%!    % boundary.  Since the data is flat there, this is correct.
+%!    d = NA (size (phi));
+%!    low = 1 : gridSteps - 2;
+%!    mid = 2 : gridSteps - 1;
+%!    high = 3 : gridSteps;
+%!    d(mid) = (phi(high) - phi(low)) / (2 * h);
+%!    d([1, end]) = 0;
+%!    gradNorm = abs (d);
+%!  
+%!    % Do the solve.
+%!    phi = M \ (phi - dt * F' .* gradNorm);
+%!    phiEx = max (phi0 - t(tInd), floorValue);
+%!    assert (phi, phiEx', tol);
+%!  
+%!    % Show solution as animation in real time.
+%!    %{
+%!    if (mod (tInd, 100) == 0)
+%!      plot (x, phi, "r", x, phiEx, "k");
+%!      legend ("Approximate", "Exact");
+%!      axis (ax);
+%!      title (sprintf ("t = %.2f", t(tInd)));
+%!      drawnow ();
+%!      %sleep (0.1);
+%!    endif
+%!    %}
+%!  endfor
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+% Plot how the distance and an extended function "flow" around an obstacle.
+%!demo
+%!  n = 100;
+%!  x = linspace (-1, 1, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  u0 = NA (size (XX));
+%!  u0(sqrt (XX.^2 + YY.^2) < 0.5) = Inf;
+%!  u0(end, :) = 0;
+%!  f = 3 + XX;
+%!  g0 = NA (size (XX));
+%!  g0(end, :) = x;
+%!
+%!  [u, g] = fastmarching (u0, g0, h * f);
+%!
+%!  figure ();
+%!  subplot (2, 2, 1);
+%!  imagesc (f);
+%!  colorbar ();
+%!  title ("Speed Field");
+%!  subplot (2, 2, 2);
+%!  imagesc (u0);
+%!  colorbar ();
+%!  title ("Initial Distances");
+%!  subplot (2, 2, 3);
+%!  imagesc (u);
+%!  colorbar ();
+%!  title ("Distance u");
+%!  subplot (2, 2, 4);
+%!  imagesc (g);
+%!  colorbar ();
+%!  title ("Extended Function g");
+
+% Demo solving a "maze".
+%!demo
+%!  pkgs = pkg ("list");
+%!  file = false;
+%!  for i = 1 : length (pkgs)
+%!    if (strcmp (pkgs{i}.name, "level-set"))
+%!      file = fullfile (pkgs{i}.dir, "maze.png");
+%!    endif
+%!  endfor
+%!  if (!file)
+%!    error ("could not locate example image file");
+%!  endif
+%!  img = double (imread (file)) / 255;
+%!
+%!  % Red is the start area, white (not black) the accessible domain.
+%!  start = (img(:, :, 1) > img(:, :, 2) + img(:, :, 3));
+%!  domain = (img(:, :, 2) + img(:, :, 3) > 1.0) | start;
+%!
+%!  u0 = NA (size (domain));
+%!  u0(start) = 0;
+%!  u0(~domain) = Inf;
+%!  f = ones (size (u0));
+%!
+%!  u = fastmarching (u0, f);
+%!  u = u / max (u(isfinite (u)));
+%!
+%!  % Format result as image.
+%!  walls = isinf (u);
+%!  notReached = isna (u);
+%!  ur = u;
+%!  ug = u;
+%!  ub = u;
+%!  ur(notReached) = 1;
+%!  ug(notReached) = 0;
+%!  ub(notReached) = 0;
+%!  ur(walls) = 0;
+%!  ug(walls) = 0;
+%!  ub(walls) = 1;
+%!  ur(start) = 1;
+%!  ug(start) = 1;
+%!  ub(start) = 0;
+%!  u = NA (size (u, 1), size (u, 2), 3);
+%!  u(:, :, 1) = ur;
+%!  u(:, :, 2) = ug;
+%!  u(:, :, 3) = ub;
+%!  figure ();
+%!  imshow (u);
+%!  title ("Solved Maze.  Blue: Walls, Red: Unreachable, Yellow: Start.");
diff --git a/inst/ls_absolute_geom.m b/inst/ls_absolute_geom.m
new file mode 100644
index 0000000..bfbd0da
--- /dev/null
+++ b/inst/ls_absolute_geom.m
@@ -0,0 +1,82 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{geom} =} ls_absolute_geom (@var{geom}, @var{XX}, @var{YY})
+##
+## Extend the geometry structure @var{geom} of @code{ls_find_geometry}
+## to include absolute coordinates.  In addition to @code{ls_find_geometry},
+## this function has access to absolute grid-point coordinates, and uses them
+## to set the additional @code{@var{geom}.ispts.coord} field to absolute
+## coordinates.  The format is the same as the relative coordinates
+## in @code{@var{geom}.ispts.incoord}.  Also, it adds the new entry
+## @code{nodes} to @var{geom}, with the following fields:
+##
+## @table @code
+## @item n
+## Total number of nodes.  This is the number of entries in @var{phi}.
+##
+## @item coord
+## @code{n} x 2 matrix containing the absolute coordinates of each node.
+## The nodes are numbered in the range 1-- at code{n} in the internal
+## ordering of Octave.
+## @end table
+##
+## Currently, only 2D is supported.  @var{XX} and @var{YY} should be the
+## grid-point coordinates according to @code{meshgrid} or @code{ndgrid}.
+## 
+## @seealso{ls_find_geometry, meshgrid, ndgrid}
+## @end deftypefn
+
+function geom = ls_absolute_geom (geom, XX, YY)
+  if (nargin () ~= 3)
+    print_usage ();
+  endif
+
+  sz = size (XX);
+  if (length (sz) ~= 2 || any (sz < 2))
+    error ("ls_absolute_geom is only implemented for 2D");
+  endif
+  if (any (sz ~= size (YY)))
+    error ("sizes mismatch");
+  endif
+
+  xCoords = XX(:);
+  yCoords = YY(:);
+  inpts = geom.ispts.inout(:, 1);
+  geom.ispts.coord = [xCoords(inpts), yCoords(inpts)] + geom.ispts.incoord;
+  geom.nodes = struct ("n", prod (sz), "coord", [xCoords, yCoords]);
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_absolute_geom (1, 2);
+%!error <Invalid call to>
+%!  ls_absolute_geom (1, 2, 3, 4);
+%!error <ls_absolute_geom is only implemented for 2D>
+%!  x = [-1, 1];
+%!  [XX, YY, ZZ] = ndgrid (x);
+%!  ls_absolute_geom (struct (), XX, YY);
+%!error <ls_absolute_geom is only implemented for 2D>
+%!  ls_absolute_geom (struct (), [-1, 0, 1], [0, 0, 0]);
+%!error <sizes mismatch>
+%!  ls_absolute_geom (struct (), zeros (3, 2), zeros (2, 2));
+
+% No real functional test for now.  It is 'tested' in the demo
+% of ls_find_geometry, though.
diff --git a/inst/ls_animate_evolution.m b/inst/ls_animate_evolution.m
new file mode 100644
index 0000000..e0335a0
--- /dev/null
+++ b/inst/ls_animate_evolution.m
@@ -0,0 +1,71 @@
+##  Copyright (C) 2014-2015  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {} ls_animate_evolution (@var{phi}, @var{f}, @var{h}, @var{times}, @var{wait})
+## 
+## Animate the evolution of a level-set geometry.  The evolution
+## is calculated with @code{ls_solve_stationary} and @code{ls_extract_solution}
+## for the given arguments,  and the result plotted in the current figure
+## for the times given in @var{times}.  Between updating to the next
+## ``movie frame'', sleep for @var{wait} seconds.
+##
+## @seealso{ls_solve_stationary, ls_extract_solution}
+## @end deftypefn
+
+function ls_animate_evolution (phi, f, h, times, wait)
+  if (nargin () != 5)
+    print_usage ();
+  endif
+
+  d = ls_solve_stationary (phi, f, h);
+
+  % The figure is cleared and the speed plotted only once,
+  % to prevent ugly "jittering".  At each step, only the
+  % last contour plot object is removed to re-draw it.
+
+  clf ();
+  hold ("on");
+  imagesc (f);
+  ls_sign_colourmap ("highlight");
+  set (gca (), "ydir", "normal");
+
+  handle = false;
+  for t = times
+    phit = ls_extract_solution (t, d, phi, f);
+    if (handle)
+      delete (handle);
+    endif
+    [~, handle] = contour (phit, [0, 0], "k", "LineWidth", 2);
+    axis ("equal");
+    drawnow ();
+    sleep (wait);
+  endfor
+  hold ("off");
+endfunction
+
+% Simple demo with a basic situation.
+%!demo
+%!  n = 100;
+%!  x = linspace (-2, 2, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi = ls_genbasic (XX, YY, "sphere", [0, 0], 1.7);
+%!  f = -XX.^2 - YY.^2;
+%!
+%!  times = linspace (0, 1, 60);
+%!  ls_animate_evolution (phi, f, h, times, 0.05);
diff --git a/inst/ls_build_mesh.m b/inst/ls_build_mesh.m
new file mode 100644
index 0000000..5f55212
--- /dev/null
+++ b/inst/ls_build_mesh.m
@@ -0,0 +1,271 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {[@var{mesh}, @var{vmap}] =} ls_build_mesh (@var{geom}, @var{phi}, @var{glob} = false)
+##
+## Build a triangle mesh for the level-set geometry described by
+## @var{geom} and by the level-set function @var{phi}.  Note that
+## @var{geom} must contain absolute coordinates as set by
+## @code{ls_absolute_geom}.
+##
+## If @var{glob} is set to @code{true}, then the mesh will contain
+## the whole hold-all domain and not just the interior of the level-set
+## geometry.  It will, however, be constructed such that the boundary
+## of the level-set domain is resolved precisely, and such that the
+## level-set domain can be described as a subset of the mesh triangles.
+##
+## The mesh points will be made up @emph{both} of grid nodes as well as
+## intersection points.  While all intersection points will appear
+## as mesh nodes, unless @var{glob} is set, only @strong{inner} grid nodes
+## will correspond to mesh points.  The points on the mesh are numbered
+## differently to the points in @var{geom}, although the indices can be
+## translated both ways using @var{vmap} (see below).
+##
+## On the other hand, the boundary edges of the mesh match precisely the
+## edges given in @code{@var{geom}.bedges}.  They have the same indices
+## in both data structures.
+##
+## The returned struct @var{mesh} follows the format of the @code{msh}
+## package, which means that it contains these fields:
+##
+## @table @code
+## @item p
+## 2 x @var{np} matrix which contains the x- and y-coordinates
+## of the mesh points in its two rows.
+##
+## @item t
+## 4 x @var{nt} matrix describing the mesh triangles.
+## The first three rows contain the indices of mesh points making up the
+## triangle, and the fourth row contains the index of the geometrical
+## surface this triangle belongs to.  It is always set to @code{1} for now.
+## The points are ordered @strong{counter-clockwise} (in the coordinate-system
+## interpretation of the grid).
+##
+## @item e
+## 7 x @var{ne} matrix describing the edges of the mesh.  They
+## correspond precisely to @code{@var{geom}.bedges}.
+## The rows of this matrix are:
+##
+## @table @asis
+## @item 1--2
+## Start- and end-point index.  They are ordered such that
+## the level-set domain is @strong{on the left} of the edge.
+##
+## @item 3--4
+## Always @code{0} for compatibility.
+##
+## @item 5
+## The gamma component index (as per @code{@var{geom}.gamma}) that
+## contains this edge.  Will be in the range 1-- at code{@var{geom}.gamma.n}.
+##
+## @item 6--7
+## Geometrical surface index of the domain parts to the right
+## and left.  They are set to @code{0} and @code{1} for now.
+## @end table
+## @end table
+##
+## The second output, @var{vmap}, describes the mappings between geometrical
+## node / intersection-point indices in @var{geom} and the indices of the
+## mesh points used in @var{mesh}.  It is a struct with these fields:
+##
+## @table @code
+## @item grid
+## Maps the index of a node on the grid to the index of the
+## corresponding mesh point.  The value is @code{NA} if the mesh
+## does not contain the point.
+##
+## @item ispt
+## Maps the index of an intersection point (according to
+## @code{@var{geom}.ispts}) to the index of the corresponding mesh point.
+##
+## @item mesh
+## Maps the index of a mesh point back to either the grid or intersection-point
+## index.  A @strong{positive} value means that the mesh point is on the
+## grid and has the respective index, while a @strong{negative} value means
+## that it is an intersection point with index equal to the absolute value
+## of the entry.
+## @end table
+##
+## @seealso{ls_find_geometry, ls_absolute_geom, msh2m_structured_mesh}
+## @end deftypefn
+
+function [mesh, vmap] = ls_build_mesh (geom, phi, glob = false)
+  if (nargin () < 2 || nargin () > 3)
+    print_usage ();
+  endif
+
+  if (!isfield (geom.ispts, "coord") || !isfield (geom, "nodes"))
+    error ("GEOM misses absolute coordinates, use ls_absolute_geom first");
+  endif
+
+  % Find inside array.
+  L = geom.dim(1);
+  M = geom.dim(2);
+  inside = ls_inside (phi(:));
+
+  % Call internal routine.
+  [mesh, vmap] ...
+    = __levelset_internal_mesh (L, M, inside, ...
+                                geom.nodes.coord, geom.ispts.coord, ...
+                                geom.elem.nodelist, geom.elem.index.inner, ...
+                                geom.elem.index.outer, geom.elem.index.bdry, ...
+                                geom.internal.bdryelSegments, glob);
+  % FIXME: Should be 0 for outside triangles (in global mesh)?
+  mesh.t(4, :) = 1; % Geometrical component.
+
+  % Set mesh vertexMap entries to NA where they are -1.  This is not already
+  % done in the C++ code since it uses integers instead of doubles
+  % for the index arrays being built up.
+  vmap.grid(vmap.grid < 0) = NA;
+  assert (all (vmap.ispt > 0));
+  assert (all (isfinite (vmap.mesh)));
+
+  % Set boundary table from geom.bedges.  We only have to convert
+  % geometrical ispt indices to mesh vertex indices.
+  mesh.e = NA (7, geom.bedges.n);
+  mesh.e(1 : 2, :) = vmap.ispt(geom.bedges.ispts');
+  mesh.e([3:4, 6], :) = 0; % Reserved and outside on the right.
+  mesh.e(5, :) = geom.bedges.comp; % Boundary component.
+  mesh.e(7, :) = 1; % Domain on the left.
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_build_mesh (1)
+%!error <Invalid call to>
+%!  ls_build_mesh (1, 2, 3, 4)
+
+% Test for missing absolute coordinates.
+%!error <GEOM misses absolute coordinates, use ls_absolute_geom first>
+%!  x = linspace (-10, 10, 10);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!  phi = ls_genbasic (XX, YY, "sphere", [0, 0], 5);
+%!  phi = ls_normalise (phi, h);
+%!  geom = ls_find_geometry (phi, h);
+%!  ls_build_mesh (geom, phi);
+
+% Helper function to verify that two points are in counter-clockwise
+% direction with respect to a third one.
+%!function verifyDirection (a, b, centre)
+%!  v1 = [a - centre; 0];
+%!  v2 = [b - centre; 0];
+%!  x = cross (v1, v2);
+%!  assert (x(1 : 2), zeros (2, 1));
+%!  assert (x(3) > 0);
+%!endfunction
+
+% A function to verify certain expected properties / postconditions
+% for a given mesh.
+%!function verifyMesh (geom, phi, glob)
+%!  [mesh, vmap] = ls_build_mesh (geom, phi, glob);
+%!  nPts = prod (geom.dim);
+%!  
+%!  % Verify ordering of triangle points.
+%!  for i = 1 : size (mesh.t, 2)
+%!    verifyDirection (mesh.p(:, mesh.t(1, i)), mesh.p(:, mesh.t(2, i)), ...
+%!                     mesh.p(:, mesh.t(3, i)));
+%!  endfor
+%!
+%!  % Go through all grid points and verify them with the mesh.
+%!  for i = 1 : nPts
+%!    gridInd = vmap.grid(i);
+%!    if (isna (gridInd))
+%!      assert (!glob);
+%!      continue;
+%!    endif
+%!
+%!    assert (vmap.mesh(gridInd), i);
+%!    assert (mesh.p(:, gridInd), geom.nodes.coord(i, :)');
+%!  endfor
+%!
+%!  % Go through all intersection points and verify them.
+%!  for i = 1 : geom.ispts.n
+%!    gridInd = vmap.ispt(i);
+%!    assert (vmap.mesh(gridInd), -i);
+%!    assert (mesh.p(:, gridInd), geom.ispts.coord(i, :)');
+%!  endfor
+%!
+%!  % Verify mesh.e against geom.bedges.
+%!  for i = 1 : geom.bedges.n
+%!    assert (mesh.e(1 : 2, i), vmap.ispt(geom.bedges.ispts(i, :)'));
+%!    assert (mesh.e(5, i), geom.bedges.comp(i));
+%!  endfor
+%!endfunction
+%
+% Verify a given phi, by first constructing the geometry and mesh
+% both in global and local mode as well as trying both phi and -phi.
+%!function verifyPhi (phi, XX, YY, h)
+%!  for s = [-1, 1]
+%!    cur = phi * s;
+%!    cur = ls_normalise (cur, h);
+%!    geom = ls_find_geometry (cur, h);
+%!    geom = ls_absolute_geom (geom, XX, YY);
+%!
+%!    verifyMesh (geom, cur, false);
+%!    verifyMesh (geom, cur, true);
+%!  endfor
+%!endfunction
+
+% Test against some example phis.  We do not use ls_get_tests, since
+% we only want phis where we have a grid, too.
+%!test
+%!  n = 20;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi = ls_union (ls_genbasic (XX, YY, "sphere", [-6, -6], 3), ...
+%!                  ls_genbasic (XX, YY, "sphere", [6, 6], 3));
+%!  verifyPhi (phi, XX, YY, h);
+%!
+%!  phi = ls_setdiff (ls_genbasic (XX, YY, "sphere", [0, 0], 8), ...
+%!                    ls_genbasic (XX, YY, "sphere", [0, 0], 4));
+%!  verifyPhi (phi, XX, YY, h);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demo.
+
+% Build a mesh both in global and non-global mode and plot the result.
+%!demo
+%!  x = linspace (-10, 10, 11);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi = ls_union (ls_genbasic (XX, YY, "sphere", [5, 5], 4.5), ...
+%!                  ls_genbasic (XX, YY, "sphere", [-2, -2], 4));
+%!  phi = ls_normalise (phi, h);
+%!  geom = ls_find_geometry (phi, h);
+%!  geom = ls_absolute_geom (geom, XX, YY);
+%!
+%!  mesh = ls_build_mesh (geom, phi, false);
+%!  meshGlob = ls_build_mesh (geom, phi, true);
+%!
+%!  figure ();
+%!  hold ("on");
+%!  trimesh (meshGlob.t(1 : 3, :)', meshGlob.p(1, :), meshGlob.p(2, :), "r");
+%!  trimesh (mesh.t(1 : 3, :)', mesh.p(1, :), mesh.p(2, :), "g");
+%!  for i = 1 : size (mesh.e, 2)
+%!    plot (mesh.p(1, mesh.e(1 : 2, i)), mesh.p(2, mesh.e(1 : 2, i)), "b");
+%!  endfor
+%!  hold ("off");
+%!  legend ("Global", "Domain", "Boundary");
+%!  axis ("equal");
+%!  axis ("square");
diff --git a/inst/ls_check.m b/inst/ls_check.m
new file mode 100644
index 0000000..1e0db64
--- /dev/null
+++ b/inst/ls_check.m
@@ -0,0 +1,98 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{ok} =} ls_check (@var{phi}, @var{type}, @var{where})
+## 
+## Check a geometric constraint for the level-set function @var{phi}.
+##
+## @var{type} specifies what the constraint
+## should be, and @var{where} should be a logical array of the same size
+## as the grid (and thus @var{phi}), specifying which grid points are
+## part of the set that defines the constraint.
+## Possible values for @var{type}:
+##
+## @table @asis
+## @item @qcode{"inside"}
+## The domain should be inside the region marked as @var{where}.
+##
+## @item @qcode{"outside"}
+## The domain should not intersect the region marked as @var{where}.
+##
+## @item @qcode{"contain"}
+## The domain should always contain the region marked in @var{where}.
+## @end table
+##
+## @seealso{ls_enforce, ls_enforce_speed, ls_inside}
+## @end deftypefn
+
+function ok = ls_check (phi, type, where)
+  if (nargin () ~= 3)
+    print_usage ();
+  endif
+
+  if (~all (size (phi) == size (where)))
+    error ("PHI and WHERE must be of the same size");
+  endif
+  if (~strcmp (typeinfo (type), "string"))
+    error ("TYPE must be a string");
+  endif
+
+  inside = ls_inside (phi);
+  switch (type)
+    case "inside"
+      ok = all (~inside(~where));
+    case "outside"
+      ok = all (~inside(where));
+    case "contain"
+      ok = all (inside(where));
+    otherwise
+      error ("invalid value '%s' for TYPE argument", type);
+  endswitch
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_check (1, 2)
+%!error <Invalid call to>
+%!  ls_check (1, 2, 3, 4)
+%!error <PHI and WHERE must be of the same size>
+%!  ls_check (1, "inside", [1, 2]);
+%!error <invalid value 'foo' for TYPE argument>
+%!  ls_check (1, "foo", true);
+%!error <TYPE must be a string>
+%!  ls_check (1, NA, false);
+
+% Basic tests for the cases.
+%!test
+%!  n = 100;
+%!  x = linspace (-10, 10, n);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  circ2 = (XX.^2 + YY.^2 < 2^2);
+%!  circ8 = (XX.^2 + YY.^2 < 8^2);
+%!  phi = (XX.^2 + YY.^2 - 5^2);
+%!
+%!  assert (ls_check (phi, "inside", circ8));
+%!  assert (ls_check (phi, "contain", circ2));
+%!  assert (ls_check (phi, "outside", ~circ8));
+%!
+%!  assert (~ls_check (phi, "inside", circ2));
+%!  assert (~ls_check (phi, "contain", circ8));
+%!  assert (~ls_check (phi, "outside", circ8));
diff --git a/inst/ls_complement.m b/inst/ls_complement.m
new file mode 100644
index 0000000..b822131
--- /dev/null
+++ b/inst/ls_complement.m
@@ -0,0 +1,66 @@
+##  Copyright (C) 2014-2015  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{phi} =} ls_complement (@var{phi})
+## 
+## Construct a level-set function for the complement of the set
+## described by @var{phi}.
+##
+## This operation preserves a signed distance function:  If @var{phi}
+## is actually the signed distance function of the set, then also
+## the return value will be the signed distance function of the set's
+## complement.
+##
+## @seealso{ls_union, ls_intersect, ls_setdiff, ls_setxor}
+## @end deftypefn
+
+function phi = ls_complement (phi)
+  if (nargin () ~= 1)
+    print_usage ();
+  endif
+
+  phi = -phi;
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_complement ()
+%!error <Invalid call to>
+%!  ls_complement (1, 2)
+
+% Basic tests for the function.
+%!test
+%!  n = 50;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi1 = (XX - 2).^2 + (YY - 2).^2 - 2^2;
+%!  phi2 = (XX + 2).^2 + (YY + 2).^2 - 2^2;
+%!
+%!  assert (ls_disjoint (phi1, ls_complement (phi1)));
+%!  assert (ls_disjoint (phi2, ls_complement (phi2)));
+%!
+%!  assert (~ls_disjoint (phi1, ls_complement (phi2)));
+%!  assert (~ls_disjoint (phi2, ls_complement (phi1)));
+%!
+%!  sd1 = ls_signed_distance (ls_complement (phi1), h);
+%!  sd2 = ls_complement (ls_signed_distance (phi1, h));
+%!  assert (sd1, sd2, sqrt (eps));
diff --git a/inst/ls_disjoint.m b/inst/ls_disjoint.m
new file mode 100644
index 0000000..47f2fe8
--- /dev/null
+++ b/inst/ls_disjoint.m
@@ -0,0 +1,83 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{res} =} ls_disjoint (@var{phi1}, @var{phi2})
+## @deftypefnx  {Function File} {@var{res} =} ls_disjoint (@var{phi}, ...)
+## 
+## Check if all the sets described by the given level-set functions
+## are disjoint.
+##
+## @seealso{ls_inside, ls_equal, is_issubset, ls_check}
+## @end deftypefn
+
+function res = ls_disjoint (varargin)
+  if (length (varargin) < 1)
+    print_usage ();
+  endif
+
+  % The algorithm is like this:  Go over the list of sets once
+  % and keep a "running union" of the sets already processed.  Check
+  % that each new set is disjoint to this union.
+  total = varargin{1};
+  for i = 2 : length (varargin)
+    phi = varargin{i};
+    if (~all (size (total) == size (phi)))
+      error ("size mismatch in the arguments");
+    endif
+
+    common = (ls_inside (phi) & ls_inside (total));
+    if (any (common(:)))
+      res = false;
+      return;
+    endif
+
+    total = ls_union (total, phi);
+  endfor
+
+  % No intersection found.
+  res = true;
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_disjoint ()
+%!error <size mismatch in the arguments>
+%!  ls_disjoint (1, -2, [1, 2])
+
+% Basic test with some constructed examples.
+%!test
+%!  assert (ls_disjoint ([-1, 0, 1], [0, 0, 0], [1, 0, -1]));
+%!  assert (~ls_disjoint ([Inf, -Inf, 1], [-1, -1, 1]));
+%!  assert (~ls_disjoint ([-1, -1, 1], [0, 0, 0], [1, -1, -1]));
+
+% Test in 2D with some circles.
+%!test
+%!  n = 50;
+%!  x = linspace (-10, 10, n);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi1 = (XX - 2).^2 + (YY - 2).^2 - 2^2;
+%!  phi2 = (XX + 2).^2 + (YY + 2).^2 - 2^2;
+%!  phi3 = XX.^2 + YY.^2 - 2^2;
+%!
+%!  assert (ls_disjoint (phi1, phi2));
+%!  assert (~ls_disjoint (phi1, phi3));
+%!  assert (~ls_disjoint (phi2, phi3));
+%!  assert (~ls_disjoint (phi1, phi2, phi3));
diff --git a/inst/ls_distance_fcn.m b/inst/ls_distance_fcn.m
new file mode 100644
index 0000000..8b54435
--- /dev/null
+++ b/inst/ls_distance_fcn.m
@@ -0,0 +1,133 @@
+##  Copyright (C) 2015  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{d} =} ls_distance_fcn (@var{phi}, @var{h} = 1)
+## 
+## Calculate the distance function of a set.  The domain is described by
+## its level-set function, whose values on a rectangular grid with spacing
+## @var{h} are given in the array @var{phi}.
+##
+## In contrast to @code{ls_signed_distance}, which calculates the
+## @emph{signed} distance function, @var{d} will be set to zero
+## on interior points.  This allows @code{ls_distance_fcn} to be
+## faster, as only exterior points are processed.
+##
+## It may be a good idea to use @code{ls_normalise} on the level-set function
+## before using this method, to prevent almost-zero values from underflowing
+## due to the performed calculations.
+##
+## @seealso{fastmarching, ls_signed_distance, ls_normalise}
+## @end deftypefn
+
+function d = ls_distance_fcn (phi, h = 1)
+  if (nargin () < 1 || nargin () > 2)
+    print_usage ();
+  endif
+
+  d = __levelset_internal_init_narrowband (phi, h);
+  d(ls_inside (phi)) = 0;
+
+  f = ones (size (phi));
+  d = fastmarching (d, h * f);
+
+  assert (all (d(:) >= 0));
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_distance_fcn ()
+%!error <Invalid call to>
+%!  ls_distance_fcn (1, 2, 3)
+
+% Check 0D case.
+%!test
+%!  assert (ls_distance_fcn ([]), []);
+
+% Check 1D case, for which the expected result is trivial to calculate
+% and should be almost exact.
+%!test
+%!  n = 10;
+%!  x = linspace (-2, 2, n);
+%!  h = x(2) - x(1);
+%!  phi = abs (x) - 1;
+%!
+%!  d = ls_distance_fcn (phi, h);
+%!  assert (d, max (phi, 0), sqrt (eps));
+
+% Test with circular region in 3D.
+%!test
+%!  n = 50;
+%!  x = linspace (-2, 2, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY, ZZ] = ndgrid (x);
+%!  RRsq = XX.^2 + YY.^2 + ZZ.^2;
+%!  phi = RRsq - 1;
+%!
+%!  d = ls_distance_fcn (phi, h);
+%!  assert (d, max (sqrt (RRsq) - 1, 0), h);
+
+% Compare timing to ls_signed_distance.
+%!test
+%!  n = 500;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!
+%!  [XX, YY] = meshgrid (x, x);
+%!  phi = ls_genbasic (XX, YY, "sphere", [0, 0], 8);
+%!
+%!  id = tic ();
+%!  d = ls_distance_fcn (phi, h);
+%!  time1 = toc (id);
+%!
+%!  id = tic ();
+%!  sd = ls_signed_distance (phi, h);
+%!  time2 = toc (id);
+%!
+%!  assert (d, max (sd, 0), sqrt (eps));
+%!  printf ("ls_distance_fcn faster than ls_signed_distance by %.1f%%\n", ...
+%!          100 * ((time2 - time1) / time2));
+%!  assert (time1 < time2);
+
+% Check that ls_distance_fcn returns the same as ls_signed_distance.
+%!test
+%!  phis = ls_get_tests ();
+%!  for i = 1 : length (phis)
+%!    phi = ls_normalise (phis{i});
+%!    d = ls_distance_fcn (phi);
+%!    sd = ls_signed_distance (phi);
+%!    assert (d, max (sd, 0), sqrt (eps));
+%!  endfor
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+% Calculate distance from an ellipsoid and plot it.
+%!demo
+%!  n = 100;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi = ls_genbasic (XX, YY, "sphere", [0, 0], [8, 5]);
+%!  d = ls_distance_fcn (phi, h);
+%!
+%!  figure ();
+%!  mesh (XX, YY, d);
+%!  view ([45, 45]);
diff --git a/inst/ls_enforce.m b/inst/ls_enforce.m
new file mode 100644
index 0000000..98f71fe
--- /dev/null
+++ b/inst/ls_enforce.m
@@ -0,0 +1,107 @@
+##  Copyright (C) 2014-2015  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{phi} =} ls_enforce (@var{phi}, @var{type}, @var{where})
+## 
+## Enforce a geometric constraint for the level-set function @var{phi}.  It
+## will be updated to satisfy the constraint.
+##
+## @var{type} specifies what the constraint
+## should be, and @var{where} should be a logical array of the same size
+## as the grid (and thus @var{phi}), specifying which grid points are
+## part of the set that defines the constraint.
+## Possible values for @var{type}:
+##
+## @table @asis
+## @item @qcode{"inside"}
+## The domain should be inside the region marked as @var{where}.
+##
+## @item @qcode{"outside"}
+## The domain should not intersect the region marked as @var{where}.
+##
+## @item @qcode{"contain"}
+## The domain should always contain the region marked in @var{where}.
+## @end table
+##
+## @seealso{ls_check, ls_enforce_speed}
+## @end deftypefn
+
+function phi = ls_enforce (phi, type, where)
+  if (nargin () ~= 3)
+    print_usage ();
+  endif
+
+  if (~all (size (phi) == size (where)))
+    error ("PHI and WHERE must be of the same size");
+  endif
+  if (~strcmp (typeinfo (type), "string"))
+    error ("TYPE must be a string");
+  endif
+
+  switch (type)
+    case "inside"
+      phi(~where) = Inf;
+    case "outside"
+      phi(where) = Inf;
+    case "contain"
+      phi(where) = -Inf;
+    otherwise
+      error ("invalid value '%s' for TYPE argument", type);
+  endswitch
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_enforce (1, 2)
+%!error <Invalid call to>
+%!  ls_enforce (1, 2, 3, 4)
+%!error <PHI and WHERE must be of the same size>
+%!  ls_enforce (1, "inside", [1, 2]);
+%!error <invalid value 'foo' for TYPE argument>
+%!  ls_enforce (1, "foo", true);
+%!error <TYPE must be a string>
+%!  ls_enforce (1, NA, false);
+
+% Basic tests for the cases.
+%!test
+%!  n = 100;
+%!  x = linspace (-10, 10, n);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  circ2 = (XX.^2 + YY.^2 < 2^2);
+%!  circ8 = (XX.^2 + YY.^2 < 8^2);
+%!  phi = (XX.^2 + YY.^2 - 5^2);
+%!
+%!  phi2 = ls_enforce (phi, "inside", circ8);
+%!  assert (ls_equal (phi2, phi));
+%!  phi2 = ls_enforce (phi, "inside", circ2);
+%!  assert (ls_check (phi2, "inside", circ2));
+%!
+%!  phi2 = ls_enforce (phi, "contain", circ2);
+%!  assert (ls_equal (phi2, phi));
+%!  phi2 = ls_enforce (phi, "contain", circ8);
+%!  assert (ls_check (phi2, "contain", circ8));
+%!
+%!  phi2 = ls_enforce (phi, "outside", ~circ8);
+%!  assert (ls_equal (phi2, phi));
+%!  phi2 = ls_enforce (phi, "outside", ~circ2);
+%!  ls_check (phi2, "outside", ~circ2);
+%!  phi2 = ls_enforce (phi, "outside", circ2);
+%!  ls_check (phi2, "outside", circ2);
diff --git a/inst/ls_enforce_speed.m b/inst/ls_enforce_speed.m
new file mode 100644
index 0000000..e73de37
--- /dev/null
+++ b/inst/ls_enforce_speed.m
@@ -0,0 +1,148 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{f} =} ls_enforce_speed (@var{f}, @var{type}, @var{where})
+## 
+## Enforce geometric constraints by changing the
+## speed field @var{f} accordingly.  This change ensures that
+## the constraint is not violated while evolving a level-set function
+## with the new speed field.
+##
+## @var{type} specifies what the constraint
+## should be, and @var{where} should be a logical array of the same size
+## as the grid (and thus @var{f}), specifying which grid points are
+## part of the set that defines the constraint.
+## Possible values for @var{type}:
+##
+## @table @asis
+## @item @qcode{"inside"}
+## The domain should be inside the region marked as @var{where}.
+##
+## @item @qcode{"outside"}
+## The domain should not intersect the region marked as @var{where}.
+##
+## @item @qcode{"contain"}
+## The domain should always contain the region marked in @var{where}.
+## @end table
+##
+## @seealso{ls_check, ls_enforce, ls_solve_stationary, ls_extract_solution}
+## @end deftypefn
+
+function f = ls_enforce_speed (f, type, where)
+  if (nargin () ~= 3)
+    print_usage ();
+  endif
+
+  if (~all (size (f) == size (where)))
+    error ("F and WHERE must be of the same size");
+  endif
+  if (~strcmp (typeinfo (type), "string"))
+    error ("TYPE must be a string");
+  endif
+
+  switch (type)
+    case "inside"
+      f(~where & (f > 0)) = 0;
+    case "outside"
+      f(where & (f > 0)) = 0;
+    case "contain"
+      f(where & (f < 0)) = 0;
+    otherwise
+      error ("invalid value '%s' for TYPE argument", type);
+  endswitch
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_enforce_speed (1, 2)
+%!error <Invalid call to>
+%!  ls_enforce_speed (1, 2, 3, 4)
+%!error <F and WHERE must be of the same size>
+%!  ls_enforce_speed (1, "inside", [1, 2]);
+%!error <invalid value 'foo' for TYPE argument>
+%!  ls_enforce_speed (1, "foo", true);
+%!error <TYPE must be a string>
+%!  ls_enforce_speed (1, NA, false);
+
+% For the following test, define a utility function that
+% checks whether a given domain is (approximately) a circle
+% with given radius.
+%!function checkCircleRadius (XX, YY, h, phi, r)
+%!  phiCorrect = XX.^2 + YY.^2 - r^2;
+%!  sd1 = ls_signed_distance (phi, h);
+%!  sd2 = ls_signed_distance (phiCorrect, h);
+%!  assert (sd1, sd2, h);
+%!endfunction
+
+% Shrink and grow a circle, touching constraints.  This checks that the
+% constraints are correctly enforced and also that non-applicable constraints
+% do not interfere with the speed.
+%!test
+%!  n = 100;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi = XX.^2 + YY.^2 - 3^2;
+%!  F = ones (size (XX));
+%!  whereContain = (XX.^2 + YY.^2 < 5^2);
+%!  F = ls_enforce_speed (F, "contain", whereContain);
+%!  whereInside = (XX.^2 + YY.^2 < 8^2);
+%!  F = ls_enforce_speed (F, "inside", whereInside);
+%!  dists = ls_solve_stationary (phi, F, h);
+%!  phi2 = ls_extract_solution (10, dists, phi, F);
+%!  assert (ls_check (phi2, "contain", whereContain));
+%!  assert (ls_check (phi2, "inside", whereInside));
+%!  checkCircleRadius (XX, YY, h, phi2, 8);
+%!
+%!  phi = XX.^2 + YY.^2 - 8^2;
+%!  F = -ones (size (XX));
+%!  whereContain = (XX.^2 + YY.^2 < 5^2);
+%!  F = ls_enforce_speed (F, "contain", whereContain);
+%!  whereOutside = (XX.^2 + YY.^2 >= 7^2);
+%!  F = ls_enforce_speed (F, "outside", whereOutside);
+%!  dists = ls_solve_stationary (phi, F, h);
+%!  phi2 = ls_extract_solution (10, dists, phi, F);
+%!  assert (ls_check (phi2, "contain", whereContain));
+%!  assert (ls_check (phi2, "outside", whereOutside));
+%!  checkCircleRadius (XX, YY, h, phi2, 5);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+% Shrink and grow a circle.
+%!demo
+%!  n = 100;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi = XX.^2 + YY.^2 - 8^2;
+%!  F = -ones (size (XX));
+%!  F = ls_enforce_speed (F, "outside", XX.^2 + YY.^2 >= 7^2);
+%!  F = ls_enforce_speed (F, "contain", XX.^2 + YY.^2 < 3^2);
+%!  times = linspace (1, 6, 40);
+%!  ls_animate_evolution (phi, F, h, times, 0.05);
+%!
+%!  phi = XX.^2 + YY.^2 - 3^2;
+%!  F = ones (size (XX));
+%!  F = ls_enforce_speed (F, "inside", XX.^2 + YY.^2 < 8^2);
+%!  F = ls_enforce_speed (F, "contain", XX.^2 + YY.^2 < 5^2);
+%!  ls_animate_evolution (phi, F, h, times, 0.05);
diff --git a/inst/ls_equal.m b/inst/ls_equal.m
new file mode 100644
index 0000000..d157c73
--- /dev/null
+++ b/inst/ls_equal.m
@@ -0,0 +1,67 @@
+##  Copyright (C) 2014-2015  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{res} =} ls_equal (@var{phi1}, @var{phi2})
+## 
+## Check if the sets described by @var{phi1} and @var{phi2} are equal.
+## This compares the domains according to @code{ls_inside}, but does not
+## interpret magnitudes of the values and thus it does not take into
+## account where exactly the ``approximate boundary'' intersects
+## @emph{between} grid points.
+##
+## @seealso{ls_inside, ls_issubset, ls_disjoint, ls_hausdorff_dist}
+## @end deftypefn
+
+function res = ls_equal (phi1, phi2)
+  if (nargin () ~= 2)
+    print_usage ();
+  endif
+
+  if (~all (size (phi1) == size (phi2)))
+    error ("PHI1 and PHI2 must be of the same size");
+  endif
+
+  ins1 = ls_inside (phi1);
+  ins2 = ls_inside (phi2);
+  res = all (ins1(:) == ins2(:));
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_equal (1)
+%!error <Invalid call to>
+%!  ls_equal (1, 2, 3)
+%!error <PHI1 and PHI2 must be of the same size>
+%!  ls_equal (1, [1, 2])
+
+% Basic test.
+%!test
+%!  assert (ls_equal ([Inf, 0, -2, -Inf, eps], ...
+%!                    [1, 0, -Inf, -eps, 1]));
+%!  assert (ls_equal ([1, 0; -1, 0], [Inf, 0; -5, 0]));
+%!  assert (!ls_equal ([1, -1; 0, 0], [1, 0; 0, 0]));
+
+% Test involving signed zero.
+%!test
+%!  if (exist ("signbit") == 5)
+%!    assert (ls_equal ([0, -0], [1, -1]));
+%!  else
+%!    warning ("'signbit' function not available, skipping test.");
+%!  endif
diff --git a/inst/ls_extract_solution.m b/inst/ls_extract_solution.m
new file mode 100644
index 0000000..946f575
--- /dev/null
+++ b/inst/ls_extract_solution.m
@@ -0,0 +1,174 @@
+##  Copyright (C) 2014-2015  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{phi} =} ls_extract_solution (@var{t}, @var{d}, @var{phi0}, @var{f})
+## 
+## Calculate a level-set function of an evolving geometry from the
+## result of @code{ls_solve_stationary}.  In particular, it is assumed
+## that @code{@var{d} = ls_solve_stationary (@var{phi0}, @var{f}, @var{h})}.
+## @var{phi} is set to a level-set function for the evolved geometry at
+## time @var{t}.  The zero level-set of @var{phi} will describe the same
+## geometry as the solution of the level-set equation
+## @tex
+## \begin{equation*}
+##   \phi_t + f \left| \nabla \phi \right| = 0,
+## \end{equation*}
+## @end tex
+## @ifnottex
+##
+## @example
+## d/dt phi + f | grad phi | = 0,
+## @end example
+##
+## @end ifnottex
+## although @var{phi} will not be the full solution to this equation.
+##
+## @seealso{ls_solve_stationary, ls_time_step, ls_animate_evolution}
+## @end deftypefn
+
+function phi = ls_extract_solution (t, d, phi0, f)
+  if (nargin () ~= 4)
+    print_usage ();
+  endif
+
+  sz = size (phi0);
+  if (~all (sz == size (f)))
+    error ("PHI0 and F must be of the same size");
+  endif
+  if (~all (sz == size (d)))
+    error ("PHI0 and D must be of the same size");
+  endif
+
+  fp = (f > 0);
+  fz = (f == 0);
+  fn = (f < 0);
+
+  phi = NA (size (d));
+  phi(fp) = d(fp) - t;
+  phi(fn) = d(fn) + t;
+  phi(fz) = phi0(fz);
+
+  assert (all (isfinite (phi(:)) | isinf (phi(:))));
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_extract_solution (1, 2, 3)
+%!error <Invalid call to>
+%!  ls_extract_solution (1, 2, 3, 4, 5)
+%!error <PHI0 and F must be of the same size>
+%!  ls_extract_solution (1, [1, 2], [1, 2], [3; 4]);
+%!error <PHI0 and D must be of the same size>
+%!  ls_extract_solution (1, [1; 2], [3, 4], [3, 4]);
+
+% Check 0D case.
+%!test
+%!  assert (ls_extract_solution (1, [], [], []), []);
+
+% For the test phi's, just check that solving works
+% without any errors.
+%!test
+%!  ts = [0, 1, 2, 3];
+%!  phis = ls_get_tests ();
+%!  for i = 1 : length (phis)
+%!    f = ones (size (phis{i}));
+%!    for j = -1 : 1
+%!      curF = j * f;
+%!      d = ls_solve_stationary (phis{i}, curF);
+%!      for t = ts
+%!        phit = ls_extract_solution (t, d, phis{i}, curF);
+%!        assert (phit(curF == 0), phis{i}(curF == 0));
+%!      endfor
+%!    endfor
+%!  endfor
+
+% Check vanishing of domain.
+%!test
+%!  n = 100;
+%!  x = linspace (-1.5, 1.5, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi = ls_genbasic (XX, YY, "sphere", [0, 0], 1);
+%!  f = -ones (size (XX));
+%!
+%!  d = ls_solve_stationary (phi, f, h);
+%!  phiNew = ls_extract_solution (2, d, phi, f);
+%!  assert (ls_isempty (phiNew));
+
+% Test that the solution is correct for an expanding sphere.
+%!test
+%!  n = 50;
+%!  x = linspace (-3, 3, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY, ZZ] = ndgrid (x);
+%!
+%!  phi0 = ls_genbasic (XX, YY, ZZ, "sphere", [0, 0, 0], 1);
+%!  phit = ls_genbasic (XX, YY, ZZ, "sphere", [0, 0, 0], 2);
+%!  f = ones (size (phi0));
+%!
+%!  d = ls_solve_stationary (phi0, f, h);
+%!  phi = ls_extract_solution (1, d, phi0, f);
+%!
+%!  sd1 = ls_signed_distance (phi, h);
+%!  sd2 = ls_signed_distance (phit, h);
+%!  diff = norm (sd1(:) - sd2(:), Inf);
+%!  assert (diff, 0, h);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+% Visualise refraction and diffraction of a wave front.
+%
+%!demo
+%!  n = 100;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi = ls_genbasic (XX, YY, "half", [5, -10], [1, -1]);
+%!  f = sign (XX) + 2;
+%!
+%!  times = linspace (1, 9, 80);
+%!  ls_animate_evolution (phi, f, h, times, 0.05);
+%
+%!demo
+%!  n = 101;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi = ls_genbasic (XX, YY, "half", [-9, 0], [-1, 0]);
+%!  f = ones (size (phi));
+%!  f(XX == 0 & abs (YY) > 2) = 0;
+%!
+%!  times = linspace (1, 19, 80);
+%!  ls_animate_evolution (phi, f, h, times, 0.05);
+
+% Visualise evolution for more complicated case of positive and negative
+%!demo
+%!  n = 1000;
+%!  x = linspace (-5, 5, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  F = sin (XX .* YY);
+%!  phi0 = ls_genbasic (XX, YY, "sphere", [0, 0], 3);
+%!  ls_animate_evolution (phi0, F, h, linspace (0, 3, 40), 0.01);
diff --git a/inst/ls_find_geometry.m b/inst/ls_find_geometry.m
new file mode 100644
index 0000000..a66075b
--- /dev/null
+++ b/inst/ls_find_geometry.m
@@ -0,0 +1,572 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{geom} =} ls_find_geometry (@var{phi}, @var{h} = 1, @var{fractol} = 1e-3)
+## 
+## Interpret the level-set function @var{phi} and extract geometrical
+## properties.  A rectangular grid with spacing @var{h} uniform in each
+## dimension is assumed.  This function assumes the 2D situation.
+##
+## The function assumes that @var{phi} does not contain values which are
+## exact zeros.  It is a good idea to use @code{ls_normalise} before
+## calling @code{ls_find_geometry}.
+## Intersection points with edges very
+## close to a grid point may be pushed away from it, governed
+## by @var{fractol}.
+##
+## The following terms will be used to denote certain elements
+## of the geometrical description returned by this function:
+##
+## @table @dfn
+## @item node
+## A point on the grid.  @var{phi} is the value of the level-set function
+## at each node.  They are numbered from 1 to @var{l} x @var{m} in the
+## same way as the entries in the matrix @var{phi} are numbered.
+##
+## @item element
+## One rectangular cell of the grid.  Since we work in 2D,
+## each element is made up
+## of four nodes (which are the vertices of the rectangle).  If the grid
+## has @var{l} x @var{m} nodes, then it contains (@var{l} - 1) x (@var{m} - 1)
+## elements.
+##
+## @item inner element
+## An element for which each node is inside the described domain.
+##
+## @item outer element
+## An element for which each node is outside of the described domain.
+##
+## @item boundary element
+## An element which has both inside and outside nodes.  These elements are
+## intersected by the boundary of the domain and have intersection points
+## on some of their edges.
+##
+## It can be the case that it has two intersection
+## points (when one boundary edge intersects it) or four (if we have
+## a ``narrow pair'').  The latter case is that of @emph{two} intersecting
+## (parallel) boundary edges, which happens when diagonally opposing nodes
+## of the element have the same sign of @var{phi}.
+##
+## A narrow pair is ambiguous as to which intersection points should be
+## connected together.  This function is implemented such that it considers
+## the ``narrow channel'' between the parallel boundary edges to be
+## @strong{outside} of the domain.  In other words, at a narrow pair, the
+## domain is considered to be disconnected instead of connected with a narrow
+## channel.
+##
+## @item intersection point
+## A point on the @emph{edge} of a boundary element where the boundary of the
+## described domain (approximately) intersects the grid (i. e., @var{phi}
+## has a zero).  It can be characterised by a pair of inside/outside
+## nodes at each end of the edge it intersects and by the fraction of the
+## side that lies inside the domain.
+##
+## @item gamma
+## The boundary of the described domain.  I. e., the curve given by
+## the zero level-set of @var{phi}.  It may consist of several components,
+## which are each a closed curve.
+##
+## @item boundary edge
+## A single edge of the domain's boundary, which is a line connecting
+## two intersection points and cutting through a boundary element.
+## It is ordered in such a way that the domain is on the left, if the
+## grid is interpreted as coordinate system.  (On the right in matrix
+## ordering.)
+## @end table
+##
+## The returned variable @var{geom} will be a struct with these
+## fields:
+##
+## @table @code
+## @item dim
+## The dimension of the grid, equal to @code{size (@var{phi})}.
+##
+## @item elem
+## Itself a struct describing the elements of the grid.  It contains:
+##
+## @table @code
+## @item n
+## Number of elements on the grid.  Equal to (@var{l} - 1) x (@var{m} - 1)
+## if @var{phi} is @var{l} x @var{m}.
+##
+## @item nodelist
+## @code{n} x 4 matrix containing in its rows the indices of nodes
+## making up each element.  According to the ordering in the matrix @var{phi},
+## the nodes are stored in the order SE, SW, NW, NE.  Interpreted in a
+## (x, y) grid constructed via @code{meshgrid}, the order is instead
+## NE, NW, SW, SE matching the usual numbering of quadrants in the plane.
+## (This is the case because the y axis points down when numbering rows
+## of a matrix, but up in the usual coordinate system.)
+##
+## @item type
+## A vector giving the type of each element.  It is -1, 0 and 1
+## for inner, boundary and outer elements, respectively.  (This convention
+## mimics the sign of @var{phi} somewhat.)
+##
+## @item index
+## Itself a struct with the fields @code{inner}, @code{bdry}
+## and @code{outer}, containing a list of indices for inner, boundary
+## and outer elements.
+##
+## These equal the result of @code{find (@var{geom}.elem.type == @var{x})}
+## with @var{x} being -1, 0 and 1.
+## @end table
+##
+## @item bdryel
+## A struct with information about the boundary elements.  They have their
+## own index here, which can be mapped to the global element index
+## via @code{@var{geom}.elem.index.bdry}.  Its fields are:
+##
+## @table @code
+## @item n
+## Number of boundary elements.
+##
+## @item edges
+## @code{n} x 4 matrix, whose columns correspond to the four edges of
+## the boundary elements.  For each edge where an intersection point lies,
+## the respective matrix entry is set to the intersection point's index
+## into @code{@var{geom}.ispts}.
+## If there is no intersection point, the value is @code{NA}.
+##
+## The edges are ordered similarly to the order in
+## @code{@var{geom}.elem.nodelist}.  In matrix interpretation, the
+## edges are in the columns as S, W, N, E.  Interpreted as a coordinate
+## grid, the order is N, W, S, E.
+## @end table
+##
+## @item ispts
+## Struct containing details about the intersection points with the fields:
+##
+## @table @code
+## @item n
+## Number of intersection points.
+##
+## @item inout
+## @code{n} x 2 matrix containing the node indices of the inner and
+## outer nodes neighbouring each intersection point (in this order).
+##
+## @item frac
+## Fraction of the edge that is inside the domain (connecting
+## the intersection point to the inner neighbour node).  This value
+## is always in the range 0--1, independent of @var{h}.
+##
+## @item incoord
+## Coordinates of the intersection points relative to the neighbouring
+## inner nodes as a @code{n} x 2 matrix.  This takes @var{h} into account.
+##
+## @item onedge
+## @code{n} x 2 x 3 array that indicates on which edges of which
+## boundary elements this intersection point occurs.  The first dimension
+## is indexed by intersection point number, the second dimension numbers
+## the (two) boundary elements each intersection point is part of, and
+## the third dimension contains the tuple (boundary element index, edge index,
+## boundary edge index) in the ranges 1-- at code{@var{geom}.bdryel.n}, 1--4
+## and 1-- at code{@var{geom}.bedges.n}.
+##
+## The occurances (second dimension) are ordered such that the first
+## one is where the boundary edge ``enters'' the boundary element,
+## while the second one is where it ``leaves'' a boundary element.
+##
+## @item gammachain
+## @code{n} x 2 matrix that contains for each intersection point
+## the next and previous intersection point indices when
+## following gamma.  See also @code{@var{geom}.gamma.ispts}.
+## @end table
+##
+## @item gamma
+## Struct with information about the components of the domain's boundary.
+## It contains these fields:
+##
+## @table @code
+## @item n
+## Number of gamma components.
+##
+## @item ispts
+## Cell-array with @code{n} elements.  Each element is a list of indices
+## of intersection points (i. e., indices into @code{@var{geom}.ispts}).
+## In the order given, they can be joined together to form a closed curve
+## (when also the last one is connected back to the first).
+## @end table
+##
+## @item bedges
+## Information about the boundary edges:
+##
+## @table @code
+## @item n
+## Number of boundary edges in total.
+##
+## @item comp
+## A vector giving the gamma component this edge is part of.  The
+## components are in the range 1-- at code{@var{geom}.gamma.n}.
+##
+## @item ispts
+## @code{n} x 2 matrix containing the intersection point indices that
+## make up each boundary edge.
+## @end table
+##
+## @item internal
+## Data field with additional information for internal use, in particular
+## in @code{ls_build_mesh}.  The precise format may be subject to change
+## and should not be used.
+## @end table
+##
+## @seealso{ls_absolute_geom, ls_normalise, ls_build_mesh, ls_inside}
+## @end deftypefn
+
+% Currently contained information in "internal":
+%
+% bdryelSegments:
+%   Information about the (one or two) "segments" of each boundary element
+%   that belong to the domain.  This is the polygon that will be divided
+%   into triangles during mesh building, and it is a by-product of
+%   locating the gamma components.  Thus it makes sense to collect the
+%   information already here instead of redoing everything later.
+%
+%   It is a cell-array indexed by boundary element number.  Each entry
+%   is itself a cell-array with one or two struct entries (for each single
+%   segment).  These structs contain the fields:
+%     startEdge: edge index (1--4) where the startPt intersects.
+%     endEdge: edge index (1--4) where the endPt lies.
+%     startPt: ispt index of the "start" of the intersecting edge.
+%     endPt: ispt index of the "end" point.
+%     inners: Node indices of inner points "enclosed" (one to three).
+%   The corresponding gamma component piece will be [startPt, endPt],
+%   and when using [startPt, inners, endPt, startPt], one gets an enumeration
+%   of the polygon's vertices *in clockwise order* (with coordinate grid
+%   interpretation of the matrices).
+
+% Additional information that could be added as needed:
+% - more about nodes (elements that contain a node, neighbours)
+% - connected components of the domain / exterior
+
+function geom = ls_find_geometry (phi, h = 1, fractol = 1e-3)
+  if (nargin () < 1 || nargin () > 3)
+    print_usage ();
+  endif
+
+  sz = size (phi);
+  if (length (sz) ~= 2 || any (sz < 2))
+    error ("ls_find_geometry is only implemented for 2D");
+  endif
+
+  if (!all (abs (sign (phi(:))) == 1))
+    error ("PHI is not normalised and contains exact zeros or NaN's");
+  endif
+
+  % Use internal routines.
+  elem = __levelset_geomElements (phi);
+  [ispts, edges] = __levelset_geomBoundary (phi, h, fractol, ...
+                                            elem.index.bdry, elem.nodelist);
+  bdryel = struct ("n", length (elem.index.bdry), "edges", edges);
+  [gammaComp, ispts.onedge, bdryelSeg] ...
+    = __levelset_geomGamma (phi, elem.nodelist, elem.index.bdry, ...
+                            edges, ispts.inout);
+  gamma = struct ("n", length (gammaComp), "ispts", {gammaComp});
+  internal = struct ("bdryelSegments", {bdryelSeg});
+
+  % Build the bedges struct and ispts.gammachain from gammaComp.
+  bedges = struct ("n", 0, "comp", [], "ispts", []);
+  ispts.gammachain = NA (ispts.n, 2);
+  for i = 1 : gamma.n
+    comp = gamma.ispts{i};
+    nCurPts = length (gamma.ispts{i});
+    comp(end + 1) = comp(1);
+
+    bedges.n += nCurPts;
+    bedges.comp = [bedges.comp, repmat(i, 1, nCurPts)];
+
+    block = [comp(1 : end - 1), comp(2 : end)]; 
+    assert (size (block), [nCurPts, 2]);
+    bedges.ispts = [bedges.ispts; block];
+
+    ispts.gammachain(comp(1 : end - 1), 1) = comp(2 : end);
+    ispts.gammachain(comp(2 : end), 2) = comp(1 : end - 1);
+  endfor
+  assert (length (bedges.comp), bedges.n);
+  assert (size (bedges.ispts), [bedges.n, 2]);
+  assert (all (!isna (ispts.gammachain)));
+
+  % Fill in onedge(:, :, 3) from bedges.
+  ispts.onedge(bedges.ispts(:, 1), 1, 3) = 1 : bedges.n;
+  ispts.onedge(bedges.ispts(:, 2), 2, 3) = 1 : bedges.n;
+  assert (all (!isna (ispts.onedge(:))));
+
+  % Construct result.
+  geom = struct ("dim", sz, "elem", elem, ...
+                 "bdryel", bdryel, "ispts", ispts, ...
+                 "gamma", gamma, "bedges", bedges, ...
+                 "internal", internal);
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_find_geometry ()
+%!error <Invalid call to>
+%!  ls_find_geometry (1, 2, 3, 4)
+%!error <ls_find_geometry is only implemented for 2D>
+%!  ls_find_geometry (resize (zeros (8, 1), [2, 2, 2]))
+%!error <ls_find_geometry is only implemented for 2D>
+%!  ls_find_geometry ([1, -1, 1])
+%!error <PHI is not normalised>
+%!  ls_find_geometry ([0, 1; -1, -1])
+%!error <PHI is not normalised>
+%!  ls_find_geometry ([-1, NA; -1, -1])
+
+% A function to verify certain expected properties / postconditions
+% for the resulting geometry structure.
+%!function verifyGeometry (phi, geom)
+%!  sz = size (phi);
+%!  nNode = prod (sz);
+%!  L = sz(1);
+%!  M = sz(2);
+%!  nElem = (L - 1) * (M - 1);
+%!  assert (geom.dim, sz);
+%!
+%!  % Basic properties of the elem field.
+%!  assert (geom.elem.n, nElem);
+%!  assert (size (geom.elem.nodelist), [nElem, 4]);
+%!  assert (all (geom.elem.type(geom.elem.index.inner) == -1));
+%!  assert (all ((geom.elem.type(geom.elem.index.bdry) == 0)));
+%!  assert (all ((geom.elem.type(geom.elem.index.outer) == 1)));
+%!  % Order of the nodes in nodelist.
+%!  assert (geom.elem.nodelist(1, :), [L+2, 2, 1, L+1]);
+%!  % Correct classification of inner/outer/bdry elements.
+%!  assert (all (phi(geom.elem.nodelist(geom.elem.index.inner, :))(:) < 0));
+%!  assert (all (phi(geom.elem.nodelist(geom.elem.index.outer, :))(:) > 0));
+%!  bdryPhis = phi(geom.elem.nodelist(geom.elem.index.bdry, :));
+%!  bdryPhis = sort (bdryPhis, 2);
+%!  assert (all (bdryPhis(:, 1) < 0) && all (bdryPhis(:, end) > 0));
+%!
+%!  % Each boundary element should have two or four intersected edges.
+%!  % Also verify that the start/endEdge fields of the internal information
+%!  % match up to the points in question.
+%!  for i = 1 : geom.bdryel.n
+%!    empty = length (find (isna (geom.bdryel.edges(i, :))));
+%!    assert (empty == 2 || empty == 0);
+%!    for j = 1 : length (geom.internal.bdryelSegments{i})
+%!      s = geom.internal.bdryelSegments{i}{j};
+%!      assert (geom.bdryel.edges(i, s.startEdge) == s.startPt);
+%!      assert (geom.bdryel.edges(i, s.endEdge) == s.endPt);
+%!    endfor
+%!  endfor
+%!
+%!  % Run through gamma components and verify that geom.ispts.onedge matches.
+%!  for i = 1 : geom.gamma.n
+%!    c = geom.gamma.ispts{i};
+%!    c(end + 1) = c(1);
+%!    for j = 1 : length (c) - 1
+%!      cur = c(j);
+%!      next = c(j + 1);
+%!      bdryel = geom.ispts.onedge(cur, 1, 1);
+%!      assert (geom.ispts.onedge(next, 2, 1), bdryel);
+%!      assert (geom.bdryel.edges(bdryel, geom.ispts.onedge(cur, 1, 2)), cur);
+%!      assert (geom.bdryel.edges(bdryel, geom.ispts.onedge(next, 2, 2)), next);
+%!      
+%!      edgeForward = geom.ispts.onedge(cur, 1, 3);
+%!      assert (geom.bedges.ispts(edgeForward, 1), cur);
+%!      assert (geom.bedges.ispts(edgeForward, 2), next);
+%!
+%!      assert (geom.ispts.gammachain(cur, 1), next);
+%!      assert (geom.ispts.gammachain(next, 2), cur);
+%!    endfor
+%!  endfor
+%!
+%!  % Verify that the ordering of points in bedges matches the onedge
+%!  % information about start / end points in each boundary element.
+%!  for i = 1 : geom.bedges.n
+%!    assert (geom.ispts.onedge(geom.bedges.ispts(i, 1), 1, 1), ...
+%!            geom.ispts.onedge(geom.bedges.ispts(i, 2), 2, 1));
+%!    assert (geom.ispts.onedge(geom.bedges.ispts(i, 1), 1, 3), i);
+%!    assert (geom.ispts.onedge(geom.bedges.ispts(i, 2), 2, 3), i);
+%!  endfor
+%!endfunction
+
+% Test the postconditions against all test phis.
+%!test
+%!  phis = ls_get_tests ();
+%!  hs = [0.1, 0.5, 1, 1.5];
+%!  for i = 1 : length (phis)
+%!    for h = hs
+%!      for s = [-1, 1]
+%!        cur = phis{i} * s;
+%!        phi = ls_normalise (cur, h);
+%!        geom = ls_find_geometry (phi, h);
+%!        verifyGeometry (phi, geom);
+%!      endfor
+%!    endfor
+%!  endfor
+
+% Test pushing away of intersection points from nodes and the
+% fraction calculation in general.  The constructed example
+% is a narrow-pair with four intersection points.
+%!test
+%!  phi = ones (4, 4);
+%!  phi(2 : 3, 2 : 3) = [Inf, -1; -Inf, 2];
+%!  g = ls_find_geometry (phi, 0.1, 0.01);
+%!
+%!  ind = find (g.elem.index.bdry == 5);
+%!  assert (size (ind), [1, 1]);
+%!  edges = g.bdryel.edges(ind, :);
+%!  assert (all (~isna (edges)));
+%!
+%!  assert (g.ispts.inout(edges, :), [7, 11; 7, 6; 10, 6; 10, 11]);
+%!  assert (g.ispts.frac(edges), [0.99; 0.5; 0.01; 1/3], sqrt (eps));
+%!  assert (g.ispts.incoord(edges, :), ...
+%!          [0.099, 0; 0, -0.05; -0.001, 0; 0, 1/30], sqrt (eps));
+
+% Test that cutting through the hold-all domain is correctly identified.
+%!error <boundary cuts through hold-all domain>
+%!  phi = [-1, -1; 1, 1];
+%!  ls_find_geometry (phi);
+
+% Very simple test for gamma components.
+%!test
+%!  phi = ones (3, 3);
+%!  phi(2, 2) = -1;
+%!  geom = ls_find_geometry (phi);
+%!
+%!  % Permute the component array such that it starts with the north point
+%!  % and then compare coordinates.  Note that the "north" point is "south"
+%!  % in matrix interpretation and thus has "outindex" of 6.
+%!  assert (geom.gamma.n, 1);
+%!  c = geom.gamma.ispts{1}';
+%!  while (geom.ispts.inout(c(1), 2) != 6)
+%!    c = [c(2 : end), c(1)];
+%!  endwhile
+%!  assert (geom.ispts.incoord(c, :), ...
+%!          [0, 1/2; -1/2, 0; 0, -1/2; 1/2, 0], sqrt (eps));
+
+% Helper function to check that the given coordinates all have the
+% given direction with respect to the origin.  +1 means counter-clockwise
+% and -1 is clockwise.
+%!function checkBoundaryDirection (pts, dir)
+%!  pts(end + 1, :) = pts(1, :);
+%!  for i = 1 : length (pts) - 1
+%!    a = [pts(i, :), 0];
+%!    b = [pts(i + 1, :), 0];
+%!    z = cross (a, b);
+%!    assert (z(1 : 2), [0, 0]);
+%!    assert (sign (z(3)), dir);
+%!  endfor
+%!endfunction
+
+% Check that both "outside" and "inside" boundaries get the correct
+% ordering in gamma components.  The test case here is a ring, where we
+% identify the larger (in terms of count of ispts) gamma component with
+% the outer and smaller component with the inner boundary.  We expect
+% that the coordiantes of the outer and inner intersection points
+% have (counter-)clockwise ordering with respect to the origin, respectively.
+%!test
+%!  n = 50;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi = ls_setdiff (ls_genbasic (XX, YY, "sphere", [0, 0], 8), ...
+%!                    ls_genbasic (XX, YY, "sphere", [0, 0], 4));
+%!  phi = ls_normalise (phi, h);
+%!  geom = ls_find_geometry (phi, h);
+%!  geom = ls_absolute_geom (geom, XX, YY);
+%!
+%!  assert (geom.gamma.n, 2);
+%!  len1 = length (geom.gamma.ispts{1});
+%!  len2 = length (geom.gamma.ispts{2});
+%!  if (len1 < len2)
+%!    outer = geom.gamma.ispts{2};
+%!    inner = geom.gamma.ispts{1};
+%!  else
+%!    assert (len1 > len2);
+%!    outer = geom.gamma.ispts{1};
+%!    inner = geom.gamma.ispts{2};
+%!  endif
+%!
+%!  checkBoundaryDirection (geom.ispts.coord(outer, :), 1);
+%!  checkBoundaryDirection (geom.ispts.coord(inner, :), -1);
+
+% Test that narrow bands are interpreted in the documented way.
+%!test
+%!  phi = ones (4, 4);
+%!  phi(2, 2) = -1;
+%!  phi(3, 3) = -1;
+%!
+%!  geom = ls_find_geometry (phi);
+%!  assert (geom.gamma.n, 2);
+%!  geom = ls_find_geometry (-phi);
+%!  assert (geom.gamma.n, 1);
+%!
+%!  % Now also try with other "kind" of narrow pair.
+%!  phi = phi(:, end : -1 : 1);
+%!  geom = ls_find_geometry (phi);
+%!  assert (geom.gamma.n, 2);
+%!  geom = ls_find_geometry (-phi);
+%!  assert (geom.gamma.n, 1);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demo.
+
+% Plot a grid, colour the cells according to the element type and
+% mark the intersection points.
+%!demo
+%!  n = 15;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  r1 = sqrt (2) * 2.9;
+%!  r2 = 3;
+%!  phi = ls_union (ls_genbasic (XX, YY, "sphere", [-3, -3], r1), ...
+%!                  ls_genbasic (XX, YY, "sphere", [3, 3], r1), ...
+%!                  ls_genbasic (XX, YY, "sphere", [-5, 5], r2), ...
+%!                  ls_genbasic (XX, YY, "sphere", [5, -5], r2));
+%!
+%!  phi = ls_normalise (phi, h);
+%!  g = ls_find_geometry (phi, h, 0.2);
+%!  g = ls_absolute_geom (g, XX, YY);
+%!
+%!  figure ();
+%!  hold ("on");
+%!
+%!  for i = 1 : size (g.elem.nodelist, 1)
+%!    nodes = g.elem.nodelist(i, :);
+%!    switch (g.elem.type(i))
+%!      case -1
+%!        colour = [0.5, 0.5, 1];
+%!      case 0
+%!        colour = [1, 0.5, 0.5];
+%!      case 1
+%!        colour = [0.8, 0.8, 0.8];
+%!    endswitch
+%!    patch (XX(nodes), YY(nodes), colour);
+%!  endfor
+%!
+%!  plot (g.ispts.coord(:, 1), g.ispts.coord(:, 2), "k.", "MarkerSize", 8);
+%!  for i = 1 : g.gamma.n
+%!    pts = g.gamma.ispts{i};
+%!    pts(end + 1) = pts(1);
+%!    plot (g.ispts.coord(pts, 1), g.ispts.coord(pts, 2), "k-", "LineWidth", 2);
+%!  endfor
+%!
+%!  contour (XX, YY, phi, [0, 0], "g");
+%!
+%!  axis ([min(x), max(x), min(x), max(x)]);
+%!  axis ("equal");
+%!  grid ("on");
+%!  set (gca (), "xtick", x, "ytick", x, "xticklabel", "", "yticklabel", "");
+%!  hold ("off");
diff --git a/inst/ls_genbasic.m b/inst/ls_genbasic.m
new file mode 100644
index 0000000..a7fa628
--- /dev/null
+++ b/inst/ls_genbasic.m
@@ -0,0 +1,309 @@
+##  Copyright (C) 2014-2015  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{phi} =} ls_genbasic (@var{XX}, @var{YY}, @var{shape}, ...)
+## @deftypefnx {Function File} {@var{phi} =} ls_genbasic (@var{XX}, ..., @var{shape}, ...)
+## @deftypefnx {Function File} {@var{phi} =} ls_genbasic (..., @qcode{"sphere"}, @var{c}, @var{r})
+## @deftypefnx {Function File} {@var{phi} =} ls_genbasic (..., @qcode{"box"}, @var{a}, @var{b})
+## @deftypefnx {Function File} {@var{phi} =} ls_genbasic (..., @qcode{"half"}, @var{p}, @var{n})
+## 
+## Construct the level-set function for a basic geometric shape.  The arguments
+## @var{XX} and @var{YY} should be the coordinates where the level-set function
+## should be evaluated.  For a case that is not 2D, the respective
+## number of arguments must be given.
+##
+## @var{shape} specifies what shape should be constructed, the arguments
+## following are shape-dependent.
+## Possible shapes:
+##
+## @table @asis
+## @item @qcode{"sphere"}
+## Construct a sphere or ellipsoid with centre @var{c} and radius @var{r}.
+## Optionally, @var{r} can also be a vector giving the radii in each
+## coordinate direction separately.
+##
+## @item @qcode{"box"}
+## Construct a box spanned by the given two vertex coordinates.
+##
+## @item @qcode{"half"}
+## Construct a half-space at one side of a hyperplane.  The plane
+## is specified by some point @var{p} on it, and the normal vector
+## @var{n} pointing @emph{into} the domain.
+## @end table
+##
+## @seealso{ls_union, ls_intersect, ls_setdiff, meshgrid, ndgrid}
+## @end deftypefn
+
+function phi = ls_genbasic (varargin)
+
+  % Read in the coordinate arguments until we hit a string (should be the
+  % shape).  Check that they have matching dimension.
+  coords = {};
+  argpos = 1;
+  while (true)
+    if (argpos > length (varargin))
+      print_usage ();
+    endif
+
+    cur = varargin{argpos++};
+    if (strcmp (typeinfo (cur), "string"))
+      shape = cur;
+      break;
+    endif
+
+    if (length (coords) == 0)
+      sz = size (cur);
+    else
+      if (~all (size (cur) == sz))
+        error ("coordinate grid size mismatch");
+      endif
+    endif
+    coords{end + 1} = cur;
+  endwhile
+  if (length (coords) == 0)
+    print_usage ();
+  endif
+
+  % For ease-of-use later, extract the shape-dependent arguments now.
+  shargs = cell (1, length (varargin) - argpos + 1);
+  for i = 1 : length (shargs)
+    shargs{i} = varargin{argpos + i - 1};
+  endfor
+
+  % Handle the actual shapes themselves.
+  switch (shape)
+    case "sphere"
+      phi = construct_sphere (coords, shargs);
+    case "box"
+      phi = construct_box (coords, shargs);
+    case "half"
+      phi = construct_half (coords, shargs);
+    otherwise
+      error ("invalid value '%s' for SHAPE argument", shape);
+  endswitch
+endfunction
+
+% Construct a sphere.
+function phi = construct_sphere (coords, shargs)
+  if (length (shargs) ~= 2)
+    print_usage ("ls_genbasic");
+  endif
+  c = shargs{1};
+  r = shargs{2};
+  dims = length (coords);
+  if (length (c) ~= dims)
+    error ("wrong number of dimensions for centre");
+  endif
+  if (length (r) == 1)
+    r = repmat (r, 1, dims);
+  endif
+  if (length (r) ~= dims)
+    error ("wrong number of dimensions for radii");
+  endif
+
+  % The level-set function will simply use the standard equation of
+  % an ellipse:
+  %   X^2 / r1^2 + Y^2 / r2^2 = 1^2
+
+  rsq = zeros (size (coords{1}));
+  for i = 1 : dims
+    rsq += ((coords{i} - c(i)) / r(i)).^2;
+  endfor
+
+  phi = rsq - 1;
+endfunction
+
+% Construct a box.
+function phi = construct_box (coords, shargs)
+  if (length (shargs) ~= 2)
+    print_usage ("ls_genbasic");
+  endif
+  a = shargs{1};
+  b = shargs{2};
+  dims = length (coords);
+  if (length (a) ~= dims || length (b) ~= dims)
+    error ("wrong number of dimensions for box vertex");
+  endif
+
+  % Start with the full domain.  For each dimension, construct
+  % a "slice" that checks the box constraint for this dimension only,
+  % and intersect all those.
+
+  phi = -ones (size (coords{1}));
+  for i = 1 : dims
+    m = (a(i) + b(i)) / 2;
+    r = abs (a(i) - b(i)) / 2;
+    inner = abs (coords{i} - m) - r;
+
+    phi = ls_intersect (phi, inner);
+  endfor
+endfunction
+
+% Construct a half-space.
+function phi = construct_half (coords, shargs)
+  if (length (shargs) ~= 2)
+    print_usage ("ls_genbasic");
+  endif
+  p = shargs{1};
+  n = shargs{2};
+  dims = length (coords);
+  if (length (p) ~= dims)
+    error ("wrong number of dimensions for plane point");
+  endif
+  if (length (n) ~= dims)
+    error ("wrong number of dimensions for normal vector");
+  endif
+
+  % We use the plane equation in normal form.  That is, for a point x to
+  % be in the domain, the inner product between (x - p) and n must be
+  % positive.  Use its negative as level-set function.
+
+  phi = zeros (size (coords{1}));
+  for i = 1 : dims
+    phi -= n(i) * (coords{i} - p(i));
+  endfor
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Some basic variables.
+%!shared x, y, XX, YY, XXX, YYY, ZZZ
+%!  n = 121;
+%!  x = linspace (-5, 7, n);
+%!  y = linspace (-7, 5, n);
+%!  [XX, YY] = meshgrid (x, y);
+%!  [XXX, YYY, ZZZ] = ndgrid (x, y, x);
+
+% Test for basic (common) argument parsing.
+%!error <Invalid call to>
+%!  ls_genbasic ();
+%!error <Invalid call to>
+%!  ls_genbasic (1, 2);
+%!error <Invalid call to>
+%!  ls_genbasic ("sphere");
+%!error <invalid value 'unknown' for SHAPE argument>
+%!  ls_genbasic (XX, YY, "unknown");
+%!test
+%!  ls_genbasic (x, "sphere", 0, 1);
+%!  ls_genbasic (x', "sphere", 0, 1);
+%!  ls_genbasic (XX, YY, "sphere", [0, 0], 1);
+%!  ls_genbasic (0, 1, "sphere", [0, 0], 1);
+
+% Test sphere construction.
+%!error <Invalid call to>
+%!  ls_genbasic (XX, YY, "sphere");
+%!error <Invalid call to>
+%!  ls_genbasic (XX, YY, "sphere", 1);
+%!error <Invalid call to>
+%!  ls_genbasic (XX, YY, "sphere", 1, 2, 3);
+%!error <wrong number of dimensions for centre>
+%!  ls_genbasic (XX, YY, "sphere", [1, 2, 3], 1);
+%!error <wrong number of dimensions for radii>
+%!  ls_genbasic (XX, YY, "sphere", [1, 2], [1, 2, 3]);
+%!test
+%!  assert (ls_genbasic (XX, YY, "sphere", [1, -1], 1), ...
+%!          ls_genbasic (XX, YY, "sphere", [1, -1], [1, 1]));
+%!test
+%!  c = [1; -1; 2];
+%!  r = [1; 2; 0.6];
+%!  tol = 0.4;
+%!  phi1 = ls_genbasic (XXX, YYY, ZZZ, "sphere", c, r);
+%!  phi2 = ls_genbasic (XXX, YYY, ZZZ, "sphere", c, min (r) - tol);
+%!  assert (ls_issubset (phi2, phi1));
+%!  coords = {XXX, YYY, ZZZ};
+%!  for i = 1 : length (c)
+%!    assert (ls_disjoint (phi1, ...
+%!                         coords{i} - (c(i) - r(i)), ...
+%!                         c(i) + r(i) - coords{i}));
+%!    assert (~ls_disjoint (phi1 - tol, coords{i} - (c(i) - r(i))));
+%!    assert (~ls_disjoint (phi1 - tol, c(i) + r(i) - coords{i}));
+%!  endfor
+
+% Test box construction.
+%!error <Invalid call to>
+%!  ls_genbasic (XX, YY, "box", 1);
+%!error <Invalid call to>
+%!  ls_genbasic (XX, YY, "box", 1, 2, 3);
+%!error <wrong number of dimensions for box vertex>
+%!  ls_genbasic (XX, YY, "box", [1, 2, 3], [1, 2]);
+%!error <wrong number of dimensions for box vertex>
+%!  ls_genbasic (XX, YY, "box", [1; 2], [1, 2, 3]);
+%!test
+%!  assert (ls_equal (ls_genbasic (x, "box", -1, 2), abs (x - 0.5) - 1.5));
+%!  assert (ls_equal (ls_genbasic (x, "box", 2, -1), abs (x - 0.5) - 1.5));
+%!test
+%!  a = [2; -3; 1];
+%!  b = [0; 2; -1];
+%!  tol = 0.2;
+%!  phi1 = ls_genbasic (XXX, YYY, ZZZ, "box", a, b);
+%!  phi2 = ls_genbasic (XXX, YYY, ZZZ, "box", b, a);
+%!  assert (phi1, phi2);
+%!  coords = {XXX, YYY, ZZZ};
+%!  for i = 1 : length (coords)
+%!    assert (ls_disjoint (phi1, ...
+%!                         coords{i} - min (a(i), b(i)), ...
+%!                         max (a(i), b(i)) - coords{i}));
+%!    assert (~ls_disjoint (phi1 - tol, coords{i} - min (a(i), b(i))));
+%!    assert (~ls_disjoint (phi1 - tol, max (a(i), b(i)) - coords{i}));
+%!  endfor
+
+% Test half-space construction.
+%!error <Invalid call to>
+%!  ls_genbasic (XX, YY, "half", 1);
+%!error <Invalid call to>
+%!  ls_genbasic (XX, YY, "half", 1, 2, 3);
+%!error <wrong number of dimensions for plane point>
+%!  ls_genbasic (XX, YY, "half", [1, 2, 3], [1, 2]);
+%!error <wrong number of dimensions for normal vector>
+%!  ls_genbasic (XX, YY, "half", [1; 2], [1, 2, 3]);
+%!test
+%!  p = [0, 2, 1];
+%!
+%!  phi = ls_genbasic (XXX, YYY, ZZZ, "half", p, [0; 0; 1]);
+%!  assert (ls_equal (phi, 1 - ZZZ));
+%!
+%!  normal = [1; 2; -0.5];
+%!  phi1 = ls_genbasic (XXX, YYY, ZZZ, "half", p, normal);
+%!  phi2 = ls_genbasic (XXX, YYY, ZZZ, "half", p, 2 * normal);
+%!  assert (ls_equal (phi1, phi2));
+%!
+%!  phi = ls_genbasic (XX, YY, "half", [0, 0], [1, -1]);
+%!  assert (ls_equal (phi, YY - XX));
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+%!demo
+%!  n = 200;
+%!  x = linspace (-7, 7, n);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi1 = ls_genbasic (XX, YY, "box", [-5, -3], [3, 6]);
+%!  phi2 = ls_genbasic (XX, YY, "sphere", [-1; 1], 2);
+%!  phi3 = ls_genbasic (XX, YY, "sphere", [0; 0], [6, 1]);
+%!  phi4 = ls_genbasic (XX, YY, "half", [3, -3], [1, -2]) / 8;
+%!
+%!  phi = ls_union (ls_setdiff (phi1, phi2), phi3, phi4);
+%!  figure ();
+%!  hold ("on");
+%!  imagesc (x, x, phi);
+%!  set (gca (), "ydir", "normal");
+%!  ls_sign_colourmap ();
+%!  contour (XX, YY, phi, [0, 0], "k");
+%!  hold ("off");
+%!  axis ("equal");
diff --git a/inst/ls_get_tests.m b/inst/ls_get_tests.m
new file mode 100644
index 0000000..d02599a
--- /dev/null
+++ b/inst/ls_get_tests.m
@@ -0,0 +1,80 @@
+##  Copyright (C) 2013-2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{phis} =} ls_get_tests ()
+## 
+## Return a couple of level-set functions for certain 2D cases in the
+## cell-array @var{phis}.  They can be used in tests.
+##
+## Use @code{demo ls_get_tests} to get an overview of the situations included.
+##
+## @end deftypefn
+
+function phis = ls_get_tests ()
+  phis = {};
+
+  % The most basic situation.
+  phi = ones (3, 3);
+  phi(2, 2) = -1;
+  phis{end + 1} = phi;
+
+  % The same but now with infinite values.
+  phi = Inf (3, 3);
+  phi(2, 2) = -Inf;
+  phis{end + 1} = phi;
+
+  % Include a fat zero level-set.
+  phi = ones (6, 6);
+  phi(2 : 5, 2 : 5) = 0;
+  phi(3, 4) = -1;
+  phis{end + 1} = phi;
+
+  % Two connected components.
+  x = linspace (-10, 10, 100);
+  y = linspace (-10, 10, 80);
+  [XX, YY] = meshgrid (x, y);
+  phi = ls_union ((XX - 2).^2 + (YY - 5).^2 - 3^2, ...
+                  (XX + 2).^2 + (YY - 5).^2 - 3^2, ...
+                  XX.^2 + (YY + 3).^2 - 1.5^2);
+  phis{end + 1} = phi;
+
+  % Include a "narrow pair".
+  phi = ones (6, 6);
+  phi(2 : 3, 4 : 5) = -1;
+  phi(4 : 5, 2 : 3) = -1;
+  phis{end + 1} = phi;
+
+  % Very small value which may be rounded to zero during
+  % operations like ls_signed_distance.
+  phi = ones (4, 4);
+  phi(2 : 3, 2 : 3) = -1;
+  phi(2, 2) = -1e-18;
+  phis{end + 1} = phi;
+endfunction
+
+% Demo function that plots all the tests.
+%!demo
+%!  phis = ls_get_tests ();
+%!  for i = 1 : length (phis)
+%!    figure ();
+%!    hold ("on");
+%!    imagesc (sign (phis{i}));
+%!    caxis ([-1, 1]);
+%!    hold ("off");
+%!    titleFmt = "Situation #%d.  Blue: Interior, Green: Zero, Red: Outside.";
+%!    title (sprintf (titleFmt, i));
+%!  endfor
diff --git a/inst/ls_hausdorff_dist.m b/inst/ls_hausdorff_dist.m
new file mode 100644
index 0000000..9c922c9
--- /dev/null
+++ b/inst/ls_hausdorff_dist.m
@@ -0,0 +1,132 @@
+##  Copyright (C) 2015  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{dh} =} ls_hausdorff_dist (@var{phi1}, @var{phi2}, @var{h} = 1)
+## @deftypefnx {Function File} {@var{dh} =} ls_hausdorff_dist (@var{sd1}, @var{sd2}, @qcode{"sd"})
+##
+## Approximate the Hausdorff distance between two sets.  The sets are given
+## by their level-set functions @var{phi1} and @var{phi2}.  The Hausdorff
+## distance is calculated as the maximum difference between their distance
+## functions.  (Note that it is the ordinary distance function here, not
+## the @emph{signed} distance function!)
+##
+## If we neglect possible approximation errors in the distance function,
+## the result @var{dh} is guaranteed to be a lower bound of the exact
+## Hausdorff distance.  It is within the real distance by, roughly, @var{h}.
+##
+## The second call form assumes that the level-set functions of the
+## domains are actually already signed distance functions @var{sd1} and
+## @var{sd2}.  In this case, the grid spacing @var{h} is not necessary.
+## Since there is no need to call @code{ls_distance_fcn}, the calculation
+## can be performed faster in this case.
+## 
+## @seealso{ls_equal, ls_distance_fcn, ls_signed_distance}
+## @end deftypefn
+
+function dh = ls_hausdorff_dist (phi1, phi2, h = 1)
+  if (nargin () < 2 || nargin () > 3)
+    print_usage ();
+  endif
+  
+  sdMode = false;
+  if (isnumeric (h))
+    if (!isscalar (h))
+      error ("H must be scalar");
+    endif
+  else
+    if (!strcmp (h, "sd"))
+      error ("invalid option, expected H or \"sd\"");
+    endif
+    sdMode = true;
+  endif
+
+  if (!all (size (phi1) == size (phi2)))
+    error ("PHI1 and PHI2 must be of the same size");
+  endif
+
+  if (sdMode)
+    d1 = max (phi1, 0);
+    d2 = max (phi2, 0);
+  else
+    d1 = ls_distance_fcn (phi1, h);
+    d2 = ls_distance_fcn (phi2, h);
+  endif
+  assert (all (d1(:)) >= 0);
+  assert (all (d2(:)) >= 0);
+
+  dh = norm (d1(:) - d2(:), inf);
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_hausdorff_dist (1)
+%!error <Invalid call to>
+%!  ls_hausdorff_dist (1, 2, 3, 4)
+%!error <H must be scalar>
+%!  ls_hausdorff_dist (1, 2, [3, 3])
+%!error <invalid option, expected H or "sd">
+%!  ls_hausdorff_dist (1, 2, "foo")
+
+% Basic test with various circle combinations.
+% Also exercise the "sd" variant.
+%!test
+%!  n = 50;
+%!  x = linspace (-5, 5, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi1 = ls_genbasic (XX, YY, "sphere", [3, 0], 1);
+%!  phi2 = ls_genbasic (XX, YY, "sphere", [-2, 0], 2);
+%!  assert (ls_hausdorff_dist (phi1, phi2, h), 6, h);
+%!
+%!  % Make sure that phi isn't a signed-distance fcn already.
+%!  % ls_genbasic should return the same, but make it explicit.
+%!  rSq = XX.^2 + YY.^2;
+%!  d = ls_hausdorff_dist (rSq - 4^2, rSq - 2^2, h);
+%!  assert (d, 2, h);
+%!  dWrong = ls_hausdorff_dist (rSq - 4^2, rSq - 2^2, "sd");
+%!  assert (abs (d - dWrong) > 10 * h);
+%!  dRight = ls_hausdorff_dist (sqrt (rSq) - 4, sqrt (rSq) - 2, "sd");
+%!  assert (dRight, d, h);
+
+% Test the case where the difference of the *signed* distance
+% functions gives the wrong answer.
+%!test
+%!  n = 200;
+%!  x = linspace (-5, 5, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phiA = ls_genbasic (XX, YY, "sphere", [0, 0], 4);
+%!  phiInner = ls_genbasic (XX, YY, "sphere", [0, 0], 2);
+%!  phiB = ls_setdiff (phiA, phiInner);
+%!
+%!  d1 = ls_hausdorff_dist (phiA, phiB, h);
+%!  d2 = ls_hausdorff_dist (phiB, phiA, h);
+%!  assert (d1, d2);
+%!  assert (d1, 2, 2 * h);
+%!  dc = ls_hausdorff_dist (ls_complement (phiA), ...
+%!                              ls_complement (phiB), h);
+%!  assert (dc, 4, 2 * h);
+%!
+%!  sdA = ls_signed_distance (phiA, h);
+%!  sdB = ls_signed_distance (phiB, h);
+%!  sdDiff = norm (sdA(:) - sdB(:), inf);
+%!  assert (abs (sdDiff - d1) > 10 * h);
diff --git a/inst/ls_init_narrowband.m b/inst/ls_init_narrowband.m
new file mode 100644
index 0000000..9ab5ea7
--- /dev/null
+++ b/inst/ls_init_narrowband.m
@@ -0,0 +1,154 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{d} =} ls_init_narrowband (@var{phi}, @var{h} = 1)
+## 
+## Calculate the estimated distances of grid points in the ``narrow band''
+## to the zero level-set of @var{phi}.  The points considered are those
+## which have neighbours with different sign of @var{phi}.  The calculated
+## distances are returned in @var{d}, where values for points not in the
+## narrow band are set to @code{NA}.
+## If the optional argument @var{h} is given, it is used as the grid spacing
+## instead of the default value of 1.
+##
+## The distances will be positive or negative depending on the sign
+## of @var{phi}.  This means that @var{d} gives the signed
+## distance function of the level-set domain for narrow-band points.
+##
+## We assume a linear model for @var{phi}, meaning that the approximate
+## intersection points on grid edges are calculated using linear interpolation.
+## The distances at narrow-band points are calculated using the quadratic
+## update equation of the Fast-Marching Method using these approximated
+## intersection points.
+##
+## Note that this method does not take an arbitrary speed field into account.
+## It assumes a uniform speed of 1 everywhere.  For different speeds, the
+## resulting distances must be scaled as required.
+##
+## It may be a good idea to use @code{ls_normalise} on the level-set function
+## before using this method, to prevent almost-zero values from underflowing
+## due to the performed calculations.
+##
+## @seealso{ls_signed_distance, ls_nb_from_geom, ls_normalise}
+## @end deftypefn
+
+function d = ls_init_narrowband (phi, h = 1)
+  if (nargin () < 1 || nargin () > 2)
+    print_usage ();
+  endif
+
+  d = __levelset_internal_init_narrowband (phi, h);
+  d = ls_copy_sign (d, phi);
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_init_narrowband ()
+%!error <Invalid call to>
+%!  ls_init_narrowband (1, 2, 3)
+
+% Check 0D case.
+%!test
+%!  assert (ls_init_narrowband ([]), []);
+
+% Check 1D case, for which the expected result is trivial to calculate
+% and should be almost exact.
+%!test
+%!  n = 10;
+%!  x = linspace (-2, 2, n);
+%!  h = x(2) - x(1);
+%!  phi = abs (x) - 1;
+%!
+%!  d = ls_init_narrowband (phi, h);
+%!  where = ~isna (d);
+%!  assert (d(where), phi(where), sqrt (eps));
+
+% Test with circular region in 3D.
+%!test
+%!  n = 50;
+%!  x = linspace (-2, 2, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY, ZZ] = ndgrid (x);
+%!  RRsq = XX.^2 + YY.^2 + ZZ.^2;
+%!  phi = RRsq - 1;
+%!
+%!  d = ls_init_narrowband (phi, h);
+%!  where = ~isna (d);
+%!  assert (d(where), sqrt (RRsq(where)) - 1, h / 3);
+
+% Test cases of infinities in level-set function.
+%!test
+%!  tol = sqrt (eps);
+%!  assert (ls_init_narrowband ([Inf, -Inf, Inf]), [0.5, -0.5, 0.5], tol);
+%!  assert (ls_init_narrowband ([Inf, -1, Inf]), [1, 0, 1], tol);
+%!  %assert (ls_init_narrowband ([1, -Inf, 1]), [0, 1, 0], tol);
+
+% Compare two contour lines as per contourc for their Hausdorff distance.
+% This is used to check that the contour lines found for d and phi itself
+% are (almost) the same.
+%
+%!function dh = distToSegment (x, a, b)
+%!  dir = b - a;
+%!  dir /= norm (dir);
+%!  proj = dot (x - a, dir);
+%!  if (proj < 0)
+%!    dh = norm (x - a);
+%!  elseif (proj > 1)
+%!    dh = norm (x - b);
+%!  else
+%!    dh = norm (x - (a + proj * dir));
+%!  endif
+%!endfunction
+%
+%!function dh = distToLine (x, c)
+%!  assert (c(1, 1), 0);
+%!  dh = Inf;
+%!  for i = 2 : size (c, 2) - 1
+%!    dh = min (dh, distToSegment (x, c(:, i), c(:, i + 1)));
+%!  endfor
+%!endfunction
+%
+%!function dh = distOfContoursAsym (c1, c2)
+%!  dh = 0;
+%!  for i = 2 : size (c1, 2)
+%!    dh = max (dh, distToLine (c1(:, i), c2));
+%!  endfor
+%!endfunction
+%
+%!function dh = distOfContours (c1, c2)
+%!  dh = max (distOfContoursAsym (c1, c2), distOfContoursAsym (c2, c1));
+%!endfunction
+
+% Compare distance of contour lines for the test phis.
+%!test
+%!  phis = ls_get_tests ();
+%!  printf ("Comparing contours for %d cases...\n", length (phis));
+%!  for i = 1 : length (phis)
+%!    d = ls_init_narrowband (phis{i});
+%!    c1 = contourc (d, [0, 0]);
+%!    if (size (c1, 2) > 0)
+%!      c2 = contourc (phis{i}, [0, 0]);
+%!      dh = distOfContours (c1, c2);
+%!      printf ("i = %d: dist = %f\n", i, dh);
+%!      if (isfinite (dh))
+%!        assert (dh, 0, 1e-1);
+%!      endif
+%!    endif
+%!  endfor
diff --git a/inst/ls_inside.m b/inst/ls_inside.m
new file mode 100644
index 0000000..bae53f5
--- /dev/null
+++ b/inst/ls_inside.m
@@ -0,0 +1,60 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{where} =} ls_inside (@var{phi})
+## 
+## Return in a logical array which points are inside the domain
+## described by the level-set function @var{phi}.  These are the points
+## where @var{phi} is negative.  If the @code{signbit} function is available
+## (on newer versions of GNU Octave), also negative zeros are considered
+## to be part of the domain.
+##
+## @seealso{ls_check, ls_issubset, ls_normalise}
+## @end deftypefn
+
+function res = ls_inside (phi)
+  if (nargin () ~= 1)
+    print_usage ();
+  endif
+
+  if (exist ("signbit") == 5)
+    res = signbit (phi);
+  else
+    res = (phi < 0);
+  endif
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_inside ()
+%!error <Invalid call to>
+%!  ls_inside (1, 2)
+
+% Basic test.
+%!test
+%!  assert (ls_inside ([1, 0, -1, 0, 1]), [false, false, true, false, false]);
+
+% Test negative zeros.
+%!test
+%!  if (exist ("signbit") == 5)
+%!    assert (ls_inside ([-0, 0]), [true, false]);
+%!  else
+%!    warning ("'signbit' function not available, skipping test.");
+%!  endif
diff --git a/inst/ls_intersect.m b/inst/ls_intersect.m
new file mode 100644
index 0000000..d594582
--- /dev/null
+++ b/inst/ls_intersect.m
@@ -0,0 +1,90 @@
+##  Copyright (C) 2014-2015  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{phi} =} ls_intersect (@var{phi1}, @var{phi2})
+## @deftypefnx  {Function File} {@var{phi} =} ls_intersect (@var{phi}, ...)
+## 
+## Calculate a level-set function for the intersection of the sets described
+## by the argument level-set functions.
+##
+## @seealso{ls_complement, ls_union, ls_setdiff, ls_setxor, intersect}
+## @end deftypefn
+
+function res = ls_intersect (varargin)
+  res = ls_union_intersect (@max, varargin, "ls_intersect");
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_intersect ()
+%!error <size mismatch in the arguments>
+%!  ls_intersect (1, -2, [1, 2])
+
+% Test that the intersection is part of all pieces.
+%!test
+%!  n = 50;
+%!  x = linspace (-10, 10, n);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi1 = (XX - 2).^2 + (YY - 2).^2 - 2^2;
+%!  phi2 = (XX + 2).^2 + (YY + 2).^2 - 2^2;
+%!  phi3 = XX.^2 + YY.^2 - 2^2;
+%!
+%!  assert (ls_isempty (ls_intersect (phi1, phi2)));
+%!  assert (ls_intersect (phi3), phi3);
+%!
+%!  phi = ls_intersect (phi1, phi3);
+%!  assert (~ls_isempty (phi));
+%!  assert (ls_issubset (phi, phi1) && ls_issubset (phi, phi3));
+%!
+%!  phi = ls_intersect (phi2, phi3);
+%!  assert (~ls_isempty (phi));
+%!  assert (ls_issubset (phi, phi2) && ls_issubset (phi, phi3));
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demo.
+
+%!demo
+%!  n = 100;
+%!  x = linspace (-7, 7, n);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi1 = (XX - 2 * cos (7/6 * pi)).^2 + (YY - 2 * sin (7/6 * pi)).^2 - 3^2;
+%!  phi2 = (XX - 2 * cos (11/6 * pi)).^2 + (YY - 2 * sin (11/6 * pi)).^2 - 3^2;
+%!  phi3 = XX.^2 + (YY - 2).^2 - 3^2;
+%!  phi = ls_intersect (phi1, phi2, phi3);
+%!
+%!  figure ();
+%!  subplot (1, 2, 1);
+%!  hold ("on");
+%!  contour (XX, YY, phi1, [0, 0], "k");
+%!  contour (XX, YY, phi2, [0, 0], "k");
+%!  contour (XX, YY, phi3, [0, 0], "k");
+%!  hold ("off");
+%!  axis ("equal");
+%!
+%!  subplot (1, 2, 2);
+%!  hold ("on");
+%!  imagesc (x, x, phi);
+%!  set (gca (), "ydir", "normal");
+%!  ls_sign_colourmap ();
+%!  contour (XX, YY, phi, [0, 0], "k");
+%!  hold ("off");
+%!  axis ("equal");
diff --git a/inst/ls_isempty.m b/inst/ls_isempty.m
new file mode 100644
index 0000000..d809f6e
--- /dev/null
+++ b/inst/ls_isempty.m
@@ -0,0 +1,54 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{res} =} ls_isempty (@var{phi})
+## 
+## Check if the set described by @var{phi} is the empty set.
+##
+## @seealso{ls_inside}
+## @end deftypefn
+
+function res = ls_isempty (phi)
+  if (nargin () ~= 1)
+    print_usage ();
+  endif
+
+  inside = ls_inside (phi);
+  res = all (~inside(:));
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_isempty ()
+%!error <Invalid call to>
+%!  ls_isempty (1, 2)
+
+% Basic tests for the function.
+%!test
+%!  n = 50;
+%!  x = linspace (-10, 10, n);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi1 = (XX - 2).^2 + (YY - 2).^2 - 2^2;
+%!  phi2 = (XX + 2).^2 + (YY + 2).^2 - 2^2;
+%!
+%!  assert (~ls_isempty (phi1) && ~ls_isempty (phi2));
+%!  assert (ls_isempty (ls_intersect (phi1, phi2)));
+%!  assert (~ls_isempty (ls_union (phi1, phi2)));
diff --git a/inst/ls_issubset.m b/inst/ls_issubset.m
new file mode 100644
index 0000000..fefa898
--- /dev/null
+++ b/inst/ls_issubset.m
@@ -0,0 +1,61 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{res} =} ls_issubset (@var{phi1}, @var{phi2})
+## 
+## Check if the set described by @var{phi1} is a subset of @var{phi2}.
+##
+## @seealso{ls_inside, ls_equal, ls_disjoint, ls_check}
+## @end deftypefn
+
+function res = ls_issubset (phi1, phi2)
+  if (nargin () ~= 2)
+    print_usage ();
+  endif
+
+  if (~all (size (phi1) == size (phi2)))
+    error ("PHI1 and PHI2 must be of the same size");
+  endif
+
+  res = ls_check (phi2, "contain", ls_inside (phi1));
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_issubset (1)
+%!error <Invalid call to>
+%!  ls_issubset (1, 2, 3)
+%!error <PHI1 and PHI2 must be of the same size>
+%!  ls_issubset (1, [1, 2])
+
+% Basic tests for the cases.
+%!test
+%!  n = 50;
+%!  x = linspace (-10, 10, n);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi1 = XX.^2 + YY.^2 - 1^2;
+%!  phi2 = XX.^2 + YY.^2 - 5^2;
+%!  phi3 = XX.^2 + YY.^2 - 8^2;
+%!  phi4 = (XX - 3).^2 + YY.^2 - 3^2;
+%!
+%!  assert (ls_issubset (phi1, phi2));
+%!  assert (ls_issubset (phi4, phi3));
+%!  assert (~ls_issubset (phi4, phi2));
diff --git a/inst/ls_nb_from_geom.m b/inst/ls_nb_from_geom.m
new file mode 100644
index 0000000..69c7485
--- /dev/null
+++ b/inst/ls_nb_from_geom.m
@@ -0,0 +1,174 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{d} =} ls_nb_from_geom (@var{geom}, @var{phi})
+## @deftypefnx {Function File} {[@var{d}, @var{g}] =} ls_nb_from_geom (@var{geom}, @var{phi}, @var{g0})
+## 
+## Initialise the narrow-band values utilising information about the
+## geometry.  @var{geom} should be the geometry according to
+## @code{ls_find_geometry}, and it must contain absolute coordinates
+## as per @code{ls_absolute_geom} in addition.  Thus it is unnecessary
+## to pass the grid spacing, as this information is already
+## contained in the absolute coordinates.
+## This is an alternative routine to the standard
+## @code{ls_init_narrowband} for 2D situations.
+##
+## This function sets the distance at @emph{each} node of a boundary
+## element to the shortest distance to any boundary edge
+## according to the geometry information in @var{geom}.  In contrast
+## to @code{ls_init_narrowband}, this also initialises values on the
+## ``far diagonally away'' point.  This makes the result more accurate.
+##
+## In the second form, @var{g0} is expected to contain the values of some
+## function defined on each boundary edge (ordered in the same way as
+## @code{@var{geom}.bedges}).  These function values will be
+## extended onto narrow-band points as well, and returned in @var{g}.
+##
+## @seealso{ls_init_narrowband, ls_find_geometry, ls_absolute_geom,
+## ls_solve_stationary, fastmarching}
+## @end deftypefn
+
+function [d, g] = ls_nb_from_geom (geom, phi, g0 = NA)
+  if (nargin () < 2 || nargin () > 3)
+    print_usage ();
+  endif
+
+  hasG = (nargin () == 3);
+  if (hasG)
+
+    % Turn row vector into column.
+    if (size (g0, 1) == 1)
+      g0 = g0';
+    endif
+
+    if (size (g0, 2) != 1)
+      error ("G0 should be a vector");
+    endif
+    if (length (g0) != geom.bedges.n)
+      error ("G0 has wrong size");
+    endif
+  else
+    if (nargout () > 1)
+      print_usage ();
+    endif
+  endif
+
+  if (!isfield (geom.ispts, "coord") || !isfield (geom, "nodes"))
+    error ("GEOM misses absolute coordinates, use ls_absolute_geom first");
+  endif
+
+  [d, gSum, gCnt] = __levelset_nbFromGeom (geom.bedges.ispts, phi, hasG, g0, ...
+                                           geom.elem.nodelist, ...
+                                           geom.elem.index.bdry, ...
+                                           geom.ispts.onedge(:, :, 1), ...
+                                           geom.nodes.coord, geom.ispts.coord);
+
+  d = ls_copy_sign (d, phi);
+  if (hasG)
+    g = gSum ./ gCnt;
+  endif
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_nb_from_geom (1)
+%!error <Invalid call to>
+%!  ls_nb_from_geom (1, 2, 3, 4)
+%!error <Invalid call to>
+%!  [a, b] = ls_nb_from_geom (1, 2);
+%!error <G0 should be a vector>
+%!  ls_nb_from_geom (1, 2, rand (2, 2));
+
+% Test for missing absolute coordinates.
+%!error <GEOM misses absolute coordinates, use ls_absolute_geom first>
+%!  x = linspace (-10, 10, 10);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!  phi = ls_genbasic (XX, YY, "sphere", [0, 0], 5);
+%!  phi = ls_normalise (phi, h);
+%!  geom = ls_find_geometry (phi, h);
+%!  ls_nb_from_geom (geom, phi);
+
+% Test for size check on g0.
+%!error <G0 has wrong size>
+%!  x = linspace (-10, 10, 10);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!  phi = ls_genbasic (XX, YY, "sphere", [0, 0], 5);
+%!  phi = ls_normalise (phi, h);
+%!  geom = ls_find_geometry (phi, h);
+%!  ls_nb_from_geom (geom, phi, [1, 2, 3]);
+
+% Compare (with some tolerance) to ls_init_narrowband.
+%!test
+%!  n = 50;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi = ls_setdiff (ls_genbasic (XX, YY, "sphere", [0, 0], 8), ...
+%!                    ls_genbasic (XX, YY, "sphere", [1, 1], 5));
+%!  phi = ls_normalise (phi, h);
+%!  geom = ls_find_geometry (phi, h);
+%!  geom = ls_absolute_geom (geom, XX, YY);
+%!
+%!  dOld = ls_init_narrowband (phi, h);
+%!  dNew = ls_nb_from_geom (geom, phi);
+%!
+%!  where = (!isna (dOld));
+%!  assert (all (!isna (dNew(where))));
+%!  assert (dNew(where), dOld(where), h / 4);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+% TODO: Demo comparing different initialisations.
+
+% Try a basic extension of function values.
+%!demo
+%!  n = 50;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi = ls_genbasic (XX, YY, "sphere", [0, 0], 3);
+%!  phi = ls_normalise (phi, h);
+%!  geom = ls_find_geometry (phi, h);
+%!  geom = ls_absolute_geom (geom, XX, YY);
+%!
+%!  g0 = NA (1, geom.bedges.n);
+%!  for i = 1 : geom.bedges.n
+%!    a = geom.ispts.coord(geom.bedges.ispts(i, 1), :);
+%!    b = geom.ispts.coord(geom.bedges.ispts(i, 2), :);
+%!    m = (a + b) / 2;
+%!    angle = abs (atan2 (m(2), m(1)));
+%!    g0(i) = min (angle, 2 * pi - angle);
+%!  endfor
+%!
+%!  [d, g] = ls_nb_from_geom (geom, phi, g0);
+%!  [d, g] = fastmarching (d, g, h * ones (size (phi)));
+%!
+%!  figure ();
+%!  hold ("on");
+%!  imagesc (x, x, g);
+%!  set (gca (), "ydir", "normal");
+%!  contour (XX, YY, phi, [0, 0], "k");
+%!  hold ("off");
+%!  colorbar ();
diff --git a/inst/ls_normalise.m b/inst/ls_normalise.m
new file mode 100644
index 0000000..a71a7e7
--- /dev/null
+++ b/inst/ls_normalise.m
@@ -0,0 +1,74 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{phi} =} ls_normalise (@var{phi}, @var{h} = 1, @var{zerotol} = 1e-3)
+##
+## Normalise the level-set function @var{phi}.  This gets rid of values that
+## are exactly zero, by ensuring that each entry of the changed level-set
+## function has a magnitude of at least @var{h} * @var{zerotol}.  The
+## actual level-set geometry is not changed, except possibly the approximated
+## intersections between the zero level-set and grid edges due to slight
+## variations in the actual values of @var{phi}.
+##
+## Exactly zero values are interpreted according to their sign bit if the
+## @code{signbit} function is available (on newer versions of GNU Octave).
+## If the function is not available, then zeros are assumed to be
+## @strong{not} part of the level-set domain.
+## 
+## @seealso{ls_inside}
+## @end deftypefn
+
+function phi = ls_normalise (phi, h = 1, zerotol = 1e-3)
+  if (nargin () < 1 || nargin () > 3)
+    print_usage ();
+  endif
+
+  zerotol *= h;
+  exz = (phi == 0);
+  if (exist ("signbit") == 5)
+    phi(exz) = zerotol * (1 - 2 * double (signbit (phi(exz))));
+  else
+    phi(exz) = zerotol;
+  endif
+  zeroPts = (abs (phi) < zerotol);
+  phi(zeroPts) = zerotol * sign (phi(zeroPts));
+
+  assert (all (abs (phi(:)) >= zerotol));
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_normalise ()
+%!error <Invalid call to>
+%!  ls_normalise (1, 2, 3, 4)
+
+%!test
+%!  h = 0.1;
+%!  zeroTol = 0.2;
+%!  phi = ones (4, 4);
+%!  phiEx = phi;
+%!  phi(2 : 3, 2 : 3) = [-eps, -0; 0, eps];
+%!  phiEx(2 : 3, 2 : 3) = [-zeroTol*h, -zeroTol*h; zeroTol*h, zeroTol*h];
+%!  phin = ls_normalise (phi, h, zeroTol);
+%!  if (exist ("signbit") == 5)
+%!    assert (phin, phiEx);
+%!  else
+%!    warning ("'signbit' function not available, skipping test.");
+%!  endif
diff --git a/inst/ls_setdiff.m b/inst/ls_setdiff.m
new file mode 100644
index 0000000..2c0f5de
--- /dev/null
+++ b/inst/ls_setdiff.m
@@ -0,0 +1,122 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{phi} =} ls_setdiff (@var{phi1}, @var{phi2})
+## 
+## Calculate a level-set function for the set difference @var{phi1}
+## minus @var{phi2}.
+##
+## @seealso{ls_complement, ls_union, ls_intersect, ls_setxor, setdiff}
+## @end deftypefn
+
+function phi = ls_setdiff (phi1, phi2)
+  if (nargin () ~= 2)
+    print_usage ();
+  endif
+
+  if (~all (size (phi1) == size (phi2)))
+    error ("PHI1 and PHI2 must be of the same size");
+  endif
+
+  phi = ls_intersect (phi1, ls_complement (phi2));
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_setdiff (1)
+%!error <Invalid call to>
+%!  ls_setdiff (1, 2, 3)
+%!error <PHI1 and PHI2 must be of the same size>
+%!  ls_setdiff (1, [1, 2])
+
+% Basic test.
+%!test
+%!  assert (ls_equal (ls_setdiff ([-1, -1, 1], [1, -1, -1]), [-1, 1, 1]));
+
+% 2D test with circles.
+%!  n = 50;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi1 = XX.^2 + YY.^2 - 8^2;
+%!  phi2 = XX.^2 + YY.^2 - 5^2;
+%!
+%!  phi = ls_setdiff (phi1, phi2);
+%!  assert (ls_issubset (phi, phi1));
+%!  assert (ls_disjoint (phi, phi2));
+%!
+%!  phi = ls_setdiff (phi2, phi1);
+%!  assert (ls_isempty (phi));
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demo.
+
+%!demo
+%!  n = 100;
+%!  x = linspace (-7, 7, n);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi1 = (XX - 2).^2 + YY.^2 - 3^2;
+%!  phi2 = (XX + 2).^2 + YY.^2 - 3^2;
+%!  phi = ls_setdiff (phi1, phi2);
+%!
+%!  figure ();
+%!  subplot (1, 2, 1);
+%!  hold ("on");
+%!  contour (XX, YY, phi1, [0, 0], "k");
+%!  contour (XX, YY, phi2, [0, 0], "k");
+%!  hold ("off");
+%!  axis ("equal");
+%!
+%!  subplot (1, 2, 2);
+%!  hold ("on");
+%!  imagesc (x, x, phi);
+%!  set (gca (), "ydir", "normal");
+%!  ls_sign_colourmap ();
+%!  contour (XX, YY, phi, [0, 0], "k");
+%!  hold ("off");
+%!  axis ("equal");
+
+%!demo
+%!  n = 100;
+%!  x = linspace (-7, 7, n);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi1 = XX.^2 + YY.^2 - 6^2;
+%!  phi2 = XX.^2 + YY.^2 - 3^2;
+%!  phi = ls_setdiff (phi1, phi2);
+%!
+%!  figure ();
+%!  subplot (1, 2, 1);
+%!  hold ("on");
+%!  contour (XX, YY, phi1, [0, 0], "k");
+%!  contour (XX, YY, phi2, [0, 0], "k");
+%!  hold ("off");
+%!  axis ("equal");
+%!
+%!  subplot (1, 2, 2);
+%!  hold ("on");
+%!  imagesc (x, x, phi);
+%!  set (gca (), "ydir", "normal");
+%!  ls_sign_colourmap ();
+%!  contour (XX, YY, phi, [0, 0], "k");
+%!  hold ("off");
+%!  axis ("equal");
diff --git a/inst/ls_setxor.m b/inst/ls_setxor.m
new file mode 100644
index 0000000..e897d73
--- /dev/null
+++ b/inst/ls_setxor.m
@@ -0,0 +1,95 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{phi} =} ls_setxor (@var{phi1}, @var{phi2})
+## 
+## Calculate a level-set function for the set ``xor'' of the
+## two domains given via @var{phi1} and @var{phi2}.  In other words,
+## returned is a domain that contains all points that are in precisely
+## @emph{one} of the sets but not in both.
+##
+## @seealso{ls_complement, ls_union, ls_intersect, ls_setdiff, setxor}
+## @end deftypefn
+
+function phi = ls_setxor (phi1, phi2)
+  if (nargin () ~= 2)
+    print_usage ();
+  endif
+
+  if (~all (size (phi1) == size (phi2)))
+    error ("PHI1 and PHI2 must be of the same size");
+  endif
+
+  total = ls_union (phi1, phi2);
+  both = ls_intersect (phi1, phi2);
+  phi = ls_setdiff (total, both);
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_setxor (1)
+%!error <Invalid call to>
+%!  ls_setxor (1, 2, 3)
+%!error <PHI1 and PHI2 must be of the same size>
+%!  ls_setxor (1, [1, 2])
+
+% Basic test.
+%!test
+%!  assert (ls_equal (ls_setxor ([-1, -1, Inf], [1, -1, -Inf]), [-1, 1, -1]));
+
+% 2D test with circles.
+%!  n = 50;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi1 = XX.^2 + YY.^2 - 8^2;
+%!  phi2 = XX.^2 + YY.^2 - 5^2;
+%!
+%!  assert (ls_equal (ls_setdiff (phi1, phi2), ls_setxor (phi1, phi2)));
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demo.
+
+%!demo
+%!  n = 100;
+%!  x = linspace (-7, 7, n);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi1 = (XX - 2).^2 + YY.^2 - 3^2;
+%!  phi2 = (XX + 2).^2 + YY.^2 - 3^2;
+%!  phi = ls_setxor (phi1, phi2);
+%!
+%!  figure ();
+%!  subplot (1, 2, 1);
+%!  hold ("on");
+%!  contour (XX, YY, phi1, [0, 0], "k");
+%!  contour (XX, YY, phi2, [0, 0], "k");
+%!  hold ("off");
+%!  axis ("equal");
+%!
+%!  subplot (1, 2, 2);
+%!  hold ("on");
+%!  imagesc (x, x, phi);
+%!  set (gca (), "ydir", "normal");
+%!  ls_sign_colourmap ();
+%!  contour (XX, YY, phi, [0, 0], "k");
+%!  hold ("off");
+%!  axis ("equal");
diff --git a/inst/ls_sign_colourmap.m b/inst/ls_sign_colourmap.m
new file mode 100644
index 0000000..b8bbab5
--- /dev/null
+++ b/inst/ls_sign_colourmap.m
@@ -0,0 +1,244 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn   {Function File} ls_sign_colourmap (@var{type} = @qcode{"sign"})
+## @deftypefnx  {Function File} ls_sign_colourmap (@var{colours})
+## @deftypefnx  {Function File} {@var{cmap} =} ls_sign_colourmap (@var{ax}, @var{type} = @qcode{"sign"})
+## @deftypefnx  {Function File} {@var{cmap} =} ls_sign_colourmap (@var{ax}, @var{colours})
+## 
+## Construct a colour map that can visibly distinguish between
+## positive and negative values.
+## These colour maps are especially suited to show the distinction between
+## above-zero and below-zero of a level-set function or for speed fields
+## between moving outward and inward.
+##
+## The colour axis range for which it is used should be given in @var{ax}.
+## There will always be 1024 entries in the constructed colour map.
+##
+## The map is ``defined'' by four colours, with a colour gradient between
+## the first two for positive values and a gradient between the third and
+## fourth for negative values.  These colours can be explicitly given
+## in @var{colours} as a 4 x 3 matrix.  Predefined ``types'' of maps
+## can be loaded with a string as @var{type} argument.  It can be:
+##
+## @table @qcode
+## @item "sign"
+## The default value.  Values above zero will be yellow--red, and values
+## below zero cyan--blue.  There is a visible discontinuity in colour
+## at the zero level-set, with yellow and cyan on both ``sides'' of the
+## transition.
+##
+## @item "highlight"
+## Show zero as white (independent of the sign).  Positive and negative
+## values are marked as red and blue, respectively.  This is useful to
+## show speed fields and just highlight where they are most active.
+## (Like a heat map.)
+## @end table
+##
+## The forms without @var{ax} and output arguments use @code{caxis ()} of the
+## current figure and set the figure's colour map to the result, instead of
+## returning the constructed colour map.
+##
+## Use @code{demo ls_sign_colourmap} to get an overview of how the
+## predefined maps look like.
+##
+## @seealso{colormap, colorbar}
+## @end deftypefn
+
+function cmap = ls_sign_colourmap (varargin)
+
+  % Start by determining which form of the function was called and doing
+  % some usage checks, setting default arguments and all that.
+
+  n = 1024;
+  type = "sign";
+
+  if (length (varargin) == 0)
+    % No arguments at all:  First form, keep defaults.
+    setFig = true;
+
+  elseif (strcmp (typeinfo (varargin{1}), "string"))
+    % First form with type given as string.
+    if (length (varargin) != 1)
+      print_usage ();
+    endif
+    setFig = true;
+    type = varargin{1};
+
+  elseif (min (size (varargin{1})) > 1)
+    % First form with colours given.
+    if (length (varargin) != 1)
+      print_usage ();
+    endif
+    setFig = true;
+
+    type = "custom";
+    colours = varargin{1};
+
+  else
+    % Should be second form.
+    setFig = false;
+    ax = varargin{1};
+
+    if (length (varargin) > 1)
+      if (length (varargin) != 2)
+        print_usage ();
+      endif
+
+      if (strcmp (typeinfo (varargin{2}), "string"))
+        type = varargin{2};
+      else
+        type = "custom";
+        colours = varargin{2};
+      endif
+    endif
+  endif
+
+  if (setFig && nargout () > 0)
+    print_usage ();
+  endif
+  if (!setFig && (min (size (ax)) != 1 || length (ax) != 2))
+    print_usage ();
+  endif
+
+  switch (type)
+    case "sign"
+      colours = [1, 0, 0; 1, 1, 0; 0, 1, 1; 0, 0, 1];
+    case "highlight"
+      colours = [1, 0, 0; 1, 1, 1; 1, 1, 1; 0, 0, 1];
+    case "custom"
+      if (size (colours, 1) != 4 || size (colours, 2) != 3)
+        print_usage ();
+      endif
+    otherwise
+      error ("unknown type of colour map: '%s'", type);
+  endswitch
+
+  if (setFig)
+    ax = caxis ();
+  endif
+
+  % Now comes the real calculation of the map.
+
+  cmap = NA (n, 3);
+
+  if (ax(1) >= 0)
+    nBelow = 0;
+    nAbove = n;
+  elseif (ax(2) <= 0)
+    nBelow = n;
+    nAbove = 0;
+  else
+    assert (ax(1) < 0 && ax(2) > 0);
+    nBelow = round (n * -ax(1) / (ax(2) - ax(1)));
+    nAbove = n - nBelow;
+    assert (nAbove, round (n * ax(2) / (ax(2) - ax(1))), 1);
+  endif
+
+  for i = 1 : nBelow
+    theta = (i - 1) / (nBelow - 1);
+    cmap(i, :) = theta * colours(3, :) + (1 - theta) * colours(4, :);
+  endfor
+
+  for i = 1 : nAbove
+    theta = (i - 1) / (nAbove - 1);
+    cmap(i + nBelow, :) = theta * colours(1, :) + (1 - theta) * colours(2, :);
+  endfor
+
+  if (setFig)
+    colormap (cmap);
+  endif
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% The tests check the various usage patterns, but not really
+% the functionality.  This can be best checked by a visual inspection
+% of the demos.
+
+%!shared colours
+%!  colours = [1, 1, 1; 0, 0, 0; 1, 1, 1; 0, 0, 0];
+
+% Test usage of the "first form".
+%!error <Invalid call to>
+%!  ls_sign_colourmap (1, 2);
+%!error <Invalid call to>
+%!  ls_sign_colourmap (1);
+%!error <Invalid call to>
+%!  cmap = ls_sign_colourmap ();
+%!error <unknown type of colour map>
+%!  ls_sign_colourmap ("foo");
+%!test
+%!  figure ();
+%!  ls_sign_colourmap ();
+%!  ls_sign_colourmap ("highlight");
+%!  ls_sign_colourmap (colours);
+%!  close ();
+
+% Test usage of the "second form".
+%!error <Invalid call to>
+%!  ls_sign_colourmap (1, 2, 3);
+%!error <Invalid call to>
+%!  ls_sign_colourmap ([1, 2, 3]);
+%!error <Invalid call to>
+%!  ls_sign_colourmap ([1, 2], 1);
+%!error <unknown type of colour map>
+%!  ls_sign_colourmap ([1, 2], "foo");
+%!test
+%!  cmap = ls_sign_colourmap ([1, 2]);
+%!  cmap = ls_sign_colourmap ([1; 2], "highlight");
+%!  cmap = ls_sign_colourmap ([1; 2], colours);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+% Demo function for the "sign" map.
+%!demo
+%!  n = 100;
+%!  x = linspace (-2, 2, n);
+%!  y = linspace (-1, 3, n);
+%!  [XX, YY] = meshgrid (x, y);
+%!  phi = ls_genbasic (XX, YY, "sphere", [0, 1], 1);
+%!
+%!  figure ();
+%!  imagesc (x, y, phi);
+%!  set (gca (), "ydir", "normal");
+%!  ls_sign_colourmap ();
+%!  colorbar ();
+%!  title ("Type 'sign'");
+
+% Demo function for the "highlight" version.
+%!demo
+%!  n = 100;
+%!  x = linspace (-2, 2, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi = ls_genbasic (XX, YY, "sphere", [0, 0], 1);
+%!  d = ls_signed_distance (phi, h);
+%!  F = YY .* exp (-10 * d.^2);
+%!
+%!  figure ();
+%!  hold ("on");
+%!  imagesc (x, x, F);
+%!  set (gca (), "ydir", "normal");
+%!  ls_sign_colourmap ("highlight");
+%!  colorbar ();
+%!  contour (XX, YY, phi, [0, 0], "k", "LineWidth", 2);
+%!  hold ("off");
+%!  title ("Type 'highlight'");
diff --git a/inst/ls_signed_distance.m b/inst/ls_signed_distance.m
new file mode 100644
index 0000000..6559216
--- /dev/null
+++ b/inst/ls_signed_distance.m
@@ -0,0 +1,144 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{d} =} ls_signed_distance (@var{phi}, @var{h} = 1)
+## 
+## Calculate the signed distance function of a set described by its level-set
+## function.  The array @var{phi} must contain the values of the level-set
+## function on a rectangular grid with spacing @var{h}.
+##
+## The initial distances are approximated using @code{ls_init_narrowband},
+## and then @code{fastmarching} is used to propagate them to all other points
+## on the grid.
+##
+## It may be a good idea to use @code{ls_normalise} on the level-set function
+## before using this method, to prevent almost-zero values from underflowing
+## due to the performed calculations.
+##
+## @seealso{fastmarching, ls_distance_fcn, ls_solve_stationary, ls_normalise}
+## @end deftypefn
+
+function d = ls_signed_distance (phi, h = 1)
+  if (nargin () < 1 || nargin () > 2)
+    print_usage ();
+  endif
+
+  % We work with all positive distances for the beginning.  This does not
+  % hurt, as points in the interior can only be reached by points on the
+  % inner side of the boundary, and vice versa.
+  d = __levelset_internal_init_narrowband (phi, h);
+  f = ones (size (phi));
+  d = fastmarching (d, h * f);
+
+  % Fix the sign.
+  d = ls_copy_sign (d, phi);
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_signed_distance ()
+%!error <Invalid call to>
+%!  ls_signed_distance (1, 2, 3)
+
+% Check 0D case.
+%!test
+%!  assert (ls_signed_distance ([]), []);
+
+% Check 1D case, for which the expected result is trivial to calculate
+% and should be almost exact.
+%!test
+%!  n = 10;
+%!  x = linspace (-2, 2, n);
+%!  h = x(2) - x(1);
+%!  phi = abs (x) - 1;
+%!
+%!  d = ls_signed_distance (phi, h);
+%!  assert (d, phi, sqrt (eps));
+
+% Test with circular region in 3D.
+%!test
+%!  n = 50;
+%!  x = linspace (-2, 2, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY, ZZ] = ndgrid (x);
+%!  RRsq = XX.^2 + YY.^2 + ZZ.^2;
+%!  phi = RRsq - 1;
+%!
+%!  d = ls_signed_distance (phi, h);
+%!  assert (d, sqrt (RRsq) - 1, h);
+
+% Ensure that a previous error with numeric underflow for very small but
+% non-zero phi values is now fixed.
+%!test
+%!  phi = [5e-6, 1.7e-154, -1.3e-2];
+%!  for h = [0.02, 0.01, 0.005]
+%!    d = ls_signed_distance (phi, h);
+%!    assert (all (isfinite (d)));
+%!    assert (d(2), 0);
+%!  endfor
+
+% Check that ls_signed_distance together with phi normalisation
+% produces a level-set domain equivalent to the original one.
+%!test
+%!  phis = ls_get_tests ();
+%!  for i = 1 : length (phis)
+%!    phi = ls_normalise (phis{i});
+%!    d = ls_signed_distance (phi);
+%!    assert (ls_equal (d, phi));
+%!  endfor
+
+% Check for proper handling of signed zeros.
+%!test
+%!  if (exist ("signbit") == 5)
+%!    phi = [-0, 0];
+%!    d = ls_signed_distance (phi);
+%!    assert (ls_equal (phi, d));
+%!  else
+%!    warning ("'signbit' function not available, skipping test.");
+%!  endif
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+% Use ls_signed_distance to "normalise" a very irregularly scaled
+% initial level-set function.
+%!demo
+%!  n = 100;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi0 = atan (ls_union ((XX.^2 + (YY - 5).^2) - 20, ...
+%!                         (XX.^2 + (YY + 5).^2) - 20));
+%!  %phi0 = XX.^2 / 1.3^2 + YY.^2 - 1;
+%!  d = ls_signed_distance (phi0, h);
+%!
+%!  figure ();
+%!  subplot (1, 2, 1);
+%!  contour (XX, YY, phi0);
+%!  title ("Initial");
+%!  colorbar ();
+%!  axis ("equal");
+%!
+%!  subplot (1, 2, 2);
+%!  contour (XX, YY, d);
+%!  title ("Signed Distance");
+%!  colorbar ();
+%!  axis ("equal");
diff --git a/inst/ls_solve_stationary.m b/inst/ls_solve_stationary.m
new file mode 100644
index 0000000..8218952
--- /dev/null
+++ b/inst/ls_solve_stationary.m
@@ -0,0 +1,299 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{d} =} ls_solve_stationary (@var{phi}, @var{f}, @var{h} = 1)
+## @deftypefnx {Function File} {@var{d} =} ls_solve_stationary (@var{phi}, @var{f}, @var{h} = 1, @var{nb})
+## 
+## Solve a generalised Eikonal equation with speeds of arbitrary
+## signs.  The equation solved is
+## @tex
+## \begin{equation*}
+##   f \left| \nabla d \right| = 1
+## \end{equation*}
+## @end tex
+## @ifnottex
+##
+## @example
+## f | grad d | = 1
+## @end example
+##
+## @end ifnottex
+## with d = 0 on the boundary.  The domain is described by the level-set
+## function @var{phi} on a rectangular grid.  @var{f} should contain the values
+## of the speed field on the grid points.  @var{h} can be given as the
+## grid spacing.  In the second form, where the optional @var{nb} is given,
+## it is used to initialise the narrow band with a manual calculation.
+## By default, the result of @code{ls_init_narrowband} is used.  Values which
+## are not fixed by the narrow band should be set to @code{NA}.
+##
+## Note that in comparison to @code{fastmarching}, the speed need not be
+## positive.  It is the reciprocal of @var{f} in @code{fastmarching}.
+##
+## This is a preparation step, and afterwards, the evolved geometry according
+## to the level-set equation
+## @tex
+## \begin{equation*}
+##   \phi_t + f \left| \nabla \phi \right| = 0
+## \end{equation*}
+## @end tex
+## @ifnottex
+##
+## @example
+## d/dt phi + f | grad phi | = 0
+## @end example
+##
+## @end ifnottex
+## can be extracted from @var{d} at arbitrary positive times using
+## the supplemental function @code{ls_extract_solution}.
+##
+## At points where @var{f} is exactly zero, the output will be set
+## to @code{NA}.  This case is handled separately in @code{ls_extract_solution}.
+## In the narrow band, the returned distances may actually be negative
+## even for positive @var{f} and vice-versa.  This helps to avoid
+## unnecessary errors introduced into the level-set function due to
+## a finite grid-size when the time step is chosen small.
+##
+## @seealso{ls_extract_solution, ls_signed_distance, ls_nb_from_geom}
+## @end deftypefn
+
+function d = ls_solve_stationary (phi, f, h = 1, nb)
+  if (nargin () < 2 || nargin () > 4)
+    print_usage ();
+  endif
+
+  sz = size (phi);
+  if (~all (sz == size (f)))
+    error ("PHI and F must be of the same size");
+  endif
+
+  if (nargin () < 4)
+    nb = ls_init_narrowband (phi, h);
+  elseif (~all (sz == size (nb)))
+    error ("PHI and NB must be of the same size");
+  endif
+
+  % Find regions of signs of f.  Below for the main calculations,
+  % we will always ignore f = 0 regions and will mostly work with
+  % the absolute value of f, fixing the sign of d at the end.
+  fp = (f > 0);
+  fz = (f == 0);
+  fn = (f < 0);
+  fabs = abs (f);
+
+  % Find regions of the domain.
+  inner = (phi < 0);
+  outer = (phi > 0);
+
+  % Initialise narrow-band distances.  Scale them (roughly) according
+  % to our f.
+  d0 = nb;
+  d0(~fz) ./= fabs(~fz);
+
+  % Handle points where the front moves away (depending on the signs of
+  % phi and f).  In theory, these distances should be zero according to
+  % the definition in the theoretical paper.  Set them to Inf or -Inf
+  % in the numerics for points deeply inside, and leave the narrow-band
+  % values for points near the boundary.  Since fastmarching does not
+  % allow -Inf, choose the signs such that the points will be passed with
+  % positive Inf to fastmarching.  The sign will be reverted later
+  % on to get the value matching the distance semantics (-Inf inside with
+  % front moving away and Inf outside).
+  % For phi = 0, nothing special needs to be done as d0 is set to zero there
+  % already by internal_init_narrowband.
+  d0(isna (d0) & inner & fp) = Inf;
+  d0(isna (d0) & outer & fn) = -Inf;
+  assert (all (isna (d0(:)) | ~isnan(d0(:))));
+
+  % Solve for both parts.  Invert the negative one's distances.  Also for
+  % the "islands" with -Inf there, this is fine, as islands mean that the
+  % respective points are never reached by the front, thus never taken
+  % out of the domain, and thus D < -t should always be true for them
+  % and all arbitrarily large times t.
+  dp = solvePart (d0, fp, h, fabs);
+  % Be careful with -NA ~= NA!
+  invD0 = -d0;
+  invD0(isna (d0)) = NA;
+  dn = -solvePart (invD0, fn, h, fabs);
+
+  % Build everything together.
+  d = NA (size (phi));
+  d(fp) = dp(fp);
+  d(fn) = dn(fn);
+endfunction
+
+%   D = solvePart (D0, REGION, H, FABS)
+%
+% Utility function that performs one of the two solves required for the
+% regions with positive and negative f.  D0 should be the initial distances
+% (all positive), REGION a boolean flag of the region we process (f > 0
+% or f < 0), H the grid spacing and FABS the absolute value of f.
+%
+% We solve in the given REGION using fast marching for the distances
+% of points that are eventually (but not initially) reached by the front,
+% and also handle the case of never-reached "islands".
+
+function d = solvePart (d0, region, h, fabs)
+  d = d0;
+  d(~region) = Inf;
+  fval = NA (size (fabs));
+  fval(region) = h ./ fabs(region);
+  d = fastmarching (d, fval);
+
+  % Set distances from which the front moved away from Inf to -Inf.
+  d(d == Inf & region) = -Inf;
+
+  % If a region is not reached by the fast marching (i. e., NA), this means
+  % that it is separated from the boundary by a sign change of f.  This also
+  % means that it will actually never be reached, and thus we can set its
+  % distance to Inf.
+  d(isna (d)) = Inf;
+
+  assert (all (~isnan (d(:))));
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_solve_stationary (1)
+%!error <Invalid call to>
+%!  ls_solve_stationary (1, 2, 3, 4, 5)
+%!error <PHI and F must be of the same size>
+%!  ls_solve_stationary ([1, 2], [3; 4]);
+%!error <PHI and NB must be of the same size>
+%!  ls_solve_stationary ([1, 2], [1, 2], :, [3; 4]);
+
+% Check 0D case.
+%!test
+%!  assert (ls_solve_stationary ([], []), []);
+
+% Check invariants expected for the output in terms
+% of finite/infinite values and signs.
+%!function checkOutput (phi, f, d)
+%!  assert (all (isna (d(f == 0))));
+%!  where = (f ~= 0);
+%!  assert (all (isfinite (d(where)) | isinf (d(where))));
+%!  assert (sign (d(where)), sign (phi(where)));
+%!endfunction
+
+% For the test phi's, just check that solving works
+% without any errors.
+%!test
+%!  phis = ls_get_tests ();
+%!  for i = 1 : length (phis)
+%!    f = ones (size (phis{i}));
+%!    for j = -1 : 1
+%!      phi = ls_normalise (phis{i});
+%!      d = ls_solve_stationary (phi, j * f);
+%!      checkOutput (phi, j * f, d);
+%!    endfor
+%!  endfor
+
+% Test for island handling and the general output conditions
+% (that phi = 0 leads to D = NA and otherwise we only ever get
+% some ordinary values with signs depending on the sign of f
+% or infinities with appropriate signs).
+%
+% The constructed example contains in each half-plane (left / right of the
+% y-axis) an initial circle as domain, and a "ring" around the domain boundary
+% where f is positive or negative.  This ensures we have a sign change of f
+% both in the interior and exterior of the initial domain.  Depending on the
+% sign of f (and thus the half-plane), one of them will be an "island".
+%
+%!test
+%!  n = 101;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  r1 = (XX - 5).^2 + YY.^2;
+%!  r2 = (XX + 5).^2 + YY.^2;
+%!
+%!  phi = ls_union (r1 - 2^2, r2 - 2^2);
+%!  f1 = max (r1 - 3^2, 1^2 - r1);
+%!  f2 = max (r2 - 3^2, 1^2 - r2);
+%!  f = min (f1, f2) .* sign (XX);
+%!  assert (~all (f(:) ~= 0));
+%!
+%!  d = ls_solve_stationary (phi, f, h);
+%!
+%!  checkOutput (phi, f, d);
+%!  where = (f ~= 0);
+%!  assert (any (isinf (d(where))));
+%!
+%!  %{
+%!  contour (phi, [0, 0]);
+%!  colorbar ();
+%!  figure ();
+%!  imagesc (f);
+%!  colorbar ();
+%!  figure ();
+%!  imagesc (d);
+%!  colorbar ();
+%!  %}
+
+% Test manual NB initialisation.
+%!test
+%!  phi = [3, 1, -1, 1, 3];
+%!  f = ones (size (phi));
+%!  dists1 = ls_solve_stationary (phi, f);
+%!  dists2 = ls_solve_stationary (phi, f, :, [NA, NA, -0.1, NA, NA]);
+%!
+%!  assert (dists1, [1.5, 0.5, -0.5, 0.5, 1.5], sqrt (eps));
+%!  assert (dists2, [1.9, 0.9, -0.1, 0.9, 1.9]);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+% Visualise refraction and diffraction of a wave front.  If we only
+% use positive speeds, then the different times can be plotted nicely
+% just with 'contour'.
+%
+%!demo
+%!  n = 100;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi0 = YY - XX + 15;
+%!  f = sign (XX) + 2;
+%!
+%!  d = ls_solve_stationary (phi0, f, h);
+%!  figure ();
+%!  contour (XX, YY, d);
+%!  line ([0, 0], [-10, 10]);
+%!  title ("Refraction");
+%
+%!demo
+%!  n = 101;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi0 = XX + 9;
+%!  f = ones (size (phi0));
+%!  f(XX == 0 & abs (YY) > 2) = 0;
+%!
+%!  d = ls_solve_stationary (phi0, f, h);
+%!  figure ();
+%!  contour (XX, YY, d);
+%!  line ([0, 0], [-10, -2]);
+%!  line ([0, 0], [2, 10]);
+%!  title ("Diffraction");
+
+% XXX: Can we use this for ray-tracing as a nice example???
diff --git a/inst/ls_time_step.m b/inst/ls_time_step.m
new file mode 100644
index 0000000..4b5d10f
--- /dev/null
+++ b/inst/ls_time_step.m
@@ -0,0 +1,253 @@
+##  Copyright (C) 2015  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{phit} =} ls_time_step (@var{t}, @var{phi0}, @var{f}, @var{h} = 1)
+## @deftypefnx {Function File} {@var{phit} =} ls_time_step (@var{t}, @var{c}, @var{phi0}, @var{f}, @var{h} = 1)
+## 
+## Evolve the level-set equation with time stepping.  Perform explicit time
+## stepping on the equation
+## @tex
+## \begin{equation*}
+##    \phi_t + f \left| \nabla \phi \right| = 0.
+## \end{equation*}
+## @end tex
+## @ifnottex
+##
+## @example
+## d/dt phi + f | grad phi | = 0.
+## @end example
+##
+## @end ifnottex
+## The initial value is given by @var{phi0}.  @var{phi0} and @var{f} must
+## be of the same size.  If @var{h} is present, it sets the spatial grid size.
+## The time stepping uses @code{upwind_gradient_norm} for the evaluation
+## of the gradient norm at each step.
+##
+## @var{t} is the time (or vector of times) at which the solution should
+## be returned.  If more than one time is given, the result @var{phit}
+## will be a cell array of the evolved level-set functions at each of
+## the requested time points.  If @var{t} is a scalar, @var{phit} is
+## returned as array of the same size of @var{phi0}.
+##
+## The time step is chosen to satisfy the Courant-Friedrichs-Lewy condition
+## @tex
+## \begin{equation*}
+##    \Delta t = c \frac{h}{F_m n}.
+## \end{equation*}
+## @end tex
+## @ifnottex
+##
+## @example
+## dt = c h / (Fm * n).
+## @end example
+##
+## @end ifnottex
+## Here, Fm is the maximum absolute value in @var{f}, and n is the number
+## of space dimensions.  The desired ratio c is by default one, but can be
+## passed explicitly with the second usage form.
+##
+## @seealso{ls_extract_solution, upwind_gradient_norm}
+## @end deftypefn
+
+function phit = ls_time_step (t, second, third, fourth, fifth)
+  c = 1;
+  h = 1;
+  switch nargin ()
+    case 3
+      phi0 = second;
+      F = third;
+    case 4
+      if (isscalar (second))
+        c = second;
+        phi0 = third;
+        F = fourth;
+      else
+        phi0 = second;
+        F = third;
+        h = fourth;
+      endif
+    case 5
+      c = second;
+      phi0 = third;
+      F = fourth;
+      h = fifth;
+    otherwise
+      print_usage ();
+  endswitch
+
+  if (!isscalar (h))
+    print_usage ();
+  endif
+
+  if (~all (size (phi0) == size (F)))
+    error ("PHI0 and F must be of the same size");
+  endif
+
+  if (!isvector (t))
+    error ("T must be a vector or scalar");
+  endif
+
+  Fm = norm (F(:), Inf);
+  n = sum (size (phi0) > 1);
+  dt = c * h / (Fm * n);
+
+  phit = cell (length (t), 1);
+
+  phi = phi0;
+  curT = 0;
+  nextTind = 1;
+
+  while (nextTind <= length (t))
+    gradNorm = upwind_gradient_norm (phi, F, h);
+
+    dNextT = t(nextTind) - curT;
+    if (dNextT < 0)
+      error ("T must be ascending");
+    endif
+
+    if (dNextT <= dt)
+      phi -= dNextT * F .* gradNorm;
+      curT += dNextT;
+      assert (curT, t(nextTind));
+      phit{nextTind} = phi;
+      ++nextTind;
+    else
+      phi -= dt * F .* gradNorm;
+      curT += dt;
+    endif
+  endwhile
+
+  if (length (phit) == 1)
+    phit = phit{1};
+  endif
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_time_step (1, 2)
+%!error <Invalid call to>
+%!  ls_time_step (1, 2, 3, 4, 5, 6)
+%!error <Invalid call to>
+%!  ls_time_step (1, 2, 3, 4, [5, 5])
+%!error <PHI0 and F must be of the same size>
+%!  ls_time_step ([1; 1], [2, 2], [3, 3, 3])
+%!error <PHI0 and F must be of the same size>
+%!  ls_time_step ([1, 1], 1, [2, 2], [3, 3, 3])
+%!error <T must be a vector or scalar>
+%!  ls_time_step (ones (2, 2), 1, 1)
+
+% Test a monotone profile is shifted when we have constant speed.
+% Also test basic usage.
+%!test
+%!  n = 100;
+%!  x = linspace (0, 1, n);
+%!  h = x(2) - x(1);
+%!
+%!  phi0 = sin (x);
+%!  fPos = ones (size (phi0));
+%!
+%!  % Single time step, moving exactly one grid step.
+%!  phit = ls_time_step (1, phi0, fPos);
+%!  assert (phit(2 : end), phi0(1 : end - 1));
+%!
+%!  % t = 1 should correspond to the previous result, and t = 2
+%!  % shifts one more time step.  However, due to boundary effects,
+%!  % we have to exclude some elements in the comparisons.
+%!  phitArr = ls_time_step ([1, 2], 0.5, phi0, fPos);
+%!  assert (size (phitArr), [2, 1]);
+%!  assert (phitArr{1}(3 : end), phit(3 : end), 1e-4);
+%!  assert (phitArr{2}(5 : end), phi0(3 : end - 2), 1e-4);
+%!
+%!  % Try out movement in the other direction.
+%!  phit = ls_time_step (0.5, 0.1, phi0, -fPos, 0.5);
+%!  assert (phit(1 : end - 4), phi0(2 : end - 3), 1e-4);
+
+% Compare evolution to the fast marching method.
+%!function compareFastMarching (t, phi0, F, h, tol)
+%!  phit = ls_time_step (t, phi0, F, h);
+%!  d = ls_solve_stationary (phi0, F, h);
+%!  phit_fm = ls_extract_solution (t, d, phi0, F);
+%!
+%!  sd1 = ls_signed_distance (phit, h);
+%!  sd2 = ls_signed_distance (phit_fm, h);
+%!  assert (sd1, sd2, tol);
+%!endfunction
+
+% Run comparison tests between fast marching and time stepping.
+%!test
+%!  warning ("off", "level-set:fast-marching:too-far-alive");
+%!
+%!  n = 100;
+%!  x = linspace (-5, 5, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi0 = ls_genbasic (XX, YY, "sphere", [0, 0], [3, 1.5]);
+%!  F = ones (size (phi0));
+%!  compareFastMarching (0.5, phi0, F, h, h / 2);
+%!  compareFastMarching (1.5, phi0, F, h, h / 2);
+%!
+%!  phi0 = ls_genbasic (XX, YY, "box", [-3, -3], [3, 3]);
+%!  F = YY / 5;
+%!  compareFastMarching (1, phi0, F, h, h);
+%!
+%!  F = ones (size (phi0));
+%!  compareFastMarching (1, phi0, F, h, h / 2);
+%!
+%!  phi0 = ls_genbasic (XX, YY, "sphere", [0, 0], 3);
+%!  F = sin (XX .* YY);
+%!  compareFastMarching (1, phi0, F, h, h);
+
+% Try also a 3D problem.
+%!test
+%!  warning ("off", "level-set:fast-marching:increased-distance");
+%!
+%!  n = 50;
+%!  x = linspace (-5, 5, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY, ZZ] = ndgrid (x, x, x);
+%!
+%!  phi0 = ls_genbasic (XX, YY, ZZ, "sphere", [0, 0, 0], 3);
+%!  F = ones (size (phi0));
+%!  compareFastMarching (1, phi0, F, h, h / 2);
+%!  compareFastMarching (1, phi0, -F, h, h / 2);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demos.
+
+%!demo
+%!  n = 500;
+%!  x = linspace (-5, 5, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  F = sin (XX .* YY);
+%!  phi0 = ls_genbasic (XX, YY, "sphere", [0, 0], 3);
+%!  phit = ls_time_step ([0.5, 1], phi0, F, h);
+%!
+%!  figure ();
+%!  hold ("on");
+%!  imagesc (x, x, F);
+%!  set (gca (), "ydir", "normal");
+%!  ls_sign_colourmap ();
+%!  contour (XX, YY, phi0, [0, 0], "k", "LineWidth", 2);
+%!  contour (XX, YY, phit{1}, [0, 0], "k", "LineWidth", 2);
+%!  contour (XX, YY, phit{2}, [0, 0], "k", "LineWidth", 2);
+%!  hold ("off");
diff --git a/inst/ls_union.m b/inst/ls_union.m
new file mode 100644
index 0000000..18b334b
--- /dev/null
+++ b/inst/ls_union.m
@@ -0,0 +1,86 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{phi} =} ls_union (@var{phi1}, @var{phi2})
+## @deftypefnx  {Function File} {@var{phi} =} ls_union (@var{phi}, ...)
+## 
+## Calculate a level-set function for the union of the sets described
+## by the argument level-set functions.
+##
+## @seealso{ls_complement, ls_intersect, ls_setdiff, ls_setxor, union}
+## @end deftypefn
+
+function res = ls_union (varargin)
+  res = ls_union_intersect (@min, varargin, "ls_union");
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  ls_union ()
+%!error <size mismatch in the arguments>
+%!  ls_union (1, -2, [1, 2])
+
+% Test that the union creates something which contains all pieces.
+%!test
+%!  n = 50;
+%!  x = linspace (-10, 10, n);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi1 = (XX - 3).^2 + (YY - 3).^2 - 2^2;
+%!  phi2 = (XX + 3).^2 + (YY + 3).^2 - 2^2;
+%!  phi3 = XX.^2 + YY.^2 - 2^2;
+%!
+%!  phi = ls_union (phi1, phi2, phi3);
+%!  assert (ls_issubset (phi1, phi));
+%!  assert (ls_issubset (phi2, phi));
+%!  assert (ls_issubset (phi3, phi));
+%!
+%!  assert (ls_union (phi3), phi3);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demo.
+
+%!demo
+%!  n = 100;
+%!  x = linspace (-7, 7, n);
+%!  [XX, YY] = meshgrid (x, x);
+%!
+%!  phi1 = (XX - 2 * cos (7/6 * pi)).^2 + (YY - 2 * sin (7/6 * pi)).^2 - 3^2;
+%!  phi2 = (XX - 2 * cos (11/6 * pi)).^2 + (YY - 2 * sin (11/6 * pi)).^2 - 3^2;
+%!  phi3 = XX.^2 + (YY - 2).^2 - 3^2;
+%!  phi = ls_union (phi1, phi2, phi3);
+%!
+%!  figure ();
+%!  subplot (1, 2, 1);
+%!  hold ("on");
+%!  contour (XX, YY, phi1, [0, 0], "k");
+%!  contour (XX, YY, phi2, [0, 0], "k");
+%!  contour (XX, YY, phi3, [0, 0], "k");
+%!  hold ("off");
+%!  axis ("equal");
+%!
+%!  subplot (1, 2, 2);
+%!  hold ("on");
+%!  imagesc (x, x, phi);
+%!  set (gca (), "ydir", "normal");
+%!  ls_sign_colourmap ();
+%!  contour (XX, YY, phi, [0, 0], "k");
+%!  hold ("off");
+%!  axis ("equal");
diff --git a/inst/maze.png b/inst/maze.png
new file mode 100644
index 0000000..8f64570
Binary files /dev/null and b/inst/maze.png differ
diff --git a/inst/private/ls_copy_sign.m b/inst/private/ls_copy_sign.m
new file mode 100644
index 0000000..17d1092
--- /dev/null
+++ b/inst/private/ls_copy_sign.m
@@ -0,0 +1,81 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{d} =} ls_copy_sign (@var{d}, @var{phi})
+## 
+## Internal routine for taking over the sign of @var{phi} onto @var{d}.  This
+## routine assumes that @var{d} is positive initially.  It copies over the
+## sign of @var{phi} to @var{d}, taking signed zeros into account.
+##
+## @seealso{ls_signed_distance, ls_init_narrowband, ls_nb_from_geom, sign}
+## @end deftypefn
+
+function d = ls_copy_sign (d, phi)
+  if (nargin () != 2)
+    print_usage ();
+  endif
+
+  sz = size (phi);
+  if (!all (size (d) == sz))
+    error ("size mismatch in the arguments");
+  endif
+
+  if (!all (d(:) >= 0 | isna (d(:))))
+    error ("D should initially be positive");
+  endif
+
+  if (exist ("signbit") == 5)
+    where = signbit (phi);
+  else
+    where = (phi < 0);
+  endif
+  d(where & !isna (d)) *= -1;
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for errors.
+%!error <Invalid call to>
+%!  ls_copy_sign (1);
+%!error <Invalid call to>
+%!  ls_copy_sign (1, 2, 3);
+%!error <size mismatch in the arguments>
+%!  ls_copy_sign (1, [2, 3]);
+%!error <D should initially be positive>
+%!  ls_copy_sign (-1, 5);
+
+% Test basic functionality.
+%!test
+%!  d = [0, 1, 2, eps, Inf];
+%!  zeros = (d == 0);
+%!  phi = [-1, 1, 1, -1, -1];
+%!  dp = ls_copy_sign (d, phi);
+%!  assert (abs (dp), d);
+%!  assert (sign (dp(!zeros)), sign (phi(!zeros)));
+
+% Test signed zeros.
+%!test
+%!  if (exist ("signbit") == 5)
+%!    d = [0, 0, 1, 1];
+%!    phi = [-0, 0, -0, 0];
+%!    dp = ls_copy_sign (d, phi);
+%!    assert (signbit (dp), signbit (phi));
+%!    assert (dp, [-0, 0, -1, 1]);
+%!  else
+%!    warning ("'signbit' function not available, skipping test.");
+%!  endif
diff --git a/inst/private/ls_union_intersect.m b/inst/private/ls_union_intersect.m
new file mode 100644
index 0000000..3f5cfba
--- /dev/null
+++ b/inst/private/ls_union_intersect.m
@@ -0,0 +1,43 @@
+##  Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{phi} =} ls_union_intersect (@var{fcn}, @var{phis}, @var{fcnName})
+## 
+## Internal routine to calculate intersection or union.  @var{fcn} should be
+## a handle to either @code{min} or @code{max}.  @var{phis} is a cell-array
+## of level-set functions.
+##
+## @seealso{ls_union, ls_intersect}
+## @end deftypefn
+
+function res = ls_union_intersect (fcn, phis, fcnName)
+  if (length (phis) < 1)
+    print_usage (fcnName);
+  endif
+
+  sz = size (phis{1});
+  for i = 2 : length (phis)
+    if (!all (size (phis{i}) == sz))
+      error ("size mismatch in the arguments");
+    endif
+  endfor
+
+  res = phis{1};
+  for i = 2 : length (phis)
+    res = fcn (res, phis{i});
+  endfor
+endfunction
diff --git a/inst/private/so_load_compressed.m b/inst/private/so_load_compressed.m
new file mode 100644
index 0000000..7786d43
--- /dev/null
+++ b/inst/private/so_load_compressed.m
@@ -0,0 +1,35 @@
+##  Copyright (C) 2015  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{x} =} so_load_compressed (@var{fd}, @var{field}, @var{data})
+## 
+## Internal routine that loads and optionally ``uncompresses'' data
+## from a descent log.  This is used by both @code{so_replay_descent}
+## and @code{so_explore_descent} and thus shared here.
+##
+## @seealso{so_replay_descent, so_explore_descent}
+## @end deftypefn
+
+function x = so_load_compressed (fd, field, data)
+  x = fload (fd);
+
+  if (isfield (data, "compress") && isfield (data.compress, "load")
+        && isfield (data.compress.load, field))
+    fcn = getfield (data.compress.load, field);
+    x = fcn (x, data);
+  endif
+endfunction
diff --git a/inst/so_example_problem.m b/inst/so_example_problem.m
new file mode 100644
index 0000000..2586edd
--- /dev/null
+++ b/inst/so_example_problem.m
@@ -0,0 +1,309 @@
+##  Copyright (C) 2015  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{data} =} so_example_problem (@var{data})
+##
+## Construct an example problem for shape optimisation.  The example problem
+## is one-dimensional.  Roughly speaking, the cost is chosen such that
+## the optimal solution should be symmetric around the origin and
+## have a determined volume.
+##
+## The parameters in @code{@var{data}.p} as well as the basic grid in
+## @code{@var{data}.g} should already be set.  This routine fills in
+## the necessary callbacks to compute the cost and to find a descent direction.
+##
+## These parameters in @code{@var{data}.p} are used to refine the
+## example problem:
+##
+## @table @code
+## @code vol
+## The desired volume.  Deviations from this volume will be penalised in the
+## cost functional.
+##
+## @code weight
+## Weight of the volume penalisation term.  This is relative to the term
+## that penalises non-symmetry and should be relatively large.
+## @end table
+##
+## Note that these routines assume (for simplicity) that the level-set domain
+## will always be a single interval.  Thus, the initial domain should
+## fulfil this assumption.  (It will then automatically stay that way
+## even when the shape is evolved.)
+##
+## In addition to the callbacks, also utility routines will be returned
+## that can be used in handlers for plotting.  They will be set
+## in @code{@var{data}.util}:
+##
+## @table @code
+## @item [@var{a}, @var{b}] = bounds (@var{phi}, @var{data})
+## Find the left and right bounds of the (assumed) interval that the
+## level-set function @var{phi} describes.  This makes use of the
+## grid in @code{@var{data}.g}.  The bounds will also be filled into
+## the state struct as @code{@var{s}.a} and @code{@var{s}.b}.
+## @end table
+## 
+## @seealso{so_run_descent}
+## @end deftypefn
+
+function data = so_example_problem (data)
+  if (nargin () != 1)
+    print_usage ();
+  endif
+  if (!isstruct (data))
+    error ("DATA should be a struct");
+  endif
+
+  if (!isfield (data, "figs"))
+    data.figs = struct ();
+  endif
+
+  data.util = struct ("bounds", @bounds);
+  data.cb = struct ("update_state", @updateState, ...
+                    "get_direction", @getDirection);
+  data.handler = struct ("direction", @plotSpeed);
+
+  % Set also an "onFirst" handler for so_explore_descent.
+  % This handler plots all (already known) costs as we go.
+  if (isfield (data.figs, "exploreCosts"))
+    data.onFirst = struct ("before_step", @plotCosts);
+  endif
+
+  holdall = true (size (data.g.x));
+  holdall([1, end]) = false;
+  data.g.constraints = struct ("holdall", holdall);
+endfunction
+
+% Cost function of the example problem.
+function s = updateState (phi, data)
+  s = struct ("cost", 0);
+
+  % Disallow empty domain.
+  if (ls_isempty (phi))
+    s.cost = Inf;
+    return;
+  endif
+
+  % Penalise deviations from the desired volume.
+  [s.a, s.b] = bounds (phi, data);
+  s.vol = s.b - s.a;
+  assert (s.vol > 0);
+  s.cost += data.p.weight * (s.vol - data.p.vol)^2;
+
+  % Penalise non-symmetry.  This is done by integrating over x.
+  where = ls_inside (phi);
+  s.integ = data.g.h * sum (data.g.x(where));
+  s.cost += s.integ^2;
+endfunction
+
+% Compute shape derivative and gradient.
+function [f, dJ] = getDirection (data)
+
+  % Find derivatives at the end points (assuming F = 1).
+  bdry = [data.s.a, data.s.b];
+  deriv = zeros (size (bdry));
+  for i = 1 : length (bdry)
+    deriv(i) += 2 * data.p.weight * (data.s.vol - data.p.vol);
+    deriv(i) += 2 * data.s.integ * bdry(i);
+  endfor
+
+  % Use derivatives at the end points to compute dJ.  Furthermore,
+  % build the descent direction as a linear interpolation of the
+  % end-point derivatives.
+
+  f = NA (size (data.g.x));
+
+  left = (data.g.x < data.s.a);
+  fact = (data.g.x(left) - data.g.x(1)) ./ (data.s.a - data.g.x(1));
+  f(left) = -deriv(1) * fact;
+
+  mid = (data.g.x >= data.s.a & data.g.x <= data.s.b);
+  fact = (data.g.x(mid) - data.s.a) ./ (data.s.b - data.s.a);
+  f(mid) = -deriv(2) * fact - (1 - fact) * deriv(1);
+
+  right = (data.g.x > data.s.b);
+  fact = (data.g.x(end) - data.g.x(right)) ./ (data.g.x(end) - data.s.b);
+  f(right) = -deriv(2) * fact;
+
+  assert (all (isfinite (f)));
+  dJ = -dot (deriv, deriv);
+endfunction
+
+% Handler to plot speed and the current situation.
+function log = plotSpeed (k, f, dJ, data)
+  fmin = min (f);
+  fmax = max (f);
+
+  if (!isfield (data.figs, "speed"))
+    figure ();
+  else
+    figure (data.figs.speed);
+    clf ();
+  endif
+  hold ("on");
+  plot (data.g.x, f, "b");
+  plot ([-5, -5], [fmin, fmax], "k");
+  plot ([data.s.a, data.s.a], [fmin, fmax], "r");
+  plot ([5, 5], [fmin, fmax], "k");
+  plot ([data.s.b, data.s.b], [fmin, fmax], "r");
+  hold ("off");
+  axis ([data.g.x(1), data.g.x(end), fmin, fmax]);
+  legend ("Speed", "Target", "Current");
+  title (sprintf ("Step %d", k));
+
+  log = data.log;
+endfunction
+
+% Plot the currently known costs.  This is used for so_explore_descent.
+function log = plotCosts (k, data)
+  assert (k <= data.log.steps);
+
+  figure (data.figs.exploreCosts);
+  clf ();
+  costs = postpad (data.log.costs, data.p.nSteps + 1, NA);
+  semilogy (0 : data.p.nSteps, costs, "o");
+  axis ([0, data.p.nSteps]);
+  title (sprintf ("Costs up to step %d", data.log.steps));
+
+  log = data.log;
+endfunction
+
+% Bounds utility function.
+function [a, b] = bounds (phi, data)
+  if (ls_isempty (phi))
+    error ("PHI describes empty domain");
+  endif
+  if (!all (size (phi) == size (data.g.x)))
+    error ("PHI and DATA.g.x size mismatch");
+  endif
+
+  % Hack around infinite phi values.  This could be done
+  % nicer by checking for infinite values directly (as it
+  % is done in ls_find_geometry, for instance), but just
+  % do it like this for simplicity.
+  whereInf = !isfinite (phi);
+  phi(whereInf) = sign (phi(whereInf)) * max (abs (phi(!whereInf)));
+
+  insideInd = find (ls_inside (phi));
+  minInd = min (insideInd);
+  maxInd = max (insideInd);
+
+  convexZero = @(phiA, phiB, xA, xB) ...
+                  xA + (xB - xA) * abs (phiA) / (abs (phiA) + abs (phiB));
+
+  if (minInd == 1)
+    a = data.g.x(1);
+  else
+    a = convexZero (phi(minInd - 1), phi(minInd), ...
+                    data.g.x(minInd - 1), data.g.x(minInd));
+  endif
+
+  if (maxInd == length (phi))
+    b = data.g.x(end);
+  else
+    b = convexZero (phi(maxInd), phi(maxInd + 1), ...
+                    data.g.x(maxInd), data.g.x(maxInd + 1));
+  endif
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Build basic data with example grid.
+%!shared basicData
+%!  basicData = struct ();
+%!  basicData.p = so_init_params (true);
+%!  basicData.p.vol = 4;
+%!  basicData.p.weight = 10;
+%!
+%!  n = 50;
+%!  x = linspace (-4, 5, n);
+%!  h = x(2) - x(1);
+%!  basicData.g = struct ("x", x, "h", h);
+
+% Test for errors.
+%!error <Invalid call to>
+%!  so_example_problem ();
+%!error <Invalid call to>
+%!  so_example_problem (1, 2);
+%!error <DATA should be a struct>
+%!  so_example_problem (1);
+
+% Test the bounds routine created.
+%!test
+%!  data = so_example_problem (basicData);
+%!  phi = ls_genbasic (data.g.x, "box", -2.5, 1.3);
+%!
+%!  [a, b] = data.util.bounds (phi, data);
+%!  assert (a, -2.5, sqrt (eps));
+%!  assert (b, 1.3, sqrt (eps));
+
+% Test that the cost works qualitatively as it should.
+%!test
+%!  data = so_example_problem (basicData);
+%!
+%!  phi = ls_genbasic (data.g.x, "box", -2, 2); 
+%!  s0 = data.cb.update_state (phi, data);
+%!
+%!  phi = ls_genbasic (data.g.x, "box", -3, 3); 
+%!  s1 = data.cb.update_state (phi, data);
+%!
+%!  phi = ls_genbasic (data.g.x, "box", -1, 1); 
+%!  s2 = data.cb.update_state (phi, data);
+%!
+%!  phi = ls_genbasic (data.g.x, "box", -1, 3); 
+%!  s3 = data.cb.update_state (phi, data);
+%!
+%!  phi = ls_genbasic (data.g.x, "box", -3, 1); 
+%!  s4 = data.cb.update_state (phi, data);
+%!
+%!  assert (all ([s0.cost, s1.cost, s2.cost, s3.cost, s4.cost] >= 0));
+%!  assert (s0.cost, 0, 1e-1);
+%!  assert (s1.cost > s3.cost);
+%!  assert (s1.cost > s4.cost);
+%!  assert (s2.cost > s3.cost);
+%!  assert (s2.cost > s4.cost);
+%!  assert (s3.cost > s0.cost);
+%!  assert (s4.cost > s0.cost);
+
+% Do a rough finite-difference test for the derivative and ensure
+% that we have a descent direction.
+%
+%!function testDeriv (a, b, data)
+%!  phi = ls_genbasic (data.g.x, "box", a, b);
+%!  data.s = data.cb.update_state (phi, data);
+%!  [f, dJ] = data.cb.get_direction (data);
+%!  assert (dJ < 0);
+%!
+%!  dt = 5e-3;
+%!  dists = ls_solve_stationary (phi, f, data.g.h);
+%!  phip = ls_extract_solution (dt, dists, phi, f);
+%!  sp = data.cb.update_state (phip, data);
+%!
+%!  assert (sp.cost < data.s.cost);
+%!  relErr = abs (sp.cost - (data.s.cost + dt * dJ)) / data.s.cost;
+%!  assert (relErr < 0.1);
+%!endfunction
+%
+%!test
+%!  data = so_example_problem (basicData);
+%!
+%!  testDeriv (-1, 1, data);
+%!  testDeriv (-3, 3, data);
+%!  testDeriv (-1, 3, data);
+%!  testDeriv (-3, 1, data);
+
+% This routine is also "tested" by the tests and demos in so_run_descent.
diff --git a/inst/so_explore_descent.m b/inst/so_explore_descent.m
new file mode 100644
index 0000000..66cae46
--- /dev/null
+++ b/inst/so_explore_descent.m
@@ -0,0 +1,362 @@
+##  Copyright (C) 2015  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {} so_explore_descent (@var{file}, @var{init})
+##
+## Interactively explore a descent file saved by @code{so_save_descent}.
+## This opens a shell where the user can navigate through the replay.
+##
+## The arguments are the same as for @code{so_replay_descent}.  In order
+## to allow backwards navigation, @var{file} must either be a file name
+## or a file descriptor that allows seeking.
+##
+## On the initial call, the @code{initialised} handler is called.
+## Then, @code{before_step}, @code{direction} and @code{after_step}
+## will be called for the currently active data at each navigation
+## of the user.  Note that the descent steps on which they are called may be
+## out-of-order and may skip intermediate iterations.  The @code{finished}
+## handler will be called with the data that is active when the user closes
+## the interactive session.
+##
+## Note that @code{@var{data}.log.steps} and @code{@var{data}.log.costs} will
+## always contain @emph{all} steps and costs that have been loaded during
+## the replay so far, which may not correspond to the currently active step.
+## These fields will be present in all calls to handlers except
+## @code{initialised}, not just for the call to @code{finished}.
+##
+## In addition to the default handlers in @code{handler} of the
+## @var{data} struct, also extra handlers can be defined in
+## @code{@var{data}.onFirst}.  They will be called @emph{in order} and
+## @emph{on each} iteration when it is first loaded from the descent log.
+## The @code{finished} handler will be called on the last iteration data
+## that has been loaded before the session is terminated.  This may not
+## be the active data at that point in time, if the user has navigated
+## backwards.  The @code{onFirst} handlers (if present) will be called before
+## the corresponding @code{handler} functions.
+##
+## No on-the-fly computation of extra steps (as it is done by
+## @code{so_replay_descent} when not enough steps are saved in the log) is
+## supported by this routine.  Instead, it is simply not possible to step
+## beyond EOF of the log file.
+##
+## This routine requires @code{fload} from the @code{parallel} package
+## to be available.
+##
+## @seealso{so_replay_descent, so_run_descent, so_save_descent, fload}
+## @end deftypefn
+
+function so_explore_descent (file, init)
+  if (nargin () != 2)
+    print_usage ();
+  endif
+
+  openFile = ischar (file);
+  if (!openFile && (!isnumeric (file) || !isscalar (file)))
+    error ("FILE must be a string or file ID");
+  endif
+
+  hasFload = exist ("fload");
+  if (hasFload != 2 && hasFload != 3)
+    error ("'fload' is not available");
+  endif
+
+  % Open file and read header.
+  if (openFile)
+    fd = fopen (file, "rb");
+    if (fd == -1)
+      error ("failed to open file '%s' for reading the descent log", file);
+    endif
+  else
+    fd = file;
+  endif
+  header = fload (fd);
+
+  % Call init method on the header.
+  data = init (header);
+
+  % Add some (possibly) missing stuff with defaults.
+  if (!isfield (data, "log"))
+    data.log = struct ();
+  endif
+  if (!isfield (data, "handler"))
+    data.handler = struct ();
+  endif
+  if (!isfield (data, "onFirst"))
+    data.onFirst = struct ();
+  endif
+
+  % Load initial state.  During the entire run, we keep track of fseek
+  % positions for the state *before* each step's begin.  This is used
+  % to later quickly jump to it.  For the first step, this is now.
+  filePos = ftell (fd);
+  data.s = so_load_compressed (fd, "state", data);
+
+  % Call "initialised" handler and save log stuff.
+  data.log.s0 = data.s;
+  if (isfield (data.onFirst, "initialised"))
+    data.log = data.onFirst.initialised (data);
+  endif
+  if (isfield (data.handler, "initialised"))
+    data.log = data.handler.initialised (data);
+  endif
+  data.log.costs = [data.s.cost];
+  data.log.steps = 0;
+
+  % Start the shell loop.
+  k = 0;
+  target = 1;
+  lastN = 1;
+  s = data.s;
+  while (true)
+    if (ferror (fd))
+      error ("error reading descent log");
+    endif
+
+    % Read in sequentially until we reach the target.  Seeking to the
+    % correct previous position as well as reading the previous state
+    % has already been performed -- either above for the initial
+    % step or at the end of the loop after a navigation command.
+    assert (k < target);
+    while (k < target)
+      try
+        f = fload (fd);
+      catch
+        if (feof (fd))
+          break;
+        endif
+        error ("error reading descent log");
+      end_try_catch
+
+      % Now update data.s.  This ensures that data.s is always
+      % set to the correct value (i. e., the old state) when calling
+      % any of the onFirst or handler handlers.  The update should
+      % be done after the EOF check above and before overwriting s with
+      % the newly loaded state below.
+      data.s = s;
+
+      dJ = fload (fd);
+      step = fload (fd);
+      pos = ftell (fd);
+      s = so_load_compressed (fd, "state", data);
+      if (ferror (fd))
+        error ("error reading descent log");
+      endif
+
+      ++k;
+      assert (k <= length (filePos));
+      if (k == length (filePos))
+        assert (data.log.steps == k - 1);
+        data.log.costs(k + 1) = s.cost;
+        data.log.steps = k;
+        assert (data.log.steps == length (filePos));
+        assert (data.log.steps + 1 == length (data.log.costs));
+        filePos(k + 1) = pos;
+
+        if (isfield (data.onFirst, "before_step"))
+          data.log = data.onFirst.before_step (k, data);
+        endif
+        if (isfield (data.onFirst, "direction"))
+          data.log = data.onFirst.direction (k, f, dJ, data);
+        endif
+        if (isfield (data.onFirst, "after_step"))
+          data.log = data.onFirst.after_step (k, step, s, data);
+        endif
+      else
+        assert (filePos(k + 1) == pos);
+      endif
+    endwhile
+
+    % Handle EOF if we have it.
+    assert (k <= target);
+    if (k < target)
+      assert (feof (fd));
+      if (k == 0)
+        error ("no data in descent log");
+      else
+        fprintf (stderr (), "no more steps present in the descent log\n");
+      endif
+    endif
+
+    % Call the actual handlers.
+    if (isfield (data.handler, "before_step"))
+      data.log = data.handler.before_step (k, data);
+    endif
+    if (isfield (data.handler, "direction"))
+      data.log = data.handler.direction (k, f, dJ, data);
+    endif
+    if (isfield (data.handler, "after_step"))
+      data.log = data.handler.after_step (k, step, s, data);
+    endif
+
+    % Prompt for user input.
+    fullExit = false;
+    do
+      try
+        cmd = input (sprintf ("@ step %d> ", k), "s");
+      catch
+        cmd = "exit";
+      end_try_catch
+      if (strcmp (cmd, "exit") || strcmp (cmd, "quit"))
+        fullExit = true;
+        break;
+      endif
+
+      haveValidN = true;
+      relative = true;
+      if (strcmp (cmd, ""))
+        n = lastN;
+      elseif (strcmp (cmd, "start"))
+        n = -k;
+      elseif (strcmp (cmd, "end"))
+        n = Inf;
+      elseif (strcmp (cmd, "help"))
+        fprintf (stderr (), "\nCommands for descent log explorer:\n\n");
+        fprintf (stderr (), "exit    Close the explorer session.\n");
+        fprintf (stderr (), "quit    Close the explorer session.\n");
+        fprintf (stderr (), "help    Display this help message.\n");
+        fprintf (stderr (), "kb      Open Octave shell with 'data' set.\n");
+        fprintf (stderr (), "start   Go to the very first iteration.\n");
+        fprintf (stderr (), "end     Go to the last iteration.\n");
+        fprintf (stderr (), "go N    Go to iteration N.\n");
+        fprintf (stderr (), "N       Go N steps forward.\n");
+        fprintf (stderr (), "-N      Go N steps backward.\n");
+        fprintf (stderr (), "\nIf no command is given, the last navigation");
+        fprintf (stderr (), " command is repeated.\n");
+        haveValidN = false;
+      elseif (strcmp (cmd, "kb"))
+        openKbShell (data);
+        haveValidN = false;
+      else
+        if (length (cmd) >= 4 && strcmp (substr (cmd, 1, 3), "go "))
+          relative = false;
+          cmd = substr (cmd, 4);
+        endif
+
+        n = str2double (cmd);
+        if (isnan (n) || n != fix (n))
+          fprintf (stderr (), "invalid command given\n");
+          haveValidN = false;
+        elseif (relative)
+          lastN = n;
+        endif
+      endif
+    until (haveValidN);
+    if (fullExit)
+      break;
+    endif
+
+    % Find target step number and fix out-of-bounds backward steps.
+    assert (haveValidN);
+    if (relative)
+      target = k + n;
+    else
+      target = n;
+    endif
+    if (target < 1)
+      fprintf (stderr (), "stepping to beginning\n");
+      target = 1;
+    endif
+
+    % If we have to go back, seek to the correct position.
+    if (target <= k)
+      assert (length (filePos) >= target);
+      fseek (fd, filePos(target));
+      k = target - 1;
+      s = fload (fd);
+    endif
+  endwhile
+
+  % Close the file now.
+  if (openFile)
+    fclose (fd);
+  endif
+
+  % Call the finished handlers.
+  if (isfield (data.onFirst, "finished"))
+    data.log = data.onFirst.finished (data);
+  endif
+  if (isfield (data.handler, "finished"))
+    data.log = data.handler.finished (data);
+  endif
+endfunction
+
+% Helper function for the "kb" command.  It creates a new scope
+% where the user can safely play around with data without
+% messing up anything else.
+function openKbShell (data)
+  fprintf (stderr (), "You can now interactively explore the current data.\n");
+  fprintf (stderr (), "Use the variable 'data' to access all information.\n\n");
+  fprintf (stderr (), "Type 'return' to go back to the explore prompt.\n\n");
+  keyboard ("kb> ");
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for errors.
+%!error <Invalid call to>
+%!  so_explore_descent (1);
+%!error <Invalid call to>
+%!  so_explore_descent (1, 2, 3);
+%!error <FILE must be a string or file ID>
+%!  so_explore_descent (struct (), 2);
+%!error <FILE must be a string or file ID>
+%!  so_explore_descent ([2, 3], 2);
+%!error <'fload' is not available>
+%!  pkg unload parallel;
+%!  so_explore_descent ("foo", 2);
+
+% No functional tests, since they require user interaction.
+% Use the demo instead for basic testing.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demo.
+
+%!demo
+%!  pkg load parallel;
+%!
+%!  data = struct ();
+%!  data.p = so_init_params (false);
+%!  data.p.vol = 10;
+%!  data.p.weight = 50;
+%!
+%!  data.p.nSteps = 10;
+%!  data.figs = struct ();
+%!  data.figs.speed = figure ();
+%!  data.figs.exploreCosts = figure ();
+%!
+%!  n = 100;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  data.g = struct ("x", x, "h", h);
+%!
+%!  data = so_example_problem (data);
+%!  phi0 = ls_genbasic (data.g.x, "box", -3, 7);
+%!
+%!  printf ("Computing descent...\n");
+%!  f = tmpfile ();
+%!  d = data;
+%!  d.handler = struct ();
+%!  d = so_save_descent (f, struct (), d);
+%!  s = so_run_descent (data.p.nSteps, phi0, d);
+%!  printf ("Final cost: %.6d\n", s.cost);
+%!
+%!  printf ("\nNow replaying...\n");
+%!  init = @() data;
+%!  frewind (f);
+%!  so_explore_descent (f, init);
+%!  fclose (f);
diff --git a/inst/so_init_params.m b/inst/so_init_params.m
new file mode 100644
index 0000000..257a6a2
--- /dev/null
+++ b/inst/so_init_params.m
@@ -0,0 +1,82 @@
+##  Copyright (C) 2015  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{p} =} so_init_params (@var{verbose}, @var{nProc} = 1)
+##
+## Initialise the parameter structure for the shape optimisation routines.
+## Choose default parameters, and set whether or not ``verbose'' log chatter
+## should be printed.
+##
+## @var{nProc} specifies the number of processes to use for some
+## steps that can be done in parallel (like the line search with
+## @code{so_step_armijo}).  If this number is larger than one,
+## the function @code{pararrayfun} from the @code{parallel} package
+## must be available.
+##
+## The result of this function call can be used as a basis for defining
+## @code{@var{data}.p}.  Values can be overwritten after the call where desired.
+## 
+## @seealso{so_run_descent, so_step_armijo, pararrayfun}
+## @end deftypefn
+
+function p = so_init_params (verbose, nProc = 1)
+  if (nargin () < 1 || nargin () > 2)
+    print_usage ();
+  endif
+
+  if (nProc < 1 || nProc != round (nProc))
+    error ("invalid number of processors");
+  endif
+  hasParallel = exist ("pararrayfun");
+  if (nProc > 1 && hasParallel != 2 && hasParallel != 3)
+    error ("multi-threading requested but 'pararrayfun' is not available");
+  endif
+
+  p = struct ("verbose", verbose, "nProc", nProc);
+
+  p.lineSearch = struct ("relaxation", 0.1, "backtrack", 0.8, ...
+                         "initial", 2, "minStep", 1e-6);
+
+  p.descent = struct ("initialStep", 1);
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  so_init_params ()
+%!error <Invalid call to>
+%!  so_init_params (true, 1, 2)
+%!error <invalid number of processors>
+%!  so_init_params (true, 0);
+%!error <invalid number of processors>
+%!  so_init_params (true, 1.5);
+
+% Check the test for loaded pararrayfun.
+%!test
+%!  p = so_init_params (true);
+%!error <'pararrayfun' is not available>
+%!  pkg unload parallel;
+%!  p = so_init_params (true, 2);
+%!test
+%!  pkg load parallel;
+%!  p = so_init_params (true, 2);
+
+% Since so_init_params is used in the tests of the other
+% routines, they already verify that the parameters
+% are accepted by them.
diff --git a/inst/so_replay_descent.m b/inst/so_replay_descent.m
new file mode 100644
index 0000000..c10b5ac
--- /dev/null
+++ b/inst/so_replay_descent.m
@@ -0,0 +1,479 @@
+##  Copyright (C) 2015  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {[@var{s}, @var{log}] =} so_replay_descent (@var{file}, @var{init})
+## @deftypefnx {Function File} {[@var{s}, @var{log}] =} so_replay_descent (@var{file}, @var{init}, @var{nSteps})
+##
+## Replay a descent file saved by @code{so_save_descent}.  @var{file} should
+## be either an already open file descriptor or a file name to be opened
+## in @qcode{"rb"} mode.
+##
+## @var{init} must be a function that can be called with the ``header''
+## saved in the descent log.  It should return the @var{data} structure
+## to use for the descent run.  The handlers defined in
+## @code{@var{data}.handler} will be called in the same way as they
+## would by @code{so_run_descent}.  Also similar log chatter will be printed
+## if @code{@var{data}.p.verbose} is @code{true}.
+##
+## If the state structs were compressed when saving with @code{so_save_descent},
+## a routine should be provided to ``uncompress'' them as necessary.  This
+## function should be defined in @code{@var{data}.compress.load.state}
+## and should return the uncompressed state struct when called
+## with the arguments @code{(@var{s}, @var{data})} where @var{s}
+## is the compressed struct.
+##
+## If @var{nSteps} is not given, the file is read until EOF is encountered.
+## If @var{nSteps} is present, the descent will be run until either
+## this number is reached or @code{@var{data}.cb.check_stop} returns true.
+## If the descent log does not contain enough data for this condition to be
+## fulfilled, more steps will be performed according to @code{so_run_descent}.
+## In this case, the callbacks in @code{@var{data}.cb} must be set in the
+## same way as for @code{so_run_descent}.
+##
+## Returned are the final state and the produced log structure,
+## in the same way as by @code{so_run_descent}.
+##
+## This routine requires @code{fload} from the @code{parallel} package
+## to be available.
+##
+## @seealso{so_explore_descent, so_run_descent, so_save_descent, fload}
+## @end deftypefn
+
+function [s, descentLog] = so_replay_descent (file, init, nSteps)
+  if (nargin () < 2 || nargin () > 3)
+    print_usage ();
+  endif
+
+  if (nargin () == 3)
+    forceSteps = true;
+    if (!isscalar (nSteps) || nSteps != round (nSteps) || nSteps < 1)
+      print_usage ();
+    endif
+  else
+    forceSteps = false;
+  endif
+
+  openFile = ischar (file);
+  if (!openFile && (!isnumeric (file) || !isscalar (file)))
+    error ("FILE must be a string or file ID");
+  endif
+
+  hasFload = exist ("fload");
+  if (hasFload != 2 && hasFload != 3)
+    error ("'fload' is not available");
+  endif
+
+  % Open file and read header.
+  if (openFile)
+    fd = fopen (file, "rb");
+    if (fd == -1)
+      error ("failed to open file '%s' for reading the descent log", file);
+    endif
+  else
+    fd = file;
+  endif
+  header = fload (fd);
+
+  % Call init method and load initial state.
+  data = init (header);
+  data.s = so_load_compressed (fd, "state", data);
+
+  % Add some (possibly) missing stuff with defaults.
+  if (!isfield (data, "log"))
+    data.log = struct ();
+  endif
+  if (!isfield (data, "handler"))
+    data.handler = struct ();
+  endif
+  if (!isfield (data, "cb"))
+    data.cb = struct ();
+  endif
+  if (!isfield (data.cb, "check_stop"))
+    data.cb.check_stop = @() false;
+  endif
+
+  % Call "initialised" handler and save initial state struct.
+  data.log.s0 = data.s;
+  if (isfield (data.handler, "initialised"))
+    data.log = data.handler.initialised (data);
+  endif
+
+  % Do the iteration.
+  costs = [data.s.cost];
+  k = 0;
+  while (true)
+    if (ferror (fd))
+      error ("error reading descent log");
+    endif
+
+    % Honour stopping condition if nSteps is given.
+    if (forceSteps && (k >= nSteps || data.cb.check_stop (data.s)))
+      break;
+    endif
+
+    % Read next data and check for EOF.
+    try
+      f = fload (fd);
+    catch
+      if (feof (fd))
+        break;
+      endif
+      error ("error reading descent log");
+    end_try_catch
+    dJ = fload (fd);
+    step = fload (fd);
+    newS = so_load_compressed (fd, "state", data);
+    if (ferror (fd))
+      error ("error reading descent log");
+    endif
+
+    % Call handlers.
+    ++k;
+    if (data.p.verbose)
+      printf ("\nDescent iteration %d...\n", k);
+      printf ("Starting cost: %.6f\n", data.s.cost);
+      printf ("Directional derivative: %.6f\n", dJ);
+      printf ("Armijo step %.6f: cost = %.6f\n", step, newS.cost);
+    endif
+    if (isfield (data.handler, "before_step"))
+      data.log = data.handler.before_step (k, data);
+    endif
+    if (isfield (data.handler, "direction"))
+      data.log = data.handler.direction (k, f, dJ, data);
+    endif
+    if (isfield (data.handler, "after_step"))
+      data.log = data.handler.after_step (k, step, newS, data);
+    endif
+
+    % Update cost tracking and switch to new state.
+    costs(k + 1) = newS.cost;
+    data.s = newS;
+  endwhile
+
+  % Make sure to close the file now.  Record EOF condition
+  % before we do so.
+  haveEOF = feof (fd);
+  if (openFile)
+    fclose (fd);
+  endif
+
+  % If we have an EOF condition, see if we need more steps.
+  if (haveEOF && forceSteps)
+    assert (k < nSteps && !data.cb.check_stop (data.s));
+
+    if (k == 0)
+      error ("no data in descent log");
+    endif
+
+    % Call through to so_run_descent, in a special "continuation"
+    % mode (defined internally for this purpose).
+    data._descentContinuation = struct ("k", k, "step", step, "costs", costs);
+    [s, descentLog] = so_run_descent (nSteps, [], data);
+    return;
+  endif
+
+  % Finish everything.
+  data.log.steps = k;
+  assert (length (costs) == k + 1);
+  data.log.costs = costs;
+  if (isfield (data.handler, "finished"))
+    data.log = data.handler.finished (data);
+  endif
+
+  % Fill return values.
+  s = data.s;
+  descentLog = data.log;
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for errors.
+%!error <Invalid call to>
+%!  so_replay_descent (1);
+%!error <Invalid call to>
+%!  so_replay_descent (1, 2, 3, 4);
+%!error <Invalid call to>
+%!  so_replay_descent (1, 2, 0);
+%!error <Invalid call to>
+%!  so_replay_descent (1, 2, 1.5);
+%!error <Invalid call to>
+%!  so_replay_descent (1, 2, [2, 3]);
+%!error <FILE must be a string or file ID>
+%!  so_replay_descent (struct (), 2);
+%!error <FILE must be a string or file ID>
+%!  so_replay_descent ([2, 3], 2);
+%!error <'fload' is not available>
+%!  pkg unload parallel;
+%!  so_replay_descent ("foo", 2);
+
+% Define compression / uncompression functions.  They do not really
+% "compress", but instead manipulate the data in ways such that it
+% is later possible to check that they have been called correctly.
+%
+%!function x = compressionCheckStruct (s, data)
+%!  x = struct ("weight", data.p.weight, "cost", s.cost);
+%!endfunction
+%
+%!function checkCompression (s, data, field)
+%!  assert (isfield (s, field));
+%!  assert (getfield (s, field), compressionCheckStruct (s, data));
+%!endfunction
+%
+%!function s = compressState (s, data)
+%!  s.compressed = compressionCheckStruct (s, data);
+%!endfunction
+%
+%!function s = uncompressState (s, data)
+%!  checkCompression (s, data, "compressed");
+%!  s.uncompressed = compressionCheckStruct (s, data);
+%!endfunction
+
+% Sample handlers that just build up "some" array from all the data
+% they are passed.  This is used to compare replayed and computed runs.
+%
+%!function log = initialised (data)
+%!  log = data.log;
+%!  log.mashUp = [data.s.a, data.s.b];
+%!endfunction
+%
+%!function log = beforeStep (k, data)
+%!  log = data.log;
+%!  log.mashUp = [log.mashUp, k, data.s.vol];
+%!
+%!  if (data.checkUncompression)
+%!    checkCompression (data.s, data, "uncompressed");
+%!  endif
+%!endfunction
+%
+%!function log = direction (k, f, dJ, data)
+%!  log = data.log;
+%!  log.mashUp = [log.mashUp, k, mean(f), sum(f), dJ, data.s.integ];
+%!endfunction
+%
+%!function log = afterStep (k, t, s, data)
+%!  log = data.log;
+%!  log.mashUp = [log.mashUp, k, t, s.a, s.b, s.cost, data.s.cost];
+%!
+%!  if (data.checkUncompression)
+%!    checkCompression (s, data, "uncompressed");
+%!  endif
+%!endfunction
+%
+%!function log = finished (data)
+%!  log = data.log;
+%!  log.mashUp = [log.mashUp, data.s.cost, log.steps, log.costs];
+%!endfunction
+
+% Define a function that initialises the problem data.
+%!function data = getData ()
+%!  data = struct ();
+%!  data.p = so_init_params (false);
+%!  data.p.vol = 10;
+%!  data.p.weight = 50;
+%!
+%!  n = 100;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  data.g = struct ("x", x, "h", h);
+%!
+%!  data = so_example_problem (data);
+%!  data.handler = struct ("initialised", @initialised, ...
+%!                         "before_step", @beforeStep, ...
+%!                         "direction", @direction, ...
+%!                         "after_step", @afterStep, ...
+%!                         "finished", @finished);
+%!
+%!  % Unless explicitly set, do not expect compressed states.
+%!  data.checkUncompression = false;
+%!
+%!  data.phi0 = ls_genbasic (data.g.x, "box", -3, 5);
+%!endfunction
+
+% Get data and enable compression.
+%!function data = getDataForCompression (uncompress)
+%!  data = getData ();
+%!  data.compress = struct ();
+%!  data.compress.save = struct ("state", @compressState);
+%!  data.compress.load = struct ("state", @uncompressState);
+%!  data.checkUncompression = uncompress;
+%!endfunction
+
+% Compare results (s, log) two runs and check if the mash-ups match.
+% We cannot fully compare the log structs, though, since they contain
+% private data used for saving.
+%!function compareResults (s1, l1, s2, l2)
+%!  assert (s2, s1);
+%!  assert (l2.mashUp, l1.mashUp);
+%!endfunction
+
+% Create a basic log and replay it without forcing steps.  This tests
+% both saving to a file name and file descriptor.
+%!test
+%!  pkg load parallel;
+%!
+%!  nSteps = 10;
+%!  baseData = getData ();
+%!
+%!  f = tempname ();
+%!  data = so_save_descent (f, struct (), baseData);
+%!  [s1, l1] = so_run_descent (nSteps, data.phi0, data);
+%!  [s2, l2] = so_replay_descent (f, @getData);
+%!  unlink (f);
+%!  compareResults (s1, l1, s2, l2);
+%!
+%!  f = tmpfile ();
+%!  data = so_save_descent (f, struct (), baseData);
+%!  [s1, l1] = so_run_descent (nSteps, data.phi0, data);
+%!  frewind (f);
+%!  [s2, l2] = so_replay_descent (f, @getData);
+%!  fclose (f);
+%!  compareResults (s1, l1, s2, l2);
+%!
+%!  [pr, pw] = pipe ();
+%!  data = so_save_descent (pw, struct (), baseData);
+%!  [s1, l1] = so_run_descent (nSteps, data.phi0, data);
+%!  fclose (pw);
+%!  [s2, l2] = so_replay_descent (pr, @getData);
+%!  fclose (pr);
+%!  compareResults (s1, l1, s2, l2);
+
+% Check for "no data" error if we need to compute more steps
+% but *none* are saved.  Also check that it *is* ok to have
+% zero steps if the stopping criterion stops immediately.
+%
+%!test
+%!  pkg load parallel;
+%!
+%!  baseData = getData ();
+%!  [pr, pw] = pipe ();
+%!  data = so_save_descent (pw, struct (), baseData);
+%!  data.cb.check_stop = @() true;
+%!  [s1, l1] = so_run_descent (1, data.phi0, data);
+%!  fclose (pw);
+%!  assert (l1.steps, 0);
+%!  [s2, l2] = so_replay_descent (pr, @getData);
+%!  fclose (pr);
+%!  compareResults (s1, l1, s2, l2);
+%!
+%!  baseData = getData ();
+%!  [pr, pw] = pipe ();
+%!  data = so_save_descent (pw, struct (), baseData);
+%!  [s1, l1] = so_run_descent (1, data.phi0, data);
+%!  fclose (pw);
+%!  [s2, l2] = so_replay_descent (pr, @getData, 1);
+%!  fclose (pr);
+%!  compareResults (s1, l1, s2, l2);
+%
+%!error <no data in descent log>
+%!  pkg load parallel;
+%!
+%!  baseData = getData ();
+%!  [pr, pw] = pipe ();
+%!  data = so_save_descent (pw, struct (), baseData);
+%!  data.cb.check_stop = @() true;
+%!  [s1, l1] = so_run_descent (1, data.phi0, data);
+%!  fclose (pw);
+%!  [s2, l2] = so_replay_descent (pr, @getData, 1);
+%!  fclose (pr);
+%!  compareResults (s1, l1, s2, l2);
+
+% Check forcing the steps to be less or to require computation.
+%!test
+%!  pkg load parallel;
+%!
+%!  nStepsShort = 5;
+%!  nStepsLong = 10;
+%!
+%!  baseData = getData ();
+%!  [prl, pwl] = pipe ();
+%!  data = so_save_descent (pwl, struct (), baseData);
+%!  [sl, ll] = so_run_descent (nStepsLong, data.phi0, data);
+%!  fclose (pwl);
+%!
+%!  baseData = getData ();
+%!  [prs, pws] = pipe ();
+%!  data = so_save_descent (pws, struct (), baseData);
+%!  [ss, ls] = so_run_descent (nStepsShort, data.phi0, data);
+%!  fclose (pws);
+%!
+%!  [s, l] = so_replay_descent (prl, @getData, nStepsShort);
+%!  fclose (prl);
+%!  compareResults (s, l, ss, ls);
+%!
+%!  [s, l] = so_replay_descent (prs, @getData, nStepsLong);
+%!  fclose (prs);
+%!  compareResults (s, l, sl, ll);
+
+% Check compression / uncompression feature.
+%!test
+%!  pkg load parallel;
+%!
+%!  nSteps = 10;
+%!  [pr, pw] = pipe ();
+%!
+%!  data = getDataForCompression (false);
+%!  data = so_save_descent (pw, struct (), data);
+%!  [s1, l1] = so_run_descent (nSteps, data.phi0, data);
+%!  fclose (pw);
+%!
+%!  init = @(header) getDataForCompression (true);
+%!  [s2, l2] = so_replay_descent (pr, init);
+%!  fclose (pr);
+%!
+%!  % No result comparison here, since the compress / uncompress
+%!  % functions introduced changes to the state structs.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demo.
+
+% This is the "same" demo as for so_run_descent.  The difference is just
+% that the descent is first saved and only later replayed to produce
+% the actual plots.
+%
+%!demo
+%!  pkg load parallel;
+%!
+%!  data = struct ();
+%!  data.p = so_init_params (false);
+%!  data.p.vol = 10;
+%!  data.p.weight = 50;
+%!
+%!  n = 100;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  data.g = struct ("x", x, "h", h);
+%!
+%!  data = so_example_problem (data);
+%!  phi0 = ls_genbasic (data.g.x, "box", -3, 7);
+%!
+%!  printf ("Computing descent...\n");
+%!  [pr, pw] = pipe ();
+%!  d = data;
+%!  d.handler = struct ();
+%!  d = so_save_descent (pw, struct (), d);
+%!  s = so_run_descent (5, phi0, d);
+%!  fclose (pw);
+%!  printf ("Final cost: %.6d\n", s.cost);
+%!
+%!  printf ("\nNow replaying...\n");
+%!  d = data;
+%!  d.p.verbose = true;
+%!  init = @() d;
+%!  s = so_replay_descent (pr, init);
+%!  fclose (pr);
+%!  printf ("Final cost: %.6d\n", s.cost);
diff --git a/inst/so_run_descent.m b/inst/so_run_descent.m
new file mode 100644
index 0000000..cc5ebe0
--- /dev/null
+++ b/inst/so_run_descent.m
@@ -0,0 +1,458 @@
+##  Copyright (C) 2013-2015  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {[@var{s}, @var{log}] =} so_run_descent (@var{nSteps}, @var{phi0}, @var{data})
+##
+## Run a descent method for shape optimisation.  This routine is quite
+## general, since the actual ``descent direction'' used must be computed
+## by a callback (see below).  It is thus not just for steepest descent.
+## At each step, a line search according to @code{so_step_armijo} is employed.
+##
+## @var{nSteps} defines the maximal number of descent steps to take.  After
+## this number, the method returns no matter whether or not the stopping
+## criterion is fulfilled.  @var{phi0} is the initial geometry to use.
+## Returned are the final state and the descent log structs (see below).
+##
+## @var{data} contains all the information about the actual problem.
+## This includes various parameters that influence the behaviour of this
+## and other routines.  Furthermore, also some callback functions must
+## be defined to evaluate the cost functional and to determine the
+## next descent direction.  This struct is also updated with the current
+## state at each optimisation step and passed to the callback functions.
+##
+## Most content of @var{data} is read-only, with @code{@var{data}.s} and
+## @code{@var{data}.log} being the only exceptions.  They are updated
+## accordingly from the results of the callback functions and the
+## optimisation descent.  These two structs will be created and filled in
+## by @code{so_run_descent}.  @code{@var{data}.log} is preserved if it is
+## already present.  @code{@var{data}.s} will always be overwritten
+## with the initial result of the @code{update_state} callback.
+##
+## The struct @code{@var{data}.p} contains parameters.  They can contain
+## problem-dependent parameters that are handled by the callback routines
+## as well as parameters for the line search with @code{so_step_armijo}.
+## This routine is influenced by the following parameters directly:
+##
+## @table @code
+## @item verbose
+## Whether or not log chatter should be printed.
+##
+## @item descent.initialStep
+## Initial trial step length for the line search in the first iteration.
+##
+## @item descent.projectSpeed
+## If set, project the speed field (in L2, discontinuously) to enforce
+## geometric constraints (see below).  If not set or missing, the
+## geometry is projected instead.
+## @end table
+##
+## The routine @code{so_init_params} can be used to initialise the parameter
+## structure with some defaults.
+##
+## Information about the used grid and grid-dependent data should be in
+## @code{@var{data}.g}.  Generally defined are the following fields:
+##
+## @table @code
+## @item h
+## The grid spacing.
+##
+## @item constraints
+## Optional, can be used to specify the hold-all domain and contained
+## regions as shape constraints.  If specified, the shape or the speed
+## field will be ``projected'' (depending on
+## @code{@var{data}.p.descent.projectSpeed}) to satisfy the constraints.
+## The callback computing the descent direction is also a good place
+## to take the constraints into account in a problem-specific way.
+##
+## The following fields (as logical arrays with the grid size) can
+## be used to specify the constraints:
+##
+## @table @code
+## @item holdall
+## The hold-all domain.  The current shape must always be part of it.
+## By excluding interior parts of the grid, forbidden regions can be defined.
+## 
+## @item contained
+## A part of the hold-all domain that should always be part of the shape.
+## @end table
+## @end table
+##
+## The specific problem is handled by @emph{callback routines} to compute
+## the cost and find descent directions.  They must be set in
+## @code{@var{data}.cb}, with these interfaces:
+##
+## @table @code
+## @item @var{s} = update_state (@var{phi}, @var{data})
+## Compute the state (see below) for the geometry described by @var{phi}.
+## The main work done by this callback is evaluating the cost functional.
+## It does not need to update @code{@var{s}.phi} to @var{phi}, this will
+## be done automatically after the call.
+##
+## @item [@var{f}, @var{dJ}] = get_direction (@var{data})
+## Compute the speed field @var{f} and its associated directional
+## shape derivative @var{dJ} to be used as the next descent direction.
+## The current cost and geometry can be extracted from @code{@var{data}.s}.
+##
+## @item @var{exit} = check_stop (@var{data})
+## Determine whether or not to stop the iteration.  Should return
+## @code{true} if the state in @var{data} satisfies the problem-specific
+## stopping criterion.  If not present, no stopping criterion
+## is used and the iteration done for precisely @var{nSteps} iterations.
+##
+## @item @var{d} = solve_stationary (@var{f}, @var{data})
+## Solve the stationary Eikonal equation for the speed field.
+## If not present, @code{ls_solve_stationary} is used.  It can be
+## set explicitly to give a more appropriate routine.  For instance,
+## @code{ls_nb_from_geom} could be used if geometry information is computed.
+## @end table
+##
+## Additional callback functions can be defined optionally
+## in @code{@var{data}.handler}.  They can keep problem-specific
+## logs of the descent, plot figures, print output or something else.
+## They should always return a (possibly) updated log struct, which
+## will be updated in @code{@var{data}.log} after the call.
+## This struct can also be used to persist information between
+## calls to the handlers.  These handlers can be defined:
+##
+## @table @code
+## @item @var{log} = initialised (@var{data})
+## Called before the iteration is started.  @code{@var{data}.s}
+## contains the initial geometry and computed state data for it.
+##
+## @item @var{log} = before_step (@var{k}, @var{data})
+## Called when starting a new step, before any action for the
+## step is taken.  @var{k} is the number of the iteration and
+## @code{@var{data}.s} contains the current state.
+##
+## @item @var{log} = direction (@var{k}, @var{f}, @var{dJ}, @var{data})
+## Called when the descent direction has been determined.
+## @var{f} and @var{dJ} are the results of @code{@var{data}.cb.get_direction}.
+##
+## @item @var{log} = after_step (@var{k}, @var{t}, @var{s}, @var{data})
+## Called after a step has been found.  @var{t} is the step length
+## found by the line search.  @var{s} is the state struct after the step
+## has been taken.  The previous state can still be found in
+## @code{@var{data}.s}.
+##
+## @item @var{log} = finished (@var{data})
+## Called after the descent run.  @code{@var{data}.log} is already filled
+## in with the standard data (number of steps and costs at each step).
+## @end table
+##
+## Note that information about the speed field @var{f} is only
+## passed to the @code{direction} handler and @emph{not again}
+## to @code{after_step}.  If it is required by the latter,
+## it should be saved somehow in the log struct.  This way, the handlers
+## really only receive new information and no redundant arguments.
+##
+## Finally, we come to the state struct @code{@var{data}.s}.  It must
+## contain at least the fields below, but can also contain problem-specific
+## information that is, for instance, computed in the @code{update_state}
+## handler and reused for computing the descent direction.
+##
+## @table @code
+## @item phi
+## The level-set function of the current geometry.  This is automatically
+## updated after the @code{update_state} handler has been called.
+##
+## @item cost
+## Value of the cost functional for the geometry in @code{@var{data}.s.phi}.
+## @end table
+##
+## The log struct can contain mostly user-defined data, but
+## @code{so_run_descent} will also fill in some data by itself:
+##
+## @table @code
+## @item s0
+## The state struct corresponding to the initial geometry defined
+## by @var{phi0}.
+##
+## @item steps
+## Total number of steps done.  This is useful if the stopping criterion
+## was satisfied before @var{nSteps} iterations.
+##
+## @item costs
+## Cost values for all steps.  This will be a vector of length
+## @code{@var{data}.log.steps + 1}, since it contains both the initial
+## and the final cost value in addition to all intermediate ones.
+## @end table
+##
+## @seealso{so_init_params, so_save_descent, so_replay_descent,
+## so_explore_descent, so_step_armijo}
+## @end deftypefn
+
+function [s, descentLog] = so_run_descent (nSteps, phi0, data)
+  if (nargin () != 3)
+    print_usage ();
+  endif
+
+  if (!isscalar (nSteps) || nSteps != round (nSteps) || nSteps < 1)
+    print_usage ();
+  endif
+
+  if (data.p.descent.initialStep <= 0)
+    error ("invalid initial step length");
+  endif
+
+  % Construct empty structs for optional fields.  This simplifies the
+  % process later on.
+  if (!isfield (data.g, "constraints"))
+    data.g.constraints = struct ();
+  endif
+  if (!isfield (data, "handler"))
+    data.handler = struct ();
+  endif
+
+  % Augment the update_state and get_direction callbacks.
+  data._descent = struct ();
+  data._descent.userUpdateState = data.cb.update_state;
+  data._descent.userGetDirection = data.cb.get_direction;
+  data.cb.update_state = @augmentedUpdateState;
+  data.cb.get_direction = @augmentedGetDirection;
+
+  % Create 'null' check_stop callback if none is present.
+  if (!isfield (data.cb, "check_stop"))
+    data.cb.check_stop = @() false;
+  endif
+
+  % Create default 'solve_stationary' callback.
+  if (!isfield (data.cb, "solve_stationary"))
+    data.cb.solve_stationary ...
+      = @(f, data) ls_solve_stationary (data.s.phi, f, data.g.h);
+  endif
+
+  % Do some initialisations, but only if we're not in "continuation" mode.
+  % In continuation mode, the "initialised" handler is already called
+  % and the data to use is explicitly given by the caller.
+  if (!isfield (data, "_descentContinuation"))
+    data.s = data.cb.update_state (phi0, data);
+    data.s.phi = phi0;
+    if (!isfield (data, "log"))
+      data.log = struct ();
+    endif
+
+    lastStep = data.p.descent.initialStep;
+    costs = [data.s.cost];
+    k = 0;
+
+    data.log.s0 = data.s;
+    if (isfield (data.handler, "initialised"))
+      data.log = data.handler.initialised (data);
+    endif
+  else
+    lastStep = data._descentContinuation.step;
+    costs = data._descentContinuation.costs;
+    k = data._descentContinuation.k;
+  endif
+
+  % Do the descent iteration.
+  while (k < nSteps && !data.cb.check_stop (data.s))
+    ++k;
+    if (data.p.verbose)
+      printf ("\nDescent iteration %d...\n", k);
+      printf ("Starting cost: %.6f\n", data.s.cost);
+      ticId = tic ();
+    endif
+
+    % Call "before_step" handler if present.
+    if (isfield (data.handler, "before_step"))
+      data.log = data.handler.before_step (k, data);
+    endif
+
+    % Check the constraints.  Since we enforce them, they
+    % should always be satisfied.
+    if (isfield (data.g.constraints, "holdall"))
+      assert (ls_check (data.s.phi, "inside", data.g.constraints.holdall));
+    endif
+    if (isfield (data.g.constraints, "contained"))
+      assert (ls_check (data.s.phi, "contain", data.g.constraints.contained));
+    endif
+
+    % Get the descent direction.
+    [f, dJ] = data.cb.get_direction (data);
+    if (dJ >= 0)
+      error ("no descent direction found");
+    endif
+
+    % Print log and call "direction" handler if present.
+    if (data.p.verbose)
+      printf ("Directional derivative: %.6f\n", dJ);
+    endif
+    if (isfield (data.handler, "direction"))
+      data.log = data.handler.direction (k, f, dJ, data);
+    endif
+
+    % Calculate level-set stationary distances and perform Armijo step.
+    dists = data.cb.solve_stationary (f, data);
+    [newS, lastStep] = so_step_armijo (lastStep, dists, f, dJ, data);
+
+    % Call "after_step" handler.
+    if (isfield (data.handler, "after_step"))
+      data.log = data.handler.after_step (k, lastStep, newS, data);
+    endif
+
+    % Update cost tracking and switch over to new state.
+    costs(k + 1) = newS.cost;
+    data.s = newS;
+
+    % Finish timing for verbose output.
+    if (data.p.verbose)
+      toc (ticId);
+    endif
+  endwhile
+
+  % Fill in the final results.
+  data.log.steps = k;
+  assert (length (costs) == k + 1);
+  data.log.costs = costs;
+
+  % Call "finished" handler as the very last action.
+  if (isfield (data.handler, "finished"))
+    data.log = data.handler.finished (data);
+  endif
+
+  % Fill return values.
+  s = data.s;
+  descentLog = data.log;
+endfunction
+
+% Function that augments the 'update_state' handler with general stuff
+% that it should do.  In particular, we enforce the constraints
+% before doing anything (so that they are definitely not violated
+% before calling the user code).  Also, update data.s.phi after the call.
+function s = augmentedUpdateState (phi, data)
+  phi = ls_normalise (phi, data.g.h);
+  if (isfield (data.g.constraints, "holdall"))
+    phi = ls_enforce (phi, "inside", data.g.constraints.holdall);
+  endif
+  if (isfield (data.g.constraints, "contained"))
+    phi = ls_enforce (phi, "contain", data.g.constraints.contained);
+  endif
+
+  s = data._descent.userUpdateState (phi, data);
+  s.phi = phi;
+endfunction
+
+% Function that augments the 'get_direction' handler.  It hooks the
+% speed field projection onto it when requested by the parameters.
+function [F, DJ] = augmentedGetDirection (data)
+  [F, DJ] = data._descent.userGetDirection (data);
+
+  % Note that the projection below does not take care of adapting DJ
+  % accordingly!  Using this feature makes the descent method potentially
+  % "inconsistent".  The same is true if the geometry is projected
+  % after a step, though.
+
+  if (isfield (data.p.descent, "projectSpeed") && data.p.descent.projectSpeed)
+    if (isfield (data.g.constraints, "holdall"))
+      F = ls_enforce_speed (F, "inside", data.g.constraints.holdall);
+    endif
+    if (isfield (data.g.constraints, "contained"))
+      F = ls_enforce_speed (F, "contain", data.g.constraints.contained);
+    endif
+  endif
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Construct basic example problem for testing.
+%!shared data, phi0
+%!  data = struct ();
+%!  data.p = so_init_params (false);
+%!  data.p.vol = 10;
+%!  data.p.weight = 50;
+%!
+%!  n = 100;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  data.g = struct ("x", x, "h", h);
+%!
+%!  data = so_example_problem (data);
+%!  data.handler = struct ();
+%!
+%!  phi0 = ls_genbasic (data.g.x, "box", -3, 5);
+
+% Test for errors.
+%!error <Invalid call to>
+%!  so_run_descent (1, 2);
+%!error <Invalid call to>
+%!  so_run_descent (1, 2, 3, 4);
+%!error <Invalid call to>
+%!  so_run_descent (0, phi0, data);
+%!error <Invalid call to>
+%!  so_run_descent (1.5, phi0, data);
+%!error <Invalid call to>
+%!  so_run_descent ([2, 3], phi0, data);
+
+% Validate parameters.
+%!error <invalid initial step length>
+%!  d = data;
+%!  d.p.descent.initialStep = 0;
+%!  so_run_descent (1, phi0, d);
+
+% Test optimisation of the example problem.
+%!test
+%!  nSteps = 12;
+%!
+%!  [s, descentLog] = so_run_descent (nSteps, phi0, data);
+%!  assert (s.cost, 0, 1e-3);
+%!  assert (length (descentLog.costs), nSteps + 1);
+%!  assert (descentLog.costs(end), s.cost);
+%!  %semilogy (descentLog.costs, "o");
+
+% Test stopping criterion.
+%!test
+%!  tol = 5e-2;
+%!  nSteps = 100;
+%!  d = data;
+%!  d.cb.check_stop = @(data) (data.cost < tol);
+%!
+%!  [s, descentLog] = so_run_descent (nSteps, phi0, d);
+%!  assert (s.cost < tol);
+%!  printf ("Steps needed: %d\n", descentLog.steps);
+%!  assert (descentLog.steps < nSteps);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Demo.
+
+% Use so_example_problem to define a demo.
+%
+%!demo
+%!  data = struct ();
+%!  data.p = so_init_params (true);
+%!  data.p.vol = 10;
+%!  data.p.weight = 50;
+%!
+%!  n = 100;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  data.g = struct ("x", x, "h", h);
+%!
+%!  % Look at so_example_problem to see how the callbacks
+%!  % and the plotting handler is defined.
+%!  data = so_example_problem (data);
+%!
+%!  phi0 = ls_genbasic (data.g.x, "box", -3, 7);
+%!  [s, descentLog] = so_run_descent (5, phi0, data);
+%!
+%!  figure ();
+%!  semilogy (0 : descentLog.steps, descentLog.costs, "o");
+%!  title ("Cost Descrease");
+%!
+%!  printf ("\nFinal interval: [%.6d, %.6d]\n", s.a, s.b);
+%!  printf ("Final cost: %.6d\n", s.cost);
diff --git a/inst/so_save_descent.m b/inst/so_save_descent.m
new file mode 100644
index 0000000..240ef1c
--- /dev/null
+++ b/inst/so_save_descent.m
@@ -0,0 +1,190 @@
+##  Copyright (C) 2015  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{data} =} so_save_descent (@var{file}, @var{header}, @var{data})
+##
+## Update handlers to save the descent run to a file.  The existing handlers
+## in @var{data} will be kept, but additional logic will be added to them
+## that saves all data computed during the descent run to a file.  This
+## file can then be used with @code{so_replay_descent}.
+##
+## @var{file} can either be a file ID (integer) or file name (string).
+## In the latter case, the file will be created in @qcode{"wb"} mode
+## and closed after the descent run.  @var{header} should be some data
+## (possibly a struct) that will be written as the ``file header''.
+## It can contain data that identifies the problem solved and all
+## parameter values.  It must be possible to reproduce a problem's @var{data}
+## structure from this header when replaying the descent.
+##
+## The state struct can be optionally ``compressed'' before it is written,
+## in case it contains excessive amounts of data that is not necessary
+## for the replay (for instance, because it is cheap to re-compute).
+## If this feature should be used, a function must be defined that translates
+## the state struct accordingly.  It should be stored in
+## @code{@var{data}.compress.save.state}, and will be called
+## with arguments @code{(@var{s}, @var{data})}.  It should return
+## the state struct in the form to save it.
+##
+## This routine requires @code{fsave} from the @code{parallel} package
+## to be available.  Note that the files are not guaranteed to be interoperable
+## between machines or package versions.
+##
+## @seealso{so_run_descent, so_replay_descent, so_explore_descent, fsave}
+## @end deftypefn
+
+% Format of the descent log (all variables saved with 'fsave'):
+%
+%   header
+%   initial state struct
+%   for each step:
+%     speed field f
+%     dJ for f
+%     step length taken
+%     new state struct
+%
+% This format, in particular that the new state is the very last
+% item and the initial state struct the last part of the "header",
+% ensures that each actual step data is preceded by the "old" state.
+% Thus, if we start reading actually there, we get information for each state
+% that includes both the old and the new state.
+
+function data = so_save_descent (file, header, data)
+  if (nargin () != 3)
+    print_usage ();
+  endif
+  if (!isstruct (data))
+    error ("DATA should be a struct");
+  endif
+
+  openFile = ischar (file);
+  if (!openFile && (!isnumeric (file) || !isscalar (file)))
+    error ("FILE must be a string or file ID");
+  endif
+
+  hasFsave = exist ("fsave");
+  if (hasFsave != 2 && hasFsave != 3)
+    error ("'fsave' is not available");
+  endif
+
+  % Retain old handlers (if present).
+  if (!isfield (data, "handler"))
+    data.handler = struct ();
+  endif
+  oldHandlers = data.handler;
+
+  % Set defaults for old handlers.
+  if (!isfield (oldHandlers, "initialised"))
+    oldHandlers.initialised = @(data) data.log;
+  endif
+  if (!isfield (oldHandlers, "direction"))
+    oldHandlers.direction = @(k, f, dJ, data) data.log;
+  endif
+  if (!isfield (oldHandlers, "after_step"))
+    oldHandlers.after_step = @(k, t, s, data) data.log;
+  endif
+  if (!isfield (oldHandlers, "finished"))
+    oldHandlers.finished = @(data) data.log;
+  endif
+
+  % Set new handlers.
+  args = struct ("openFile", openFile, "file", file, ...
+                 "header", header, "oldHandlers", oldHandlers);
+  data.handler.initialised = @(data) initialised (args, data);
+  data.handler.direction = @(k, f, dJ, data) direction (args, k, f, dJ, data);
+  data.handler.after_step = @(k, t, s, data) afterStep (args, k, t, s, data);
+  data.handler.finished = @(data) finished (args, data);
+endfunction
+
+% Save the given state struct, compressing it before if necessary.
+function saveStateStruct (fd, s, data)
+  if (isfield (data, "compress") && isfield (data.compress, "save")
+        && isfield (data.compress.save, "state"))
+    s = data.compress.save.state (s, data);
+  endif
+
+  fsave (fd, s);
+endfunction
+
+% Initialisation handler.  This is used to open the file (if required)
+% and to write the header.  It also saves the initial state struct.
+function log = initialised (args, data)
+  if (args.openFile)
+    fd = fopen (args.file, "wb");
+    if (fd == -1)
+      error ("failed to open file '%s' for saving the descent log", args.file);
+    endif
+  else
+    fd = args.file;
+  endif
+
+  fsave (fd, args.header);
+  saveStateStruct (fd, data.s, data);
+  data.log._saveDescentFd = fd;
+
+  log = args.oldHandlers.initialised (data);
+endfunction
+
+% Save direction information when known.
+function log = direction (args, k, f, dJ, data)
+  fd = data.log._saveDescentFd;
+  fsave (fd, f);
+  fsave (fd, dJ);
+  log = args.oldHandlers.direction (k, f, dJ, data);
+endfunction
+
+% Save new state and step length.  The new state should be the very
+% last information saved for a step.
+function log = afterStep (args, k, t, s, data)
+  fd = data.log._saveDescentFd;
+  fsave (fd, t);
+  saveStateStruct (fd, s, data);
+  log = args.oldHandlers.after_step (k, t, s, data);
+endfunction
+
+% Finished handler, closes the file (if applicable).
+function log = finished (args, data)
+  if (args.openFile)
+    fclose (data.log._saveDescentFd);
+  endif
+  log = args.oldHandlers.finished (data);
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for errors.
+%!error <Invalid call to>
+%!  so_save_descent (1, 2);
+%!error <Invalid call to>
+%!  so_save_descent (1, 2, 3, 4);
+%!error <DATA should be a struct>
+%!  so_save_descent (1, 2, 3);
+%!error <FILE must be a string or file ID>
+%!  so_save_descent (struct (), 2, struct ());
+%!error <FILE must be a string or file ID>
+%!  so_save_descent ([2, 3], 2, struct ());
+%!error <'fsave' is not available>
+%!  pkg unload parallel;
+%!  so_save_descent ("foo", 2, struct ());
+
+% Working calls.
+%!test
+%!  pkg load parallel;
+%!  so_save_descent ("foo", 2, struct ());
+%!  so_save_descent (5, 2, struct ());
+
+% Funtional tests and a demo are in so_replay_descent.
diff --git a/inst/so_step_armijo.m b/inst/so_step_armijo.m
new file mode 100644
index 0000000..99d51b4
--- /dev/null
+++ b/inst/so_step_armijo.m
@@ -0,0 +1,311 @@
+##  Copyright (C) 2013-2015  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {[@var{s}, @var{t}] =} so_step_armijo (@var{t0}, @var{d}, @var{f}, @var{dJ}, @var{data})
+##
+## Perform a line search according to the Armijo rule.  A backtracking
+## strategy is employed, such that the returned step length @var{t}
+## satisfies
+## @tex
+## \begin{equation*}
+##  J(t) \le J(0) + t \tau \cdot dJ.
+## \end{equation*}
+## @end tex
+## @ifnottex
+##
+## @example
+## J(t) <= J(0) + t * tau * dJ.
+## @end example
+##
+## @end ifnottex
+##
+## @var{t0} defines the initial step length (see below).  It can be set to
+## some suitable constant or to the last successful step length.
+## @var{d} should be the result of @code{ls_solve_stationary} for
+## the current geometry, and @var{f} the matching speed field on the grid.
+## @var{dJ} must be the directional shape derivative of the cost functional
+## in direction @var{f}.  This value must be negative.
+##
+## Data defining the problem, the current state and parameters
+## should be passed in @var{data}.  See @code{so_run_descent} for a
+## description of this struct.
+##
+## Returned is the final state struct in @var{s} and the corresponding
+## step length taken in @var{t}.
+##
+## The following parameters must be set in @code{@var{data}.p.lineSearch}
+## to determine how the Armijo line search is done:
+##
+## @table @code
+## @item relaxation
+## The parameter tau in the Armijo rule.
+##
+## @item backtrack
+## Backtracking factor.  Should be less than one.
+##
+## @item initial
+## Factor for increasing @var{t0} to get the initial guess.  This should
+## be larger than one in order to allow increasing of the step
+## length when this is possible.
+##
+## @item minStep
+## Minimum step length.  If backtracking goes below this value, use
+## @code{minStep} and disregard the condition.
+## @end table
+##
+## Furthermore, the following general parameters in @code{@var{data}.p}
+## also influence the behaviour of the line search:
+##
+## @table @code
+## @item verbose
+## Whether or not log chatter should be printed.
+##
+## @item nProc
+## Number of parallel threads to use for the line search.  If this
+## is larger than one, multiple trial steps are computed in parallel to
+## speed up the computation.  For this to work, @code{pararrayfun}
+## of the @code{parallel} package must be available.
+## @end table
+##
+## The routine @code{so_init_params} can be used to initialise the parameter
+## structure with default values.
+## 
+## @seealso{so_init_params, so_run_descent, ls_solve_stationary}
+## @end deftypefn
+
+function [s, t] = so_step_armijo (t0, d, f, dJ, data)
+  if (nargin () != 5)
+    print_usage ();
+  endif
+
+  if (!isscalar (t0) || !isscalar (dJ) || !isstruct (data))
+    print_usage ();
+  endif
+
+  if (dJ >= 0)
+    error ("DJ should be negative");
+  endif
+
+  p = data.p.lineSearch;
+  if (p.relaxation <= 0 || p.relaxation >= 1)
+    error ("invalid Armijo relaxation parameter");
+  endif
+  if (p.backtrack <= 0 || p.backtrack >= 1)
+    error ("invalid backtracking factor");
+  endif
+  if (p.initial <= 1)
+    error ("invalid initial increase factor");
+  endif
+  if (p.minStep < 0)
+    error ("invalid minimum step length");
+  endif
+
+  sz = size (d);
+  if (!all (sz == size (f)))
+    error ("sizes mismatch between D and F");
+  endif
+  if (!all (sz == size (data.s.phi)))
+    error ("sizes mismatch between D and PHI");
+  endif
+
+  t = t0 * p.initial;
+  while (true)
+
+    % Use parcellfun to check a couple of steps in parallel.
+    parTs = t * p.backtrack.^(0 : data.p.nProc - 1);
+    errorHandler = @(err, curT) deal (err, NA);
+    handler = @(curT) getCostForStep (curT, d, f, data);
+
+    if (length (parTs) == 1)
+      [costps, sps] = handler (parTs);
+      costps = {costps};
+      sps = {sps};
+    else
+      [costps, sps] = pararrayfun (data.p.nProc, handler, parTs, ...
+                                   "UniformOutput", false, ...
+                                   "ErrorHandler", errorHandler);
+    endif
+
+    % Process the results looking for a step that is ok.
+    for i = 1 : length (parTs)
+      if (isstruct (costps{i}))
+        error ("Subprocess error: %s", costps{i}.message);
+      endif
+      if (data.p.verbose)
+        printf ("Armijo step %.6f: cost = %.6f\n", parTs(i), costps{i});
+      endif
+
+      % Check for minimum step length.  Note that it may happen
+      % that even this step is "too long", eliminating the domain.
+      % In that case, error out.
+      if (parTs(i) < p.minStep)
+        warning ("level-set:shape-optimisation:no-armijo-step", ...
+                 "no step found in line search until minimum %.6f", p.minStep);
+
+        t = p.minStep;
+        [cost, s] = getCostForStep (p.minStep, d, f, data);
+        if (isinf (cost))
+          error ("minimum step length results in invalid state");
+        endif
+
+        return;
+      endif
+
+      if (costps{i} <= data.s.cost + parTs(i) * p.relaxation * dJ)
+        t = parTs(i);
+        s = sps{i};
+        return;
+      endif
+    endfor
+
+    % Decrease step length.
+    t *= p.backtrack .^ data.p.nProc;
+  endwhile
+endfunction
+
+% Helper routine to calculate costs given a step length.
+function [costp, sp] = getCostForStep (t, d, f, data)
+  phip = ls_extract_solution (t, d, data.s.phi, f);
+
+  % Do not allow steps that eliminate the domain!
+  if (ls_isempty (phip))
+    costp = Inf;
+    sp = NA;
+    return;
+  endif
+
+  sp = data.cb.update_state (phip, data);
+  costp = sp.cost;
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Cost function for our example problem.
+%!function s = updateState (phi, data)
+%!  inside = ls_inside (phi);
+%!  vol = data.g.h * length (find (inside));
+%!  cost = 1 / vol;
+%!
+%!  % Throw if the volume is too large.  This is used
+%!  % to check the error handling code.
+%!  if (vol > 15)
+%!    error ("volume too large");
+%!  endif
+%!
+%!  s = struct ("phi", phi, "cost", cost);
+%!endfunction
+
+% Define default parameters and phi used in the test later on.
+%!shared n, phi, baseData, d, f, dJ
+%!  n = 100;
+%!  x = linspace (-10, 10, n);
+%!  h = x(2) - x(1);
+%!  a = 0;
+%!  b = 5;
+%!  phi = ls_genbasic (x, "box", a, b);
+%!
+%!  baseData = struct ();
+%!  baseData.p = so_init_params (false);
+%!  baseData.g = struct ("x", x, "h", h);
+%!  baseData.cb = struct ("update_state", @updateState);
+%!  baseData.s = updateState (phi, baseData);
+%!  baseData.s.phi = phi;
+%!
+%!  f = ones (size (phi));
+%!  d = ls_solve_stationary (phi, f, h);
+%!
+%!  curVol = b - a;
+%!  dJ = -2 / curVol^2;
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  so_step_armijo (1, 2, 3, 4)
+%!error <Invalid call to>
+%!  so_step_armijo (1, 2, 3, 4, 5, 6)
+%!error <Invalid call to>
+%!  so_step_armijo ([1, 1], 2, 3, 4, struct ())
+%!error <Invalid call to>
+%!  so_step_armijo (1, 2, 3, [4, 4], struct ())
+%!error <Invalid call to>
+%!  so_step_armijo (1, 2, 3, 4, 5)
+%!error <DJ should be negative>
+%!  so_step_armijo (1, d, f, 4, baseData)
+%!error <sizes mismatch between D and F>
+%!  so_step_armijo (1, 2, f, dJ, baseData)
+%!error <sizes mismatch between D and PHI>
+%!  so_step_armijo (1, 2, 3, dJ, baseData)
+
+% Test parameter validation.
+%!error <invalid Armijo relaxation parameter>
+%!  data = baseData;
+%!  data.p.lineSearch.relaxation = 0;
+%!  so_step_armijo (1, d, f, dJ, data);
+%!error <invalid Armijo relaxation parameter>
+%!  data = baseData;
+%!  data.p.lineSearch.relaxation = 1;
+%!  so_step_armijo (1, d, f, dJ, data);
+%!error <invalid backtracking factor>
+%!  data = baseData;
+%!  data.p.lineSearch.backtrack = 0;
+%!  so_step_armijo (1, d, f, dJ, data);
+%!error <invalid backtracking factor>
+%!  data = baseData;
+%!  data.p.lineSearch.backtrack = 1;
+%!  so_step_armijo (1, d, f, dJ, data);
+%!error <invalid initial increase factor>
+%!  data = baseData;
+%!  data.p.lineSearch.initial = 1;
+%!  so_step_armijo (1, d, f, dJ, data);
+%!error <invalid minimum step length>
+%!  data = baseData;
+%!  data.p.lineSearch.minStep = -1;
+%!  so_step_armijo (1, d, f, dJ, data);
+%!test
+%!  data = baseData;
+%!  data.p.lineSearch.minStep = 0;
+%!  so_step_armijo (1, d, f, dJ, data);
+
+% Test that backtracking works.  Set the parameters such that
+% backtracking is actually required.
+%!test
+%!  data = baseData;
+%!  data.p.verbose = true;
+%!  data.p.lineSearch.relaxation = 0.9;
+%!  [~, t] = so_step_armijo (1, d, f, dJ, data);
+%!  assert (t < 0.5);
+
+% Test error handling.  With a large step, we trigger
+% the "volume too large" error above.  Verify that the
+% error is handled both with and without multi-threading.
+%
+% For evolution time t, the resulting volume will be 5 + 4 t.
+% Thus the volume constraint is hit at t = 2.5.
+%
+% Note that we don't get 'Subprocess error' prefixed.  This is
+% presumably due to how Octave handles the error checks in
+% combination with the forks induced by multi-threading.
+%
+%!test
+%!  so_step_armijo (2, d, f, dJ, baseData);
+%!error <volume too large>
+%!  so_step_armijo (3, d, f, dJ, baseData);
+%!error <volume too large>
+%!  pkg load parallel;
+%!  data = baseData;
+%!  data.p.nProc = 2;
+%!  so_step_armijo (3, d, f, dJ, data);
diff --git a/inst/upwind_gradient_norm.m b/inst/upwind_gradient_norm.m
new file mode 100644
index 0000000..29de853
--- /dev/null
+++ b/inst/upwind_gradient_norm.m
@@ -0,0 +1,140 @@
+##  Copyright (C) 2015  Daniel Kraft <d at domob.eu>
+##  GNU Octave level-set package.
+##
+##  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 3 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, see <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {Function File} {@var{gnorm} =} upwind_gradient_norm (@var{phi}, @var{h} = 1)
+## @deftypefnx {Function File} {@var{gnorm} =} upwind_gradient_norm (@var{phi}, @var{f}, @var{h} = 1)
+## 
+## Approximate the gradient norm of @var{phi} using an upwind scheme.
+## The scheme chosen is appropriate to propagate the level-set equation
+## @tex
+## \begin{equation*}
+##    \phi_t + f \left| \nabla \phi \right| = 0
+## \end{equation*}
+## @end tex
+## @ifnottex
+##
+## @example
+## d/dt phi + f | grad phi | = 0
+## @end example
+##
+## @end ifnottex
+## in time.  If the argument @var{f} is given, its sign is used to
+## find the correct upwind direction.  If it is not present, positive
+## sign is assumed throughout the domain.  @var{h} gives the grid
+## spacing to use for the difference approximation.
+##
+## @var{phi} can be an array of arbitrary dimension, and @var{gnorm}
+## is always of the same size as @var{phi}.  If @var{f} is given, it
+## must also be of the same size as @var{phi}.
+##
+## The gradient is approximated with finite differences, either the
+## forward or backward difference quotient.  The direction chosen at
+## each grid point depends on the sign of f and  the gradient of phi
+## at that point.  The approximation used is from section 6.4 of
+##
+## J. A. Sethian:  Level Set Methods and Fast Marching Methods, second edition,
+## 1999.  Cambridge Monographs on Applied and Computational Mathematics,
+## Cambridge University Press.
+##
+## @seealso{ls_time_step}
+## @end deftypefn
+
+function gnorm = upwind_gradient_norm (phi, second, third)
+  switch nargin ()
+    case 1
+      F = ones (size (phi));
+      h = 1;
+    case 2
+      if (isscalar (second))
+        F = ones (size (phi));
+        h = second;
+      else
+        F = second;
+        h = 1;
+      endif
+    case 3
+      F = second;
+      h = third;
+    otherwise
+      print_usage ();
+  endswitch
+
+  if (!isscalar (h))
+    print_usage ();
+  endif
+
+  if (~all (size (phi) == size (F)))
+    error ("PHI and F must be of the same size");
+  endif
+
+  gnorm = __levelset_upwindGrad (phi, F, h);
+endfunction
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Tests.
+
+% Test for error conditions.
+%!error <Invalid call to>
+%!  upwind_gradient_norm ()
+%!error <Invalid call to>
+%!  upwind_gradient_norm (1, 2, 3, 4)
+%!error <Invalid call to>
+%!  upwind_gradient_norm ([1, 1], [2, 2], [3, 3])
+%!error <PHI and F must be of the same size>
+%!  upwind_gradient_norm ([1, 1], [2, 2, 2])
+%!error <PHI and F must be of the same size>
+%!  upwind_gradient_norm ([1, 1], [2, 2, 2], 5)
+
+% Basic test in 1D, check that the correct direction is chosen.
+%!test
+%!  phi = [0, 2, 3];
+%!  fPos = ones (size (phi));
+%!
+%!  % Propagate the original phi to the right.  This should result
+%!  % in the *backward* difference to be used in the middle.
+%!  g = upwind_gradient_norm (phi);
+%!  assert (g(2), 2);
+%!  gp = upwind_gradient_norm (phi, fPos);
+%!  assert (gp, g);
+%!  gp = upwind_gradient_norm (phi, 0.5);
+%!  assert (gp, 2 * g);
+%!  gp = upwind_gradient_norm (phi, fPos, 0.5);
+%!  assert (gp, 2 * g);
+%!
+%!  % Propagate to the left for the reverse effect.
+%!  g = upwind_gradient_norm (phi, -fPos);
+%!  assert (g(2), 1);
+%!
+%!  % Propagate -phi, which again turns everything around.
+%!  g = upwind_gradient_norm (-phi, fPos);
+%!  assert (g(2), 1);
+%!  g = upwind_gradient_norm (-phi, -fPos);
+%!  assert (g(2), 2);
+
+% Test in 3D with a quadratic function, where we know the resulting
+% expected gradient norm.
+%!test
+%!  n = 50;
+%!  x = linspace (-1, 1, n);
+%!  h = x(2) - x(1);
+%!  [XX, YY, ZZ] = ndgrid (x, x, x);
+%!
+%!  phi = XX.^2 + YY.^2 + ZZ.^2;
+%!  g = upwind_gradient_norm (phi, h);
+%!  
+%!  r = sqrt (phi);
+%!  assert (g, 2 * r, 0.1);
diff --git a/src/FastMarching.cpp b/src/FastMarching.cpp
new file mode 100644
index 0000000..22ef9d2
--- /dev/null
+++ b/src/FastMarching.cpp
@@ -0,0 +1,417 @@
+/*
+    GNU Octave level-set package.
+    Copyright (C) 2013-2015  Daniel Kraft <d at domob.eu>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "FastMarching.hpp"
+
+#include <algorithm>
+#include <cmath>
+#include <memory>
+#include <stdexcept>
+
+namespace fastMarching
+{
+
+/* Tolerance for safety checks.  */
+static const realT ASSERT_TOL = 1.0e-9;
+
+/* ************************************************************************** */
+/* Update equation class.  */
+
+/**
+ * Destruct and free all memory held in the entries map.
+ */
+UpdateEquation::~UpdateEquation ()
+{
+  for (auto e : entries)
+    delete e.second;
+}
+
+/**
+ * Add a new entry together with g.
+ * @param d Dimension along which the point neighbours the centre.
+ * @param h Grid spacing to the neighbour.
+ * @param u Value of the distance function at the neighbour.
+ * @param g Value of the extended function at the neighbour.
+ */
+void
+UpdateEquation::add (dimensionT d, realT h, realT u, realT g)
+{
+  std::unique_ptr<EqnEntry> newEntry(new EqnEntry (h, u, g));
+
+  auto iter = entries.find (d);
+  if (iter == entries.end ())
+    {
+      entries.insert (std::make_pair (d, newEntry.release ()));
+      return;
+    }
+
+  if (iter->second->getEffDistance () > newEntry->getEffDistance ())
+    {
+      delete iter->second;
+      iter->second = newEntry.release ();
+    }
+}
+
+/**
+ * Calculate the new distance value solving the quadratic equation.
+ * @param f Speed value at the current cell.
+ * @return Distance value at the centre cell.
+ */
+realT
+UpdateEquation::solve (realT f) const
+{
+  sortedEntries.clear ();
+
+  /* First, sort the entries by distance.  */
+  for (const auto& e : entries)
+    sortedEntries.push_back (e.second);
+  std::sort (sortedEntries.begin (), sortedEntries.end (), &EqnEntry::compare);
+
+  /*
+  The equation we solve is:
+
+    (X - N_1)^2 / h_1^2 + (X - N_2)^2 / h_2^2 + ... = f^2,
+
+  where N_i are the smaller, alive neighbours in each direction.  During
+  the loop, we build up the coefficients accordingly.
+  */
+      
+  realT a, b, c;
+  realT d;
+
+  while (true)
+    {
+      assert (!sortedEntries.empty ());
+
+      a = 0.0;
+      b = 0.0;
+      c = -std::pow (f, 2);
+
+      for (const auto cur : sortedEntries)
+        {
+          const realT invH2 = std::pow (cur->h, -2);
+          a += invH2;
+          b -= 2.0 * cur->u * invH2;
+          c += std::pow (cur->u, 2) * invH2;
+        }
+
+      d = std::pow (b, 2) - 4.0 * a * c;
+      if (d >= 0.0)
+        break;
+
+      issueWarning ("level-set:fast-marching:too-far-alive",
+                    "found too far alive point in fast marching, d = %f", d);
+      sortedEntries.pop_back ();
+    }
+
+  /* Now the equation is solvable, actually do it and take the larger
+     of the solutions.  */
+  u = (-b + sqrt (d)) / (2.0 * a);
+
+  return u;
+}
+
+/**
+ * Calculate the value of the extended function at the centre.  This
+ * can only be used if all g values were given for all entries, and
+ * if solve() was already called.
+ * @return The value of the extended function at the centre.
+ * @throws std::logic_error if the equation was built without all g's.
+ * @throws std::logic_error if solve() was not yet called.
+ */
+realT
+UpdateEquation::getExtendedFunction () const
+{
+  if (gMissing)
+    throw std::logic_error ("not all values of g given in update equation");
+  if (sortedEntries.empty ())
+    throw std::logic_error ("update equation is not yet solved");
+
+  /*
+  For the extended function g, the equation to solve is:
+
+    (G - G_1) (U - N_1) / h_1^2 + (G - G_2) (U - N_2) / h_2^2 + ... = 0,
+
+  which is even linear in G since we know U already.  We solve this equation
+  in the form "a G = b".
+  */
+
+  realT a = 0.0;
+  realT b = 0.0;
+  for (const auto cur : sortedEntries)
+    {
+      const realT invH2 = std::pow (cur->h, -2);
+      const realT grad = u - cur->u;
+      a += grad * invH2;
+      b += cur->g * grad * invH2;
+    }
+
+  assert (a > 0.0);
+  return b / a;
+}
+
+/* ************************************************************************** */
+/* Single cell entry.  */
+
+/**
+ * Set state to alive.
+ * @see setNarrowBand
+ */
+void
+Entry::setAlive ()
+{
+  /* Note that this routine is only used to set narrow_band -> alive, since
+     far_away -> narrow_band must be done with setNarrowBand.  */
+  assert (state == NARROW_BAND);
+  state = ALIVE;
+
+  assert (heapEntry);
+  heapEntry = nullptr;
+}
+
+/**
+ * Set state to narrow band, adding also the heap entry.
+ * @param e Heap entry.
+ * @see setState
+ */
+void
+Entry::setNarrowBand (const heapT::Entry& e)
+{
+  assert (state == FAR_AWAY);
+  state = NARROW_BAND;
+
+  assert (!heapEntry);
+  heapEntry = e;
+}
+
+/**
+ * Update distance value from the neighbours.  They are found
+ * using our coordinates and the given grid.  This may raise assertion
+ * failures under certain conditions.
+ * @param grid The grid to use to access neighbours.
+ */
+void
+Entry::update (const Grid& grid)
+{
+  assert (state == NARROW_BAND);
+
+  /*
+  Go over all neighbours and remember in each dimension the smaller of
+  the neighbour values.  We only use alive values as suggested in the
+  technical report of J. A. Baerentzen.
+  */
+
+  UpdateEquation eqn;
+  const auto buildEqn
+    = [&eqn, &grid] (const IndexTuple& neighbour, dimensionT d, indexT)
+    {
+      const Entry* e = grid.get (neighbour);
+      if (e && e->state == ALIVE && e->u < LARGE_DIST)
+        eqn.add (d, 1.0, e->u, e->g);
+    };
+  grid.iterateNeighbours (coord, buildEqn);
+
+  /* Solve the equation.  */
+  const realT newU = eqn.solve (f);
+  g = eqn.getExtendedFunction ();
+
+  /* Update.  Usually the distance should be smaller, but in certain
+     circumstances we want to accomodate also for increases correcting
+     prior inaccuracies (and inaccuracies in the initialisation possibly).  */
+  bool bubbleDown = false;
+  if (newU > u)
+    {
+      if (newU > u + ASSERT_TOL)
+        issueWarning ("level-set:fast-marching:increased-distance",
+                      "increased distance in fast marching %f to %f", u, newU);
+      bubbleDown = true;
+    }
+  u = newU;
+
+  /* Bubble up the heap.  */
+  assert (heapEntry);
+  if (bubbleDown)
+    heapEntry->bubbleDown ();
+  else
+    heapEntry->bubbleUp ();
+}
+
+/* ************************************************************************** */
+/* Full grid.  */
+
+/**
+ * Calculate index into entries for given coordinates.
+ * @param c Coordinates in indices.
+ * @return Single index into entries or -1 if c is out of range.
+ */
+indexT
+Grid::lineariseIndex (const IndexTuple& c) const
+{
+  assert (size.size () == c.size ());
+
+  indexT res = 0;
+  for (dimensionT dim = 0; dim < size.size (); ++dim)
+    {
+      if (c[dim] < 0 || c[dim] >= size[dim])
+        return -1;
+      res *= size[dim];
+      res += c[dim];
+    }
+
+  return res;
+}
+
+/**
+ * Perform a single update step on the currently best next narrow-band
+ * entry.  It is removed and set alive, and its neighbours are then
+ * added into the narrow-band if not yet there and updated.
+ */
+void
+Grid::update ()
+{
+  assert (!narrowBand.empty ());
+
+  Entry* top = **narrowBand.top ();
+  narrowBand.pop ();
+
+  top->setAlive ();
+  recalculateNeighbours (*top);
+}
+
+/**
+ * Recalculate neighbours of a given entry.  This is used both for
+ * update and initialisation of narrow band.
+ * @param center Recalculate this entry's neighbours.
+ */
+void
+Grid::recalculateNeighbours (const Entry& center)
+{
+  IndexTuple neighbour = center.getCoordinate ();
+  const auto updateIt
+    = [this] (const IndexTuple& neighbour, dimensionT, indexT)
+    {
+      Entry* e = get (neighbour);
+      if (!e)
+        return;
+
+      switch (e->getState ())
+        {
+          case Entry::FAR_AWAY:
+            /* Note:  The heap push below inserts first at the very
+               bottom of the heap and needs no bubbling up, since the
+               distance is currently LARGE_DIST.  It will be bubbled
+               only in the update below.  */
+            {
+              const auto heapEntry = narrowBand.push (e);
+              e->setNarrowBand (heapEntry);
+            }
+            /* Fall through.  */
+          case Entry::NARROW_BAND:
+            e->update (*this);
+            break;
+
+          default:
+            /* Nothing to do.  */
+            break;
+        }
+    };
+  iterateNeighbours (center.getCoordinate (), updateIt);
+}
+
+/**
+ * Construct the grid with a given size.  The entries will be empty
+ * and will still have to be filled in.
+ * @param s Size to use.
+ */
+Grid::Grid (const IndexTuple& s)
+  : size(s), entries(), narrowBand(&Entry::compareForHeap)
+{
+  indexT fullSize = 1;
+  for (auto i : s)
+    fullSize *= i;
+
+  entries.resize (fullSize);
+  for (auto& p : entries)
+    p = nullptr;
+
+  assert (narrowBand.empty ());
+}
+
+/**
+ * Destructor, freeing all memory.
+ */
+Grid::~Grid ()
+{
+  /* Narrow band heap only holds pointers, memory not owned there!  */
+
+  /* Free memory in entries.  */
+  for (auto p : entries)
+    if (p)
+      delete p;
+  entries.clear ();
+}
+
+/**
+ * Initialise a given grid cell by the given entry.  The coordinate
+ * where to put the entry is taken from the object itself, and an
+ * error is raised if that entry is already filled in.  The entries
+ * state must be either "alive" or "far away", narrow band is
+ * automatically initialised later on.
+ * @param e The new entry.  Ownership is taken over.
+ * @throws std::invalid_argument if something is wrong with e.
+ */
+void
+Grid::setInitial (Entry* e)
+{
+  if (!e)
+    throw std::invalid_argument ("entry is NULL");
+  if (e->getState () != Entry::ALIVE && e->getState () != Entry::FAR_AWAY)
+    throw std::invalid_argument ("only alive and far-away entries can be"
+                                 " set initially");
+
+  const IndexTuple& indTuple = e->getCoordinate ();
+  const indexT ind = lineariseIndex (indTuple);
+
+  if (ind < 0)
+    throw std::invalid_argument ("invalid coordinate of entry");
+  if (entries[ind])
+    throw std::invalid_argument ("duplicate entry set on grid");
+
+  entries[ind] = e;
+}
+
+/**
+ * Perform the calculation, updating all narrow-band entries until
+ * all of them are alive.  It is necessary to have narrow-band already
+ * filled in, meaning that far-away entries not reached by the propagation
+ * from any narrow-band will not be updated!
+ */
+void
+Grid::perform ()
+{
+  for (const auto center : entries)
+    if (center && center->getState () == Entry::ALIVE)
+      recalculateNeighbours (*center);
+
+  while (!narrowBand.empty ())
+    update ();
+}
+
+/* ************************************************************************** */
+
+} // Namespace fastMarching.
diff --git a/src/FastMarching.hpp b/src/FastMarching.hpp
new file mode 100644
index 0000000..fef8e96
--- /dev/null
+++ b/src/FastMarching.hpp
@@ -0,0 +1,616 @@
+/*
+    GNU Octave level-set package.
+    Copyright (C) 2013-2014  Daniel Kraft <d at domob.eu>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef FASTMARCHING_HPP
+#define FASTMARCHING_HPP
+
+/*
+
+Implementation of the fast marching method to numerically solve the
+Eikonal equation:
+
+  | grad u | = f
+
+f is assumed to be positive, and also assumed to include the grid spacing
+already (so that 1 is used as unit distance between grid points).
+For solving problems with changing sign of f,
+the domain has to be split into parts with constant sign of f, and if f
+is negative, a corresponding transformation on f and u has to be performed
+to make f positive.
+
+The base grid is rectangular and assumed to "hold all" of the domain,
+but it is not necessary to set F on the full grid to meaningful values.
+Only for cells which are not initially marked as "alive" are the
+neighbours actually ever accessed, and points outside the domain of
+interest can be set NULL.  Those will be assumed to be always
+too far away to influence interior points, so in particular if f -> infinity
+towards the boundary we can really ignore those points.
+
+Each grid cell is represented by a pointed-to object, so that we can easily
+store pointers to those objects in the grid to access them by indices and
+find their neighbours, and also in the heap structure used to find the
+next "narrow band" point to operate on.
+
+Additionally, it is possible to extend a function g known on the domain boundary
+"along" the normal direction, such that
+
+  grad u * grad g = 0.
+
+*/
+
+#include "Heap.hpp"
+
+#include <cassert>
+#include <limits>
+#include <map>
+#include <vector>
+
+namespace fastMarching
+{
+
+class Entry;
+class Grid;
+
+/** Type used for real numbers.  */
+typedef double realT;
+
+/** Define value used to represent "very large" values.  */
+static const realT LARGE_DIST = std::numeric_limits<realT>::max ();
+
+/**
+ * Type used to number entries.  Signed values are allowed in order to allow
+ * -1 to be a "valid" index.  The grid takes care of out-of-range values.
+ */
+typedef int indexT;
+
+/**
+ * Full set of indices.  To be precise, this contains N indexT grouped
+ * together, where N is the space dimension we solve the equation in.
+ */
+typedef std::vector<indexT> IndexTuple;
+
+/** Type used to number the dimensions.  */
+typedef IndexTuple::size_type dimensionT;
+
+/** Type used for the heap.  */
+typedef Heap<Entry*, bool (*)(const Entry*, const Entry*)> heapT;
+
+/**
+ * Method to issue warnings.  This is not implemented in FastMarching.cpp
+ * but must be provided by the caller.  That way, it can use Octave's
+ * warning infrastructure but still make the FastMarching code itself
+ * independent of Octave.
+ * @param id An ID for the warning.
+ * @param fmt The (printf-like) message template.
+ * @param ... Other arguments for printf-like formatting.
+ */
+void issueWarning (const std::string& id, const std::string& fmt, ...);
+
+/* ************************************************************************** */
+/* Update equation class.  */
+
+/**
+ * Represent and solve the quadratic equation necessary in the fast marching
+ * step for updating the distance of a point from given neighbours.  This
+ * is also useful for initialising the narrow-band entries from the level-set
+ * function, and thus available as global class.
+ */
+class UpdateEquation
+{
+
+private:
+
+  class EqnEntry;
+
+  /**
+   * Map space dimensions to the currently best entry along the given
+   * dimension.  It may be null if there's not yet an acceptable entry
+   * there, and there may be multiple entries added for the same dimension
+   * (on both sides of our point).  In this case, only the closer one is
+   * relevant, and will be kept in the map.
+   */
+  typedef std::map<dimensionT, EqnEntry*> entryMapT;
+
+  /** The actual map.  */
+  entryMapT entries;
+
+  /**
+   * Remember whether one of the g values was being absent.  If that's
+   * the case, we can't calculate g from the equation.  This is enforced.
+   */
+  bool gMissing;
+
+  /**
+   * When solving, keep track of the actually used and sorted neighbours
+   * for calculating g later on.
+   */
+  mutable std::vector<const EqnEntry*> sortedEntries;
+
+  /** Store calculated distance value here.  */
+  mutable realT u;
+
+public:
+
+  /**
+   * Construct it empty.
+   */
+  inline UpdateEquation ()
+    : entries (), gMissing(false), sortedEntries(), u(0.0)
+  {}
+
+  /**
+   * Destruct and free all memory held in the entries map.
+   */
+  ~UpdateEquation ();
+
+  // No copying.
+  UpdateEquation (const UpdateEquation&) = delete;
+  UpdateEquation& operator= (const UpdateEquation&) = delete;
+
+  /**
+   * Add a new entry.  The entry is characterised by the dimension along
+   * which it neighbours the cell being calculated, the distance (grid spacing)
+   * to it and the distance value at the neighbour.  Using this function
+   * without a parameter for g flags the equation as missing g values,
+   * which means that getExtendedFunction does not work.
+   * @param d Dimension along which the point neighbours the centre.
+   * @param h Grid spacing to the neighbour.
+   * @param u Value of the distance function at the neighbour.
+   */
+  inline void
+  add (dimensionT d, realT h, realT u)
+  {
+    add (d, h, u, 0.0);
+    gMissing = true;
+  }
+
+  /**
+   * Add a new entry together with g.
+   * @param d Dimension along which the point neighbours the centre.
+   * @param h Grid spacing to the neighbour.
+   * @param u Value of the distance function at the neighbour.
+   * @param g Value of the extended function at the neighbour.
+   */
+  void add (dimensionT d, realT h, realT u, realT g);
+
+  /**
+   * Calculate the new distance value solving the quadratic equation.
+   * @param f Speed value at the current cell.
+   * @return Distance value at the centre cell.
+   */
+  realT solve (realT f) const;
+
+  /**
+   * Calculate the value of the extended function at the centre.  This
+   * can only be used if all g values were given for all entries, and
+   * if solve() was already called.
+   * @return The value of the extended function at the centre.
+   * @throws std::logic_error if the equation was built without all g's.
+   * @throws std::logic_error if solve() was not yet called.
+   */
+  realT getExtendedFunction () const;
+
+};
+
+/**
+ * The equation entry class.  This simply holds some data.
+ */
+class UpdateEquation::EqnEntry
+{
+
+public:
+
+  /** The neighbour's distance on the grid.  */
+  realT h;
+
+  /** The neighbour's distance value.  */
+  realT u;
+
+  /** The neighbour's extended function value.  */
+  realT g;
+
+  /* Note that we don't need the dimension, since this is already part
+     of the map in UpdateEquation anyway.  */
+
+  /**
+   * Construct it given all the data.
+   * @param mh Grid distance.
+   * @param mu Distance value.
+   * @param mg Extended function value.
+   */
+  inline EqnEntry (realT mh, realT mu, realT mg)
+    : h(mh), u(mu), g(mg)
+  {}
+
+  // No copying or default constructor, since it is not needed.
+  EqnEntry () = delete;
+  EqnEntry (const EqnEntry&) = delete;
+  EqnEntry& operator= (const EqnEntry&) = delete;
+
+  /**
+   * Get the "effective distance" that this point would imply for the
+   * centre, which is the distance if only this point would be there.  It
+   * is used for deciding which one of two neighbours along a single dimension
+   * is the one to use, and also for sorting later on.
+   * @return The effective distance, which is u + h.
+   */
+  inline realT
+  getEffDistance () const
+  {
+    /* Note:  The roles of u and h in the quadratic equation being solved
+       are not entirely the same, so it is not clear whether this
+       "effective distance" is really the right criterion to decide upon which
+       point to use.  However, in the cases actually used, either h is the
+       same along a dimension (grid spacing) or u is zero everywhere (initial
+       distances from the level-set function).  For those, this concept should
+       work at least.  */
+
+    return u + h;
+  }
+
+  /**
+   * Compare two entry pointers for sorting by distance.  If the distances
+   * are equal, we compare the pointers just to get a criterion to avoid
+   * false classification as being equal (which might confuse the sorting
+   * algorithm).
+   * @param a First pointer.
+   * @param b Second pointer.
+   * @return True iff a < b.
+   */
+  static inline bool
+  compare (const EqnEntry* a, const EqnEntry* b)
+  {
+    const realT diff = a->getEffDistance () - b->getEffDistance ();
+    if (diff != 0.0)
+      return (diff < 0.0);
+
+    return (a < b);
+  }
+
+};
+
+/* ************************************************************************** */
+/* Single cell entry.  */
+
+/**
+ * This class represents all data stored for a single grid point.  It also
+ * has the ability to do cell-related calculations, and update itself solving
+ * the respective quadratic equation.
+ */
+class Entry
+{
+
+public:
+
+  /** Possible states of a cell during processing.  */
+  enum State
+  {
+    ALIVE,
+    NARROW_BAND,
+    FAR_AWAY
+  };
+
+private:
+
+  /** Value of f at the given point.  */
+  const realT f;
+
+  /** Current value of u.  */
+  realT u;
+  /** Current value of the extended function g.  */
+  realT g;
+
+  /** Current state.  */
+  State state;
+
+  /**
+   * Index of this grid cell.  This is used to find the neighbours
+   * in the updating calculation.
+   */
+  const IndexTuple coord;
+
+  /** Entry into the heap in case we're in narrow band.  */
+  heapT::Entry heapEntry;
+
+  /**
+   * Construct the cell entry given the data.
+   * @param s Initial state.
+   * @param c Coordinate in the grid.
+   * @param mf Value of f at this position.
+   * @param mu Distance value.
+   * @param mg Value for extended function.
+   */
+  inline Entry (State s, const IndexTuple& c, realT mf, realT mu, realT mg)
+    : f(mf), u(mu), g(mg), state(s), coord(c), heapEntry(nullptr)
+  {
+    assert (s == ALIVE || s == FAR_AWAY);
+  }
+
+public:
+
+  /* No copying or default constructor.  */
+  Entry () = delete;
+  Entry (const Entry& o) = delete;
+  Entry& operator= (const Entry& o) = delete;
+
+  /**
+   * Construct the entry for an initially alive cell.
+   * @param c Coordinate for it.
+   * @param u Initial distance.
+   * @param g Initial function value.
+   */
+  static inline
+  Entry* newAlive (const IndexTuple& c, realT u, realT g)
+  {
+    /* f is not necessary, set to dummy value.  */
+    return new Entry (ALIVE, c, 0.0, u, g);
+  }
+
+  /**
+   * Construct the entry for an initially far away cell.
+   * @param c Coordinate for it.
+   * @param f Value of f.
+   */
+  static inline
+  Entry* newFarAway (const IndexTuple& c, realT f)
+  {
+    /* g0 is not necessary (will be overwritten), set to dummy value.  */
+    return new Entry (FAR_AWAY, c, f, LARGE_DIST, 0.0);
+  }
+
+  /**
+   * Get current value of u.
+   * @return Current value of u for this cell.
+   */
+  inline realT
+  getDistance () const
+  {
+    return u;
+  }
+
+  /**
+   * Get value of extended function g.
+   * @return Current value of g for this cell.
+   */
+  inline realT
+  getFunction () const
+  {
+    return g;
+  }
+
+  /**
+   * Get index coordinate.  This is used by the grid to put the cell into
+   * the correct place when initialising.
+   * @return This entry's coordinate.
+   */
+  inline const IndexTuple&
+  getCoordinate () const
+  {
+    return coord;
+  }
+
+  /**
+   * Get the current state.
+   * @return The current state.
+   */
+  inline State
+  getState () const
+  {
+    return state;
+  }
+
+  /**
+   * Set state to alive.
+   * @see setNarrowBand
+   */
+  void setAlive ();
+
+  /**
+   * Set state to narrow band, adding also the heap entry.
+   * @param e Heap entry.
+   * @see setState
+   */
+  void setNarrowBand (const heapT::Entry& e);
+
+  /**
+   * Update distance value from the neighbours.  They are found
+   * using our coordinates and the given grid, and the heap is
+   * automatically bubbled for the change to take place.
+   * This may raise assertion failures under certain conditions.
+   * @param grid The grid to use to access neighbours.
+   */
+  void update (const Grid& grid);
+
+  /**
+   * Compare two elements as they should be compared for the heap
+   * data structure.  Since the heap has the largest element first and
+   * that should correspond to smallest distance, return ordering
+   * induced by ">" on u.  Because the heap
+   * will be based on pointers to entries, this function also works on them.
+   * @param a First entry.
+   * @param b Second entry.
+   * @return True iff a should be accessed later than b, which is equivalent
+   *         to a.u > b.u.
+   */
+  static inline bool
+  compareForHeap (const Entry* a, const Entry* b)
+  {
+    assert (a && b);
+    return a->u > b->u;
+  }
+
+};
+
+/* ************************************************************************** */
+/* Full grid.  */
+
+/**
+ * This class represents a full grid, which also keeps track of the high-level
+ * data structures during the solve.
+ */
+class Grid
+{
+
+private:
+
+  /** Size of the grid.  */
+  const IndexTuple size;
+
+  /**
+   * Store pointers to the grid entries "by index" here.  This is the
+   * multi-dimensional array mapped to a single dimension.  Some entries
+   * may be NULL, which means that those are outside of the domain
+   * we're interested in.
+   */
+  std::vector<Entry*> entries;
+
+  /** Keep track of points currently in "narrow band" state.  */
+  heapT narrowBand;
+
+  /**
+   * Calculate index into entries for given coordinates.
+   * @param c Coordinates in indices.
+   * @return Single index into entries or -1 if c is out of range.
+   */
+  indexT lineariseIndex (const IndexTuple& c) const;
+
+  /**
+   * Perform a single update step on the currently best next narrow-band
+   * entry.  It is removed and set alive, and its neighbours are then
+   * added into the narrow-band if not yet there and updated.
+   */
+  void update ();
+
+  /**
+   * Recalculate neighbours of a given entry.  This is used both for
+   * update and initialisation of narrow band.
+   * @param center Recalculate this entry's neighbours.
+   */
+  void recalculateNeighbours (const Entry& center);
+
+  /**
+   * Get grid cell by index, mutable version for internal use.
+   * @param c Index coordinate tuple.
+   * @return The corresponding entry pointer.
+   * @see get (const IndexTuple&)
+   */
+  inline Entry*
+  get (const IndexTuple& c)
+  {
+    const indexT ind = lineariseIndex (c);
+    if (ind < 0)
+      return nullptr;
+    return entries[ind];
+  }
+
+  /**
+   * Internal routine used for iterate(), that is called recursively
+   * to perform the required number of nested loops.
+   * @param cur Current index tuple, will be built up recursively.
+   * @param depth Current depth in the recursion.
+   * @param f The call-back to call for each cell index.
+   */
+  template<typename F>
+    void iterate (IndexTuple& cur, dimensionT depth, const F& f) const;
+
+public:
+
+  /* No copying or default constructor.  */
+  Grid () = delete;
+  Grid (const Grid& o) = delete;
+  Grid& operator= (const Grid& o) = delete;
+
+  /**
+   * Construct the grid with a given size.  The entries will be empty
+   * and will still have to be filled in.
+   * @param s Size to use.
+   */
+  Grid (const IndexTuple& s);
+
+  /**
+   * Destructor, freeing all memory.
+   */
+  ~Grid ();
+
+  /**
+   * Initialise a given grid cell by the given entry.  The coordinate
+   * where to put the entry is taken from the object itself, and an
+   * error is raised if that entry is already filled in.  The entries
+   * state must be either "alive" or "far away", narrow band is
+   * automatically initialised later on.
+   * @param e The new entry.  Ownership is taken over.
+   * @throws std::invalid_argument if something is wrong with e.
+   */
+  void setInitial (Entry* e);
+
+  /**
+   * Get grid cell by index.  Returned is the pointer, which may be NULL
+   * in case this cell is outside the domain or even the index range given
+   * outside the size.
+   * @param c Index coordinate tuple.
+   * @return The corresponding entry pointer.
+   */
+  inline const Entry*
+  get (const IndexTuple& c) const
+  {
+    const indexT ind = lineariseIndex (c);
+    if (ind < 0)
+      return nullptr;
+    return entries[ind];
+  }
+
+  /**
+   * Perform the calculation, updating all narrow-band entries until
+   * all of them are alive.  It is necessary to have narrow-band already
+   * filled in, meaning that far-away entries not reached by the propagation
+   * from any narrow-band will not be updated!
+   */
+  void perform ();
+
+  /**
+   * Iterate over all cells of the given grid and call the provided
+   * call-back routine with the coordinate as IndexTuple.
+   * @param f The call-back to call for each cell index.
+   */
+  template<typename F>
+    inline void
+    iterate (const F& f) const
+  {
+    IndexTuple cur(size.size ());
+    iterate (cur, 0, f);
+  }
+
+  /**
+   * Iterate over all neighbours of a given index.  For each neighbour that
+   * is still on the grid, call the provided function with its index.
+   * @param c The centre coordinate.
+   * @param f The call-back to call for each neighbour.
+   */
+  template<typename F>
+    void iterateNeighbours (const IndexTuple& c, const F& f) const;
+
+};
+
+/* ************************************************************************** */
+
+} // Namespace fastMarching.
+
+/* Include template implementations.  */
+#include "FastMarching.tpp"
+
+#endif /* Header guard.  */
diff --git a/src/FastMarching.tpp b/src/FastMarching.tpp
new file mode 100644
index 0000000..f917ce8
--- /dev/null
+++ b/src/FastMarching.tpp
@@ -0,0 +1,82 @@
+/*
+    GNU Octave level-set package.
+    Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Template implementations of FastMarching.hpp.  */
+
+namespace fastMarching
+{
+
+/* ************************************************************************** */
+/* Full grid.  */
+
+/**
+ * Internal routine used for iterate(), that is called recursively
+ * to perform the required number of nested loops.
+ * @param cur Current index tuple, will be built up recursively.
+ * @param depth Current depth in the recursion.
+ * @param f The call-back to call for each cell index.
+ */
+template<typename F>
+  void
+  Grid::iterate (IndexTuple& cur, dimensionT depth, const F& f) const
+{
+  const dimensionT D = size.size ();
+
+  assert (depth <= D);
+  if (depth == D)
+    {
+      f (cur);
+      return;
+    }
+
+  for (indexT i = 0; i < size[depth]; ++i)
+    {
+      cur[depth] = i;
+      iterate (cur, depth + 1, f);
+    }
+}
+
+/**
+ * Iterate over all neighbours of a given index.  For each neighbour that
+ * is still on the grid, call the provided function with its index,
+ * the dimension index along which they are neighbours, and the
+ * offset along this dimension (+1 or -1).
+ * @param c The centre coordinate.
+ * @param f The call-back to call for each neighbour.
+ */
+template<typename F>
+  void
+  Grid::iterateNeighbours (const IndexTuple& c, const F& f) const
+{
+  IndexTuple neighbour = c;
+  for (dimensionT d = 0; d < neighbour.size (); ++d)
+    {
+      const auto oldValue = neighbour[d];
+      for (neighbour[d] -= 1; neighbour[d] <= oldValue + 2;
+           neighbour[d] += 2)
+        {
+          if (lineariseIndex (neighbour) >= 0)
+            f (neighbour, d, neighbour[d] - oldValue);
+        }
+      neighbour[d] = oldValue;
+    }
+}
+
+/* ************************************************************************** */
+
+} // Namespace fastMarching.
diff --git a/src/Heap.hpp b/src/Heap.hpp
new file mode 100644
index 0000000..0fb80ff
--- /dev/null
+++ b/src/Heap.hpp
@@ -0,0 +1,262 @@
+/*
+    GNU Octave level-set package.
+    Copyright (C) 2013-2014  Daniel Kraft <d at domob.eu>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef HEAP_HPP
+#define HEAP_HPP
+
+/* Manual implementation of a binary heap, which is used by the fast marching
+   algorithm.  Unfortunately I can't use the STL provided stuff directly,
+   since we need "bubbling up" of values that were already in the heap and
+   that changed to a lesser distance.  Hence I implement my own.
+
+   Mapping from the linear array physically holding the elements to the
+   tree structure and back is performed by the Entry class.  It also
+   adds an additional layer of redirection so that we can store "permanent"
+   references to heap elements (pointers to the Entry objects) which are
+   not invalidated and keep pointing to the same element and also its physical
+   position inside the heap even when the heap is bubbled in some way.  */
+
+#include <cassert>
+#include <stdexcept>
+#include <vector>
+
+namespace fastMarching
+{
+
+/* ************************************************************************** */
+/* Heap class itself.  */
+
+/**
+ * This class represents the binary heap, with all the basic operations
+ * necessary for it.  As underlying store we use a std::vector for simplicity.
+ */
+template<typename T, typename C>
+  class Heap
+{
+
+  private:
+
+    class EntryData;
+
+    /**
+     * Type of array holding physical data and the "master" pointers to
+     * the memory allocated to entries.
+     */
+    typedef std::vector<EntryData*> entryArr;
+
+    /** Type indexing a physical entry.  */
+    typedef typename entryArr::size_type indexT;
+
+    /** Hold the elements.  */
+    entryArr data;
+
+    /** Our comparator functor.  */
+    const C compare;
+
+  public:
+
+    /** Pointer to an entry as it can be used from the outside.  */
+    typedef EntryData* Entry;
+
+    /* No copying for simplicity.  The default implementations should work,
+       but it is not needed.  */
+    Heap () = delete;
+    Heap (const Heap& o) = delete;
+    Heap& operator= (const Heap& o) = delete;
+
+    /**
+     * Construct an empty heap with the given comparator.
+     * @param c Comparator object to use.
+     */
+    explicit inline Heap (const C& c)
+      : data(), compare(c)
+    {}
+
+    /**
+     * Destruct freeing all memory.
+     */
+    ~Heap ();
+
+    /**
+     * Get entry at the top of the heap.
+     * @return Entry reference to top of heap.
+     * @throws std::out_of_range if the heap is empty.
+     */
+    inline Entry
+    top ()
+    {
+      if (empty ())
+        throw std::out_of_range ("top() for empty heap");
+
+      return data.front ();
+    }
+
+    /**
+     * Pop off and remove the top.
+     * @throws std::out_of_range if the heap is empty.
+     */
+    void pop ();
+
+    /**
+     * Push a new element and return its entry.
+     * @param e The new element.
+     * @return Entry reference to it after finding the correct position.
+     */
+    Entry push (const T& e);
+
+    /**
+     * Bubble an element pointed to by the entry up to the correct position.
+     * @param e Entry to bubble up.
+     * @throws std::invalid_argument if the element belongs not to this heap.
+     */
+    void bubbleUp (Entry e);
+
+    /**
+     * Bubble an element pointed to by the entry down to the correct position.
+     * @param e Entry to bubble down.
+     * @throws std::invalid_argument if the element belongs not to this heap.
+     */
+    void bubbleDown (Entry e);
+
+    /**
+     * Query whether the heap is empty or not.
+     * @return True iff no elements are present.
+     */
+    inline bool
+    empty () const
+    {
+      return data.empty ();
+    }
+
+};
+
+/* ************************************************************************** */
+/* Heap entry class.  */
+
+/**
+ * Represent an entry in the heap.  This holds both the actual data and
+ * also the position in the heap vector.  It allows accessing parent nodes
+ * and children in the binary tree.
+ */
+template<typename T, typename C>
+  class Heap<T, C>::EntryData
+{
+
+  private:
+
+    friend class Heap;
+
+    /** Type of parent heap.  */
+    typedef Heap<T, C> parentT;
+
+    /** The heap we're part of.  */
+    parentT& parent;
+
+    /** Piece of data hold.  */
+    const T data;
+
+    /** Current index into the heap array.  */
+    parentT::indexT index;
+
+    /**
+     * Construct it, given the parent heap, data entry and index.
+     * @param h Parent heap.
+     * @param d Data entry.
+     * @param i Index.
+     */
+    inline EntryData (parentT& h, const T& d, parentT::indexT i)
+      : parent(h), data(d), index(i)
+    {}
+
+    /**
+     * Verify that the subtree belonging to this entry is in fact really
+     * a heap satisfying the invariant.  An assertion failure is thrown if not.
+     */
+    void verifySubtree ();
+
+    /**
+     * Swap two entries with each other in the vector.  This is a utility
+     * routine, which updates the vector holding the entries as well
+     * as updating the indices.  Of course, they must belong to the same
+     * parent heap.
+     * @param a First entry.
+     * @param b Second entry.
+     */
+    static void swap (EntryData& a, EntryData& b);
+
+  public:
+
+    /* No copying, users should use pointers to this one.  */
+    EntryData () = delete;
+    EntryData (const EntryData& o) = delete;
+    EntryData& operator= (const EntryData& o) = delete;
+
+    /* No destructor necessary.  */
+
+    /**
+     * Access data element.
+     * @return Data element.
+     */
+    inline const T&
+    operator* () const
+    {
+      return data;
+    }
+
+    /**
+     * Get pointer to entry of parent in the tree.
+     * @return Parent in the tree as Entry or NULL if we're on top.
+     */
+    Entry up () const;
+
+    /**
+     * Get pointer to one of the childs in the tree.
+     * @param c Which child to get (0 or 1).
+     * @return Selected child as Entry or NULL if there's no such child.
+     * @throws std::invalid_argument if c is not 0 or 1.
+     */
+    Entry down (parentT::indexT c) const;
+
+    /**
+     * Bubble up this entry in the containing heap.
+     */
+    inline void
+    bubbleUp ()
+    {
+      parent.bubbleUp (this);
+    }
+
+    /**
+     * Bubble down this entry in the containing heap.
+     */
+    inline void
+    bubbleDown ()
+    {
+      parent.bubbleDown (this);
+    }
+
+};
+
+/* ************************************************************************** */
+
+} // Namespace fastMarching.
+
+/* Include template implementations.  */
+#include "Heap.tpp"
+
+#endif /* Header guard.  */
diff --git a/src/Heap.tpp b/src/Heap.tpp
new file mode 100644
index 0000000..2dd3e5b
--- /dev/null
+++ b/src/Heap.tpp
@@ -0,0 +1,238 @@
+/*
+    GNU Octave level-set package.
+    Copyright (C) 2013-2014  Daniel Kraft <d at domob.eu>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Template implementations of Heap.hpp.  */
+
+#define AGGRESSIVE_CHECKS 0
+
+namespace fastMarching
+{
+
+/* ************************************************************************** */
+/* Heap class itself.  */
+
+/**
+ * Destruct freeing all memory.
+ */
+template<typename T, typename C>
+  Heap<T, C>::~Heap ()
+{
+  for (auto e : data)
+    delete e;
+}
+
+/**
+ * Pop off and remove the top.
+ * @throws std::out_of_range if the heap is empty.
+ */
+template<typename T, typename C>
+  void
+  Heap<T, C>::pop ()
+{
+  if (empty ())
+    throw std::out_of_range ("pop() for empty heap");
+
+  if (data.front () == data.back ())
+    {
+      assert (data.size () == 1);
+      delete data.back ();
+      data.pop_back ();
+      return;
+    }
+
+  assert (data.size () > 1);
+  EntryData::swap (*data.front (), *data.back ());
+
+  delete data.back ();
+  data.pop_back ();
+
+  bubbleDown (top ());
+
+#if AGGRESSIVE_CHECKS
+  top ()->verifySubtree ();
+#endif
+}
+
+/**
+ * Push a new element and return its entry.
+ * @param e The new element.
+ * @return Entry reference to it after finding the correct position.
+ */
+template<typename T, typename C>
+  typename Heap<T, C>::Entry
+  Heap<T, C>::push (const T& e)
+{
+  Entry newEntry = new EntryData (*this, e, data.size ());
+  data.push_back (newEntry);
+  bubbleUp (newEntry);
+
+#if AGGRESSIVE_CHECKS
+  top ()->verifySubtree ();
+#endif
+
+  return newEntry;
+}
+
+/**
+ * Bubble an element pointed to by the entry up to the correct position.
+ * @param e Entry to bubble up.
+ * @throws std::invalid_argument if the element belongs not to this heap.
+ */
+template<typename T, typename C>
+  void
+  Heap<T, C>::bubbleUp (Entry e)
+{
+  if (&e->parent != this)
+    throw std::invalid_argument ("element belongs to a different heap");
+
+  const Entry p = e->up ();
+  if (!p)
+    return;
+
+  if (!compare (**p, **e))
+    return;
+
+  EntryData::swap (*p, *e);
+#if AGGRESSIVE_CHECKS
+  e->verifySubtree ();
+#endif
+
+  bubbleUp (e);
+}
+
+/**
+ * Bubble an element pointed to by the entry down to the correct position.
+ * @param e Entry to bubble down.
+ * @throws std::invalid_argument if the element belongs not to this heap.
+ */
+template<typename T, typename C>
+  void
+  Heap<T, C>::bubbleDown (Entry e)
+{
+  if (&e->parent != this)
+    throw std::invalid_argument ("element belongs to a different heap");
+
+  const Entry c1 = e->down (0);
+  const Entry c2 = e->down (1);
+
+  if (!c1)
+    {
+      assert (!c2);
+      return;
+    }
+
+  Entry largerC;
+  if (!c2 || compare (**c2, **c1))
+    largerC = c1;
+  else
+    largerC = c2;
+  assert (largerC);
+
+  if (!compare (**e, **largerC))
+    return;
+
+  EntryData::swap (*largerC, *e);
+  /* Here, we can't yet check the tree since it is not finished and the
+     subtrees not yet satisfy the heap condition.  */
+
+  bubbleDown (e);
+}
+
+/* ************************************************************************** */
+/* Heap entry class.  */
+
+/**
+ * Verify that the subtree belonging to this entry is in fact really
+ * a heap satisfying the invariant.  An assertion failure is thrown if not.
+ */
+template<typename T, typename C>
+  void
+  Heap<T, C>::EntryData::verifySubtree ()
+{
+  for (parentT::indexT i = 0; i <= 1; ++i)
+    {
+      const auto child = down (i);
+      if (child)
+        {
+          assert (!parent.compare (**this, **child));
+          child->verifySubtree ();
+        }
+    }
+}
+
+/**
+ * Swap two entries with each other in the vector.  This is a utility
+ * routine, which updates the vector holding the entries as well
+ * as updating the indices.  Of course, they must belong to the same
+ * parent heap.
+ * @param a First entry.
+ * @param b Second entry.
+ */
+template<typename T, typename C>
+  void
+  Heap<T, C>::EntryData::swap (EntryData& a, EntryData& b)
+{
+  assert (&a.parent == &b.parent);
+
+  const auto indexA = a.index;
+  const auto indexB = b.index;
+
+  a.parent.data[indexA] = &b;
+  a.parent.data[indexB] = &a;
+
+  a.index = indexB;
+  b.index = indexA;
+}
+
+/**
+ * Get pointer to entry of parent in the tree.
+ * @return Parent in the tree as Entry or NULL if we're on top.
+ */
+template<typename T, typename C>
+  typename Heap<T, C>::Entry
+  Heap<T, C>::EntryData::up () const
+{
+  if (index == 0)
+    return nullptr;
+
+  return parent.data[(index - 1) / 2];
+}
+
+/**
+ * Get pointer to one of the childs in the tree.
+ * @param c Which child to get (0 or 1).
+ * @return Selected child as Entry or NULL if there's no such child.
+ * @throws std::invalid_argument if c is not 0 or 1.
+ */
+template<typename T, typename C>
+  typename Heap<T, C>::Entry
+  Heap<T, C>::EntryData::down (parentT::indexT c) const
+{
+  if (c != 0 && c != 1)
+    throw std::invalid_argument ("down() only accepts 0 or 1");
+  const auto ind = 2 * index + c + 1;
+
+  if (ind >= parent.data.size ())
+    return nullptr;
+
+  return parent.data[ind];
+}
+
+/* ************************************************************************** */
+
+} // Namespace fastMarching.
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644
index 0000000..32af9f7
--- /dev/null
+++ b/src/Makefile.in
@@ -0,0 +1,63 @@
+#   GNU Octave level-set package.
+#   Copyright (C) 2013-2015  Daniel Kraft <d at domob.eu>
+#
+#   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 3 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, see <http://www.gnu.org/licenses/>.
+
+# Makefile to build the test files in C++ and the package's .oct files.
+
+CXX ?= @CXX@
+MKOCTFILE ?= mkoctfile
+
+CXXFLAGS ?= -Wall -Wextra
+FULL_CXXFLAGS = @CXXFLAGS@ $(CXXFLAGS)
+EXTRA_FLAGS = -Weffc++ -pedantic -fPIC
+
+MKOCT = CXXFLAGS="$(FULL_CXXFLAGS)" $(MKOCTFILE)
+
+HEADERS = $(wildcard:*.hpp *.tpp)
+
+ALGO_SOURCES = FastMarching.cpp
+ALGO_OBJECTS = $(ALGO_SOURCES:%.cpp=%.o)
+
+OCT_SOURCES = Utils.cpp
+OCT_OBJECTS = $(OCT_SOURCES:%.cpp=%.o)
+
+TESTS = heapsort marching
+
+PREFIX = __levelset_
+OCTS = internal_fastmarching internal_init_narrowband \
+       geomElements geomBoundary geomGamma nbFromGeom \
+       internal_mesh upwindGrad
+OCT_FILES = $(OCTS:%=$(PREFIX)%.oct)
+
+.PHONY: clean oct tests
+
+oct: $(OCT_FILES)
+
+tests: $(TESTS)
+
+clean:
+	rm -f *.o *.oct $(TESTS)
+
+$(ALGO_OBJECTS): %.o: %.cpp $(HEADERS)
+	$(CXX) $(FULL_CXXFLAGS) $(EXTRA_FLAGS) -c $< -o $@
+
+$(OCT_OBJECTS): %.o: %.cpp $(HEADERS)
+	$(MKOCT) -c $< -o $@
+
+$(OCT_FILES): $(PREFIX)%.oct: %.cpp $(HEADERS) $(ALGO_OBJECTS) $(OCT_OBJECTS)
+	$(MKOCT) $< $(ALGO_OBJECTS) $(OCT_OBJECTS) -o $@
+
+$(TESTS): %: %.cpp $(HEADERS) $(ALGO_OBJECTS)
+	$(CXX) $(FULL_CXXFLAGS) $(EXTRA_FLAGS) $< $(ALGO_OBJECTS) -o $@
diff --git a/src/Utils.cpp b/src/Utils.cpp
new file mode 100644
index 0000000..d5d4bfa
--- /dev/null
+++ b/src/Utils.cpp
@@ -0,0 +1,82 @@
+/*
+    GNU Octave level-set package.
+    Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "Utils.hpp"
+
+#include <octave/lo-ieee.h>
+
+#include <algorithm>
+#include <cstdarg>
+
+using namespace fastMarching;
+
+namespace fastMarching
+{
+
+/* Provice an Octave-based implementation of issueWarning here.  */
+void
+issueWarning (const std::string& id, const std::string& fmt, ...)
+{
+  va_list args;
+  va_start (args, fmt);
+  vwarning_with_id (id.c_str (), fmt.c_str (), args);
+  va_end (args);
+}
+
+} // namespace fastMarching
+
+/**
+ * Convert an IndexTuple to an index suitable to Octave as the
+ * used type Array<octave_idx_type>.
+ * @param ind IndexTuple representing the coordinate.
+ * @return The same index suitable for Octave.
+ */
+Array<octave_idx_type>
+getOctaveIdx (const fastMarching::IndexTuple& ind)
+{
+  const dimensionT D = ind.size ();
+
+  Array<octave_idx_type> idx(dim_vector (D, 1));
+  std::copy (ind.begin (), ind.end (), idx.fortran_vec ());
+
+  return idx;
+}
+
+/**
+ * Given two phi values with differing signs, calculate the approximate
+ * fraction along the edge connecting both where the zero lies.  This
+ * function in particular also handles infinities well.
+ * @param my Phi value at the "current point" (corresponding to fraction 0).
+ * @param other Other phi value, correspondig to fraction 1.
+ * @return Fraction of the zero level-set intersection.
+ */
+double
+getZeroFraction (double my, double other)
+{
+  const bool myInf = lo_ieee_isinf (my);
+  const bool otherInf = lo_ieee_isinf (other);
+
+  if (myInf && otherInf)
+    return 0.5;
+  if (myInf)
+    return 1;
+  if (otherInf)
+    return 0;
+
+  return std::abs (my / (my - other));
+}
diff --git a/src/Utils.hpp b/src/Utils.hpp
new file mode 100644
index 0000000..0a7be2c
--- /dev/null
+++ b/src/Utils.hpp
@@ -0,0 +1,78 @@
+/*
+    GNU Octave level-set package.
+    Copyright (C) 2014-2015  Daniel Kraft <d at domob.eu>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef UTILS_HPP
+#define UTILS_HPP
+
+/* Some basic utility routines shared between the .oct files.  */
+
+#include "FastMarching.hpp"
+
+#include <octave/oct.h>
+
+#include <cassert>
+#include <sstream>
+#include <stdexcept>
+#include <string>
+
+/**
+ * Check and possibly return dimensions of an array.  If d1 and d2 are
+ * both given as positive numbers, check that the array has the given size.
+ * If one of them is negative (-1), return the dimension along this
+ * dimension and check only the other one.  Throw if mismatch.
+ * @param var The array to check.
+ * @param d1 Row dimension.
+ * @param d2 Column dimension.
+ * @return Possibly the -1 dimension size.
+ */
+#define getDimension(var, d1, d2) \
+  getDimensionReal (var, #var, d1, d2)
+template<typename Arr>
+  unsigned getDimensionReal (const Arr& var, const std::string& name,
+                             int d1, int d2);
+
+/**
+ * Convert an IndexTuple to an index suitable to Octave as the
+ * used type Array<octave_idx_type>.
+ * @param ind IndexTuple representing the coordinate.
+ * @return The same index suitable for Octave.
+ */
+Array<octave_idx_type> getOctaveIdx (const fastMarching::IndexTuple& ind);
+
+/**
+ * Convert a generic container to an Octave ColumnVector.
+ * @param cont Generic container (e. g., std::vector<double>).
+ * @return ColumnVector with the container's content.
+ */
+template<typename C>
+  ColumnVector convertToColumnVector (const C& cont);
+
+/**
+ * Given two phi values with differing signs, calculate the approximate
+ * fraction along the edge connecting both where the zero lies.  This
+ * function in particular also handles infinities well.
+ * @param my Phi value at the "current point" (corresponding to fraction 0).
+ * @param other Other phi value, correspondig to fraction 1.
+ * @return Fraction of the zero level-set intersection.
+ */
+double getZeroFraction (double my, double other);
+
+/* Include template implementations.  */
+#include "Utils.tpp"
+
+#endif /* Header guard.  */
diff --git a/src/Utils.tpp b/src/Utils.tpp
new file mode 100644
index 0000000..713badd
--- /dev/null
+++ b/src/Utils.tpp
@@ -0,0 +1,83 @@
+/*
+    GNU Octave level-set package.
+    Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Template implementations of Utils.hpp.  */
+
+/**
+ * Check and possibly return dimensions of an array.  If d1 and d2 are
+ * both given as positive numbers, check that the array has the given size.
+ * If one of them is negative (-1), return the dimension along this
+ * dimension and check only the other one.  Throw if mismatch.
+ * @param var The array to check.
+ * @param d1 Row dimension.
+ * @param d2 Column dimension.
+ * @return Possibly the -1 dimension size.
+ */
+template<typename Arr>
+  unsigned
+  getDimensionReal (const Arr& var, const std::string& name,
+                    int d1, int d2)
+{
+  const dim_vector size = var.dims ();
+  if (size.length () > 2)
+    throw std::runtime_error ("Expected at most two dimensions for "
+                              + name + ".");
+  assert (size.length () == 2);
+
+  const int expected[2] = {d1, d2};
+  unsigned res = 0;
+  bool foundRes = false;
+  for (unsigned i = 0; i < 2; ++i)
+    if (expected[i] < 0)
+      {
+        if (foundRes)
+          throw std::runtime_error ("Two dimensions unknown for " + name + ".");
+
+        foundRes = true;
+        res = size(i);
+      }
+    else if (expected[i] != size(i))
+      {
+        std::ostringstream msg;
+        msg << "Dimension mismatch for " << name
+            << " in dimension " << (i + 1) << ": ";
+        msg << "expected " << expected[i] << ", got " << size(i);
+        throw std::runtime_error (msg.str ());
+      }
+
+  return res;
+}
+
+/**
+ * Convert a generic container to an Octave ColumnVector.
+ * @param cont Generic container (e. g., std::vector<double>).
+ * @return ColumnVector with the container's content.
+ */
+template<typename C>
+  ColumnVector
+  convertToColumnVector (const C& cont)
+{
+  ColumnVector res(cont.size ());
+
+  unsigned ind = 0;
+  for (const auto& el : cont)
+    res(ind++) = el;
+  assert (ind == cont.size ());
+  
+  return res;
+}
diff --git a/src/configure b/src/configure
new file mode 100755
index 0000000..80188d6
--- /dev/null
+++ b/src/configure
@@ -0,0 +1,3584 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.69 for Octave-Forge level-set package 0.2.0.
+#
+#
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+	expr "X$arg" : "X\\(.*\\)$as_nl";
+	arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""	$as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+  # into an infinite loop, continuously re-executing ourselves.
+  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+    _as_can_reexec=no; export _as_can_reexec;
+    # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+  fi
+  # We don't want this to propagate to other subprocesses.
+          { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+  as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+"
+  as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+  exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1"
+  if (eval "$as_required") 2>/dev/null; then :
+  as_have_required=yes
+else
+  as_have_required=no
+fi
+  if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  as_found=:
+  case $as_dir in #(
+	 /*)
+	   for as_base in sh bash ksh sh5; do
+	     # Try only shells that exist, to save several forks.
+	     as_shell=$as_dir/$as_base
+	     if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+		    { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  CONFIG_SHELL=$as_shell as_have_required=yes
+		   if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  break 2
+fi
+fi
+	   done;;
+       esac
+  as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+	      { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+  CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+      if test "x$CONFIG_SHELL" != x; then :
+  export CONFIG_SHELL
+             # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+    if test x$as_have_required = xno; then :
+  $as_echo "$0: This script requires a shell more modern than all"
+  $as_echo "$0: the shells that I found on your system."
+  if test x${ZSH_VERSION+set} = xset ; then
+    $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+    $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+  else
+    $as_echo "$0: Please tell bug-autoconf at gnu.org about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+  fi
+  exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+  as_lineno_1=$LINENO as_lineno_1a=$LINENO
+  as_lineno_2=$LINENO as_lineno_2a=$LINENO
+  eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+  test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+  # already done that, so ensure we don't try to do so again and fall
+  # in an infinite loop.  This has already happened in practice.
+  _as_can_reexec=no; export _as_can_reexec
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME='Octave-Forge level-set package'
+PACKAGE_TARNAME='octave-forge-level-set-package'
+PACKAGE_VERSION='0.2.0'
+PACKAGE_STRING='Octave-Forge level-set package 0.2.0'
+PACKAGE_BUGREPORT=''
+PACKAGE_URL=''
+
+ac_subst_vars='LTLIBOBJS
+LIBOBJS
+HAVE_CXX11
+OBJEXT
+EXEEXT
+ac_ct_CXX
+CPPFLAGS
+LDFLAGS
+CXXFLAGS
+CXX
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CXX
+CXXFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CCC'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *=)   ac_optarg= ;;
+  *)    ac_optarg=yes ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+	 ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    case $ac_envvar in #(
+      '' | [0-9]* | *[!_$as_cr_alnum]* )
+      as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+    esac
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+    *)     $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
+		datadir sysconfdir sharedstatedir localstatedir includedir \
+		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+		libdir localedir mandir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_myself" : 'X\(//\)[^/]' \| \
+	 X"$as_myself" : 'X\(//\)$' \| \
+	 X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+	cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+	pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures Octave-Forge level-set package 0.2.0 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking ...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                          [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                          [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root
+                          [DATAROOTDIR/doc/octave-forge-level-set-package]
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of Octave-Forge level-set package 0.2.0:";;
+   esac
+  cat <<\_ACEOF
+
+Some influential environment variables:
+  CXX         C++ compiler command
+  CXXFLAGS    C++ compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+              you have headers in a nonstandard directory <include dir>
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to the package provider.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for guested configure.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+Octave-Forge level-set package configure 0.2.0
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_cxx_try_compile LINENO
+# ----------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_cxx_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+	 test -z "$ac_cxx_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_cxx_try_compile
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by Octave-Forge level-set package $as_me 0.2.0, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    $as_echo "PATH: $as_dir"
+  done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+    2)
+      as_fn_append ac_configure_args1 " '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+	ac_must_keep_next=false # Got value, back to normal.
+      else
+	case $ac_arg in
+	  *=* | --config-cache | -C | -disable-* | --disable-* \
+	  | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+	  | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+	  | -with-* | --with-* | -without-* | --without-* | --x)
+	    case "$ac_configure_args0 " in
+	      "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+	    esac
+	    ;;
+	  -* ) ac_must_keep_next=true ;;
+	esac
+      fi
+      as_fn_append ac_configure_args " '$ac_arg'"
+      ;;
+    esac
+  done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+	"s/'\''/'\''\\\\'\'''\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      $as_echo "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+      echo
+      for ac_var in $ac_subst_files
+      do
+	eval ac_val=\$$ac_var
+	case $ac_val in
+	*\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+	esac
+	$as_echo "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      $as_echo "$as_me: caught signal $ac_signal"
+    $as_echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+  # We do not want a PATH search for config.site.
+  case $CONFIG_SITE in #((
+    -*)  ac_site_file1=./$CONFIG_SITE;;
+    */*) ac_site_file1=$CONFIG_SITE;;
+    *)   ac_site_file1=./$CONFIG_SITE;;
+  esac
+elif test "x$prefix" != xNONE; then
+  ac_site_file1=$prefix/share/config.site
+  ac_site_file2=$prefix/etc/config.site
+else
+  ac_site_file1=$ac_default_prefix/share/config.site
+  ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+  test "x$ac_site_file" = xNONE && continue
+  if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file" \
+      || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special files
+  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
+  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+	# differences in whitespace do not lead to failure.
+	ac_old_val_w=`echo x $ac_old_val`
+	ac_new_val_w=`echo x $ac_new_val`
+	if test "$ac_old_val_w" != "$ac_new_val_w"; then
+	  { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+	  ac_cache_corrupted=:
+	else
+	  { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+	  eval $ac_var=\$ac_old_val
+	fi
+	{ $as_echo "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
+$as_echo "$as_me:   former value:  \`$ac_old_val'" >&2;}
+	{ $as_echo "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
+$as_echo "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+if test -z "$CXX"; then
+  if test -n "$CCC"; then
+    CXX=$CCC
+  else
+    if test -n "$ac_tool_prefix"; then
+  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CXX"; then
+  ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CXX=$ac_cv_prog_CXX
+if test -n "$CXX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
+$as_echo "$CXX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CXX" && break
+  done
+fi
+if test -z "$CXX"; then
+  ac_ct_CXX=$CXX
+  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CXX+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CXX"; then
+  ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CXX="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
+if test -n "$ac_ct_CXX"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5
+$as_echo "$ac_ct_CXX" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CXX" && break
+done
+
+  if test "x$ac_ct_CXX" = x; then
+    CXX="g++"
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CXX=$ac_ct_CXX
+  fi
+fi
+
+  fi
+fi
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+         10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5
+$as_echo_n "checking whether the C++ compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+	;;
+    [ab].out )
+	# We found the default executable, but exeext='' is most
+	# certainly right.
+	break;;
+    *.* )
+	if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+	then :; else
+	   ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	fi
+	# We set ac_cv_exeext here because the later test for it is not
+	# safe: cross compilers may not add the suffix if given an `-o'
+	# argument, so we may need to know it at that point already.
+	# Even if this section looks crufty: it has the advantage of
+	# actually working.
+	break;;
+    * )
+	break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+  ac_file=''
+fi
+if test -z "$ac_file"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C++ compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5
+$as_echo_n "checking for C++ compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+	  break;;
+    * ) break;;
+  esac
+done
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+  { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+  if { ac_try='./conftest$ac_cv_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+	cross_compiling=yes
+    else
+	{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C++ compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+    fi
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5
+$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; }
+if ${ac_cv_cxx_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5
+$as_echo "$ac_cv_cxx_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GXX=yes
+else
+  GXX=
+fi
+ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_save_CXXFLAGS=$CXXFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
+$as_echo_n "checking whether $CXX accepts -g... " >&6; }
+if ${ac_cv_prog_cxx_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_cxx_werror_flag=$ac_cxx_werror_flag
+   ac_cxx_werror_flag=yes
+   ac_cv_prog_cxx_g=no
+   CXXFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_prog_cxx_g=yes
+else
+  CXXFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+
+else
+  ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+	 CXXFLAGS="-g"
+	 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_prog_cxx_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_cxx_werror_flag=$ac_save_cxx_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5
+$as_echo "$ac_cv_prog_cxx_g" >&6; }
+if test "$ac_test_CXXFLAGS" = set; then
+  CXXFLAGS=$ac_save_CXXFLAGS
+elif test $ac_cv_prog_cxx_g = yes; then
+  if test "$GXX" = yes; then
+    CXXFLAGS="-g -O2"
+  else
+    CXXFLAGS="-g"
+  fi
+else
+  if test "$GXX" = yes; then
+    CXXFLAGS="-O2"
+  else
+    CXXFLAGS=
+  fi
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+## autoconf-archive (Debian package name) must be installed
+
+    ax_cxx_compile_cxx11_required=true
+  ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+  ac_success=no
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features by default" >&5
+$as_echo_n "checking whether $CXX supports C++11 features by default... " >&6; }
+if ${ax_cv_cxx_compile_cxx11+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+  template <typename T>
+    struct check
+    {
+      static_assert(sizeof(int) <= sizeof(T), "not big enough");
+    };
+
+    struct Base {
+    virtual void f() {}
+    };
+    struct Child : public Base {
+    virtual void f() override {}
+    };
+
+    typedef check<check<bool>> right_angle_brackets;
+
+    int a;
+    decltype(a) b;
+
+    typedef check<int> check_type;
+    check_type c;
+    check_type&& cr = static_cast<check_type&&>(c);
+
+    auto d = a;
+    auto l = [](){};
+
+    // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
+    // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function because of this
+    namespace test_template_alias_sfinae {
+        struct foo {};
+
+        template<typename T>
+        using member = typename T::member_type;
+
+        template<typename T>
+        void func(...) {}
+
+        template<typename T>
+        void func(member<T>*) {}
+
+        void test();
+
+        void test() {
+            func<foo>(0);
+        }
+    }
+
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ax_cv_cxx_compile_cxx11=yes
+else
+  ax_cv_cxx_compile_cxx11=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cxx_compile_cxx11" >&5
+$as_echo "$ax_cv_cxx_compile_cxx11" >&6; }
+  if test x$ax_cv_cxx_compile_cxx11 = xyes; then
+    ac_success=yes
+  fi
+
+
+
+    if test x$ac_success = xno; then
+    for switch in -std=c++11 -std=c++0x; do
+      cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh`
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5
+$as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; }
+if eval \${$cachevar+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_CXXFLAGS="$CXXFLAGS"
+         CXXFLAGS="$CXXFLAGS $switch"
+         cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+  template <typename T>
+    struct check
+    {
+      static_assert(sizeof(int) <= sizeof(T), "not big enough");
+    };
+
+    struct Base {
+    virtual void f() {}
+    };
+    struct Child : public Base {
+    virtual void f() override {}
+    };
+
+    typedef check<check<bool>> right_angle_brackets;
+
+    int a;
+    decltype(a) b;
+
+    typedef check<int> check_type;
+    check_type c;
+    check_type&& cr = static_cast<check_type&&>(c);
+
+    auto d = a;
+    auto l = [](){};
+
+    // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
+    // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function because of this
+    namespace test_template_alias_sfinae {
+        struct foo {};
+
+        template<typename T>
+        using member = typename T::member_type;
+
+        template<typename T>
+        void func(...) {}
+
+        template<typename T>
+        void func(member<T>*) {}
+
+        void test();
+
+        void test() {
+            func<foo>(0);
+        }
+    }
+
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  eval $cachevar=yes
+else
+  eval $cachevar=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+         CXXFLAGS="$ac_save_CXXFLAGS"
+fi
+eval ac_res=\$$cachevar
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+      if eval test x\$$cachevar = xyes; then
+        CXXFLAGS="$CXXFLAGS $switch"
+        ac_success=yes
+        break
+      fi
+    done
+  fi
+  ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+  if test x$ax_cxx_compile_cxx11_required = xtrue; then
+    if test x$ac_success = xno; then
+      as_fn_error $? "*** A compiler with support for C++11 language features is required." "$LINENO" 5
+    fi
+  else
+    if test x$ac_success = xno; then
+      HAVE_CXX11=0
+      { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++11 support was found" >&5
+$as_echo "$as_me: No compiler with C++11 support was found" >&6;}
+    else
+      HAVE_CXX11=1
+
+$as_echo "#define HAVE_CXX11 1" >>confdefs.h
+
+    fi
+
+
+  fi
+
+
+ac_config_files="$ac_config_files Makefile"
+
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
+      sed -n \
+	"s/'/'\\\\''/g;
+	  s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    if test "x$cache_file" != "x/dev/null"; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+      if test ! -f "$cache_file" || test -h "$cache_file"; then
+	cat confcache >"$cache_file"
+      else
+        case $cache_file in #(
+        */* | ?:*)
+	  mv -f confcache "$cache_file"$$ &&
+	  mv -f "$cache_file"$$ "$cache_file" ;; #(
+        *)
+	  mv -f confcache "$cache_file" ;;
+	esac
+      fi
+    fi
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Transform confdefs.h into DEFS.
+# Protect against shell expansion while executing Makefile rules.
+# Protect against Makefile macro expansion.
+#
+# If the first sed substitution is executed (which looks for macros that
+# take arguments), then branch to the quote section.  Otherwise,
+# look for a macro that doesn't take arguments.
+ac_script='
+:mline
+/\\$/{
+ N
+ s,\\\n,,
+ b mline
+}
+t clear
+:clear
+s/^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 (][^	 (]*([^)]*)\)[	 ]*\(.*\)/-D\1=\2/g
+t quote
+s/^[	 ]*#[	 ]*define[	 ][	 ]*\([^	 ][^	 ]*\)[	 ]*\(.*\)/-D\1=\2/g
+t quote
+b any
+:quote
+s/[	 `~#$^&*(){}\\|;'\''"<>?]/\\&/g
+s/\[/\\&/g
+s/\]/\\&/g
+s/\$/$$/g
+H
+:any
+${
+	g
+	s/^\n//
+	s/\n/ /g
+	p
+}
+'
+DEFS=`sed -n "$ac_script" confdefs.h`
+
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+	expr "X$arg" : "X\\(.*\\)$as_nl";
+	arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""	$as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+	 X"$0" : 'X\(//\)$' \| \
+	 X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\/\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='	';;	# ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='	';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$as_dir" : 'X\(//\)[^/]' \| \
+	 X"$as_dir" : 'X\(//\)$' \| \
+	 X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by Octave-Forge level-set package $as_me 0.2.0, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration.  Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
+  -q, --quiet, --silent
+                   do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE]
+                   instantiate the configuration file FILE
+
+Configuration files:
+$config_files
+
+Report bugs to the package provider."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+Octave-Forge level-set package config.status 0.2.0
+configured by $0, generated by GNU Autoconf 2.69,
+  with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=?*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  --*=)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    $as_echo "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    $as_echo "$ac_cs_config"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    '') as_fn_error $? "missing file argument" ;;
+    esac
+    as_fn_append CONFIG_FILES " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h |  --help | --hel | -h )
+    $as_echo "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+  *) as_fn_append ac_config_targets " $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+
+  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp= ac_tmp=
+  trap 'exit_status=$?
+  : "${ac_tmp:=$tmp}"
+  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+  trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+  eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+  echo "cat >conf$$subs.awk <<_ACEOF" &&
+  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+  echo "_ACEOF"
+} >conf$$subs.sh ||
+  as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  . ./conf$$subs.sh ||
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+  if test $ac_delim_n = $ac_delim_num; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+  N
+  s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = ""
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+  || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[	 ]*VPATH[	 ]*=[	 ]*/{
+h
+s///
+s/^/:/
+s/[	 ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[	 ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[	 ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+
+eval set X "  :F $CONFIG_FILES      "
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$ac_tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+	 # (if the path is not absolute).  The absolute path cannot be DOS-style,
+	 # because $ac_f cannot contain `:'.
+	 test -f "$ac_f" ||
+	   case $ac_f in
+	   [\\/$]*) false;;
+	   *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+	   esac ||
+	   as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+      esac
+      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      as_fn_append ac_file_inputs " '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+	  $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+	`' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`$as_echo "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$ac_tmp/stdin" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+	 X"$ac_file" : 'X\(//\)[^/]' \| \
+	 X"$ac_file" : 'X\(//\)$' \| \
+	 X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)[^/].*/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\/\)$/{
+	    s//\1/
+	    q
+	  }
+	  /^X\(\/\).*/{
+	    s//\1/
+	    q
+	  }
+	  s/.*/./; q'`
+  as_dir="$ac_dir"; as_fn_mkdir_p
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+  s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+  >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[	 ]*datarootdir[	 ]*:*=/p' \
+      "$ac_tmp/out"`; test -z "$ac_out"; } &&
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&2;}
+
+  rm -f "$ac_tmp/stdin"
+  case $ac_file in
+  -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+  *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+  esac \
+  || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+
+
+
+  esac
+
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
diff --git a/src/configure.ac b/src/configure.ac
new file mode 100644
index 0000000..98b6bc2
--- /dev/null
+++ b/src/configure.ac
@@ -0,0 +1,31 @@
+#   GNU Octave level-set package.
+#   Copyright (C) 2015  Daniel Kraft <d at domob.eu>
+#
+#   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 3 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, see <http://www.gnu.org/licenses/>.
+
+# autoconf script for the level-set package.
+# This is based on configure.ac of the image package.
+
+AC_PREREQ([2.67])
+AC_INIT([Octave-Forge level-set package], [0.2.0])
+
+AC_PROG_CXX
+AC_LANG(C++)
+
+## autoconf-archive (Debian package name) must be installed
+AX_CXX_COMPILE_STDCXX_11(noext)
+
+AC_CONFIG_FILES([Makefile])
+
+AC_OUTPUT
diff --git a/src/geomBoundary.cpp b/src/geomBoundary.cpp
new file mode 100644
index 0000000..c5c970f
--- /dev/null
+++ b/src/geomBoundary.cpp
@@ -0,0 +1,376 @@
+/*
+    GNU Octave level-set package.
+    Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Calculate boundary and intersection-point related stuff of the information
+   returned by ls_find_geometry.  */
+
+#include "Utils.hpp"
+
+#include <octave/oct.h>
+#include <octave/ov-struct.h>
+#include <octave/lo-ieee.h>
+
+#include <algorithm>
+#include <cassert>
+#include <map>
+#include <stdexcept>
+#include <utility>
+
+/* ************************************************************************** */
+/* IntersectionPoint class.  */
+
+/**
+ * Encapsulate an intersection point.
+ */
+class IntersectionPoint
+{
+
+public:
+
+  /** Type used as index into map to uniquely identify the point.  */
+  typedef std::pair<unsigned, unsigned> keyT;
+
+  /**
+   * Type of this intersection point in terms of how the edge it lies on
+   * is oriented.  This is used internally for some decisions.
+   */
+  enum Type
+  {
+    WESTIN_EASTOUT,
+    EASTIN_WESTOUT,
+    SOUTHIN_NORTHOUT,
+    NORTHIN_SOUTHOUT
+  };
+
+private:
+
+  /** Index of defining inside point.  */
+  unsigned inInd;
+  /** Index of defining outside point.  */
+  unsigned outInd;
+
+  /** Type to describe a boundary element edge this lies on.  */
+  typedef std::pair<unsigned, unsigned> edgeT;
+  /** Edges this point lies on (should be two in the end).  */
+  std::vector<edgeT> edges;
+
+  /** Type of the intersection point.  */
+  Type type;
+
+  /** Edge fraction value.  */
+  double frac;
+
+  /** x coordinate wrt inner point.  */
+  double coordX;
+  /** y coordinate wrt inner point.  */
+  double coordY;
+
+  /**
+   * Store index of this intersection point in the ordering we want to
+   * use in the end.
+   */
+  int index;
+
+public:
+
+  /**
+   * Construct the intersection point, given inside- and outside point
+   * defining it as well as the other parameters.
+   * @param in Inside point index.
+   * @param out Outside point index.
+   * @param L Rows of grid.
+   * @param M Columns of grid.
+   * @param phi Level-set function.
+   * @param h Grid spacing.
+   * @param fracTol Tolerance for pushing points away from nodes.
+   */
+  IntersectionPoint (unsigned in, unsigned out, unsigned L, unsigned M,
+                     const Matrix& phi, double h, double fracTol);
+
+  // No default constructor, copying is ok.
+  IntersectionPoint () = delete;
+  IntersectionPoint (const IntersectionPoint&) = default;
+  IntersectionPoint& operator= (const IntersectionPoint&) = default;
+
+  /**
+   * Retrieve the map key for this point.
+   * @return Key uniquely identifying this point.
+   */
+  inline keyT
+  getKey () const
+  {
+    return std::make_pair (inInd, outInd);
+  }
+
+  /**
+   * Add an edge this point lies on to the list.
+   * @param el The bdryelement index.
+   * @param ed The edge number.
+   */
+  inline void
+  addEdge (unsigned el, unsigned ed)
+  {
+    assert (ed <= 3);
+    edges.push_back (edgeT (el, ed));
+  }
+
+  /**
+   * Get type of this intersection point.
+   * @return This intersection point's type.
+   */
+  inline Type
+  getType () const
+  {
+    return type;
+  }
+
+  /**
+   * Set this intersection point's index.
+   * @param ind New index.
+   */
+  inline void
+  setIndex (unsigned ind)
+  {
+    assert (index == -1);
+    index = ind;
+  }
+
+  /**
+   * Fill in the bdryel.edges row corresponding to this point.
+   * @param mat Matrix to fill into.
+   */
+  inline void
+  fillEdges (Matrix& mat) const
+  {
+    if (edges.size () != 2)
+      throw std::runtime_error ("boundary cuts through hold-all domain");
+
+    assert (index >= 0);
+    for (const auto& e : edges)
+      {
+        assert (lo_ieee_is_NA (mat(e.first, e.second)));
+        mat(e.first, e.second) = index + 1;
+      }
+  }
+
+  /**
+   * Fill in the inout matrix row.
+   * @param mat Matrix to fill in.
+   */
+  inline void
+  fillInOut (Matrix& mat) const
+  {
+    assert (index >= 0);
+    mat(index, 0) = inInd + 1;
+    mat(index, 1) = outInd + 1;
+  }
+
+  /**
+   * Fill in the frac entry.
+   * @param vec Vector to fill in.
+   */
+  inline void
+  fillFrac (ColumnVector& vec) const
+  {
+    assert (index >= 0);
+    vec(index) = frac;
+  }
+
+  /**
+   * Fill in the coord_in_orig matrix row.
+   * @param mat Matrix to fill in.
+   */
+  inline void
+  fillInCoord (Matrix& mat) const
+  {
+    assert (index >= 0);
+    mat(index, 0) = coordX;
+    mat(index, 1) = coordY;
+  }
+
+};
+
+/* Define constructor.  */
+IntersectionPoint::IntersectionPoint (unsigned in, unsigned out,
+                                      unsigned L, unsigned,
+                                      const Matrix& phi,
+                                      double h, double fracTol)
+  : inInd(in), outInd(out), edges(), index(-1)
+{
+  assert (phi(inInd) < 0.0 && phi(outInd) > 0.0);
+
+  const unsigned diff
+    = std::abs (static_cast<int> (in) - static_cast<int> (out));
+  if (diff == L)
+    {
+      if (in < out)
+        type = WESTIN_EASTOUT;
+      else
+        type = EASTIN_WESTOUT;
+    }
+  else
+    {
+      assert (diff == 1);
+      if (in < out)
+        type = NORTHIN_SOUTHOUT;
+      else
+        type = SOUTHIN_NORTHOUT;
+    }
+
+  /* Calculate intersection position on the boundary edge.  Make sure to
+     avoid a total collapse of the triangles if phi takes infinite values
+     at some points.  */
+  frac = getZeroFraction (phi(inInd), phi(outInd));
+  assert (frac >= 0.0 && frac <= 1.0);
+  if (frac < fracTol)
+    frac = fracTol;
+  else if (frac > 1.0 - fracTol)
+    frac = 1.0 - fracTol;
+
+  switch (type)
+    {
+    case WESTIN_EASTOUT:
+      coordX = frac * h;
+      coordY = 0.0;
+      break;
+    
+    case EASTIN_WESTOUT:
+      coordX = -frac * h;
+      coordY = 0.0;
+      break;
+
+    case NORTHIN_SOUTHOUT:
+      coordX = 0.0;
+      coordY = frac * h;
+      break;
+
+    case SOUTHIN_NORTHOUT:
+      coordX = 0.0;
+      coordY = -frac * h;
+      break;
+
+    default:
+      assert (false);
+    }
+}
+
+/* ************************************************************************** */
+
+/** Map of intersection points.  */
+typedef std::map<IntersectionPoint::keyT, IntersectionPoint> isptMap;
+
+/* C++ implementation.  */
+DEFUN_DLD (__levelset_geomBoundary, args, nargout,
+  "  [ISPTS, EDGES] = geomBoundary (PHI, H, FRACTOL, BDRYINDX, NODELIST)\n\n"
+  "Calculate geometrical properties of the boundary for the geometry\n"
+  "described by PHI and with grid-width H.  BDRYINDX should be a vector\n"
+  "of indices of boundary elements, as per elem.index.bdry, which must\n"
+  "already be known.  NODELIST should be elem.nodelist.\n\n"
+  "ISPTS returns the intersectpts structure of the geometry, and EDGES\n"
+  "contains bdryel.edges, which can be calculated as a by-product.\n")
+{
+  try
+    {
+      if (args.length () != 5 || nargout > 2)
+        throw std::runtime_error ("invalid argument counts");
+
+      /* Get argument and retrieve dimensions.  */
+      const Matrix phi = args(0).matrix_value ();
+      const double h = args(1).double_value ();
+      const double fracTol = args(2).double_value ();
+      const ColumnVector bdryindx = args(3).column_vector_value ();
+      const Matrix nodelist = args(4).matrix_value ();
+
+      /* Check all the dimensions.  */
+      const dim_vector dims = phi.dims ();
+      const unsigned L = dims(0);
+      const unsigned M = dims(1);
+      const unsigned nBdry = getDimension (bdryindx, -1, 1);
+      getDimension (nodelist, -1, 4);
+
+      /* Build up the list of intersection points.  */
+      isptMap ispts;
+      unsigned nextInd = 0;
+      for (unsigned i = 0; i < nBdry; ++i)
+        {
+          const unsigned idx = bdryindx(i) - 1;
+          static const unsigned EDGES[4][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}};
+          for (unsigned j = 0; j < 4; ++j)
+            {
+              unsigned pt1 = nodelist(idx, EDGES[j][0]) - 1;
+              unsigned pt2 = nodelist(idx, EDGES[j][1]) - 1;
+              if (phi(pt1) > phi(pt2))
+                std::swap (pt1, pt2);
+
+              if (phi(pt1) * phi(pt2) < 0.0)
+                {
+                  IntersectionPoint p(pt1, pt2, L, M, phi, h, fracTol);
+                  auto iter = ispts.find (p.getKey ());
+                  if (iter == ispts.end ())
+                    {
+                      p.setIndex (nextInd++);
+                      const auto pair = std::make_pair (p.getKey (), p);
+                      const auto res = ispts.insert (pair);
+                      assert (res.second);
+                      iter = res.first;
+                    }
+
+                  iter->second.addEdge (i, j);
+                }
+              else
+                assert ((phi(pt1) > 0.0 && phi(pt2) > 0.0)
+                        || (phi(pt1) < 0.0 && phi(pt2) < 0.0));
+            }
+        }
+      assert (nextInd == ispts.size ());
+
+      /* Build Octave matrices from the intersection points.  */
+      Matrix edges(nBdry, 4);
+      edges.fill (octave_NA);
+      const unsigned nIspts = ispts.size ();
+      Matrix inout(nIspts, 2);
+      ColumnVector frac(nIspts);
+      Matrix incoord(nIspts, 2);
+      for (const auto& p : ispts)
+        {
+          p.second.fillEdges (edges);
+          p.second.fillInOut (inout);
+          p.second.fillFrac (frac);
+          p.second.fillInCoord (incoord);
+        }
+
+      /* Build result structure.  */
+      octave_scalar_map st;
+      st.assign ("n", nIspts);
+      st.assign ("inout", inout);
+      st.assign ("frac", frac);
+      st.assign ("incoord", incoord);
+
+      /* Return.  */
+      octave_value_list res;
+      res(0) = st;
+      res(1) = edges;
+
+      return res;
+    }
+  catch (const std::runtime_error& exc)
+    {
+      error (exc.what ());
+      return octave_value_list ();
+    }
+}
diff --git a/src/geomElements.cpp b/src/geomElements.cpp
new file mode 100644
index 0000000..7c8a1f4
--- /dev/null
+++ b/src/geomElements.cpp
@@ -0,0 +1,130 @@
+/*
+    GNU Octave level-set package.
+    Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Calculate the elements-related things of the information returned
+   by ls_find_geometry.  */
+
+#include "Utils.hpp"
+
+#include <octave/oct.h>
+#include <octave/ov-struct.h>
+
+#include <cassert>
+#include <vector>
+#include <stdexcept>
+
+/** Vector of indices used for the *indx fields.  */
+typedef std::vector<unsigned> indexArr;
+
+/* C++ implementation.  */
+DEFUN_DLD (__levelset_geomElements, args, nargout,
+  "  ELEMENTS = geomElements (PHI)\n\n"
+  "Construct ELEM structure as per ls_find_geometry for the given level-set\n"
+  "function PHI.  PHI must not contain values which are exactly zero.\n")
+{
+  try
+    {
+      if (args.length () != 1 || nargout > 1)
+        throw std::runtime_error ("invalid argument counts");
+
+      /* Get argument.  */
+      const Matrix phi = args(0).matrix_value ();
+
+      /* Extract the dimensions.  */
+      const dim_vector dims = phi.dims ();
+      const unsigned L = dims(0);
+      const unsigned M = dims(1);
+
+      /* Go over all elements to build the nodelist.  */
+      const unsigned nElem = (L - 1) * (M - 1);
+      Matrix nodelist(nElem, 4);
+      for (unsigned row = 0; row < L - 1; ++row)
+        for (unsigned col = 0; col < M - 1; ++col)
+          {
+            const unsigned elInd = col * (L - 1) + row;
+
+            /* The element nodes are stored in order SE, SW, NW, NE.  */
+#define STORE_NODELIST(num, dr, dc) \
+  nodelist(elInd, num) = (col + (dc)) * L + (row + (dr)) + 1
+            STORE_NODELIST(0, 1, 1);
+            STORE_NODELIST(1, 1, 0);
+            STORE_NODELIST(2, 0, 0);
+            STORE_NODELIST(3, 0, 1);
+#undef STORE_NODELIST
+          }
+
+      /* Find outer/inner/bdry elements.  */
+      indexArr outindx, inindx, bdryindx;
+      ColumnVector type(nElem);
+      for (unsigned i = 0; i < (L - 1) * (M - 1); ++i)
+        {
+          bool hasOut = false;
+          bool hasIn = false;
+          for (unsigned j = 0; j < 4; ++j)
+            {
+              const unsigned nodeInd = nodelist(i, j) - 1;
+              if (phi(nodeInd) > 0.0)
+                hasOut = true;
+              else
+                {
+                  assert (phi(nodeInd) < 0.0);
+                  hasIn = true;
+                }
+            }
+
+          if (hasOut && hasIn)
+            {
+              type(i) = 0.0;
+              bdryindx.push_back (i + 1);
+            }
+          else if (hasOut)
+            {
+              type(i) = 1.0;
+              outindx.push_back (i + 1);
+            }
+          else
+            {
+              assert (hasIn);
+              type(i) = -1.0;
+              inindx.push_back (i + 1);
+            }
+        }
+
+      /* Build result structure.  */
+      octave_scalar_map indx;
+      indx.assign ("inner", convertToColumnVector (inindx));
+      indx.assign ("outer", convertToColumnVector (outindx));
+      indx.assign ("bdry", convertToColumnVector (bdryindx));
+      octave_scalar_map st;
+      st.assign ("n", nElem);
+      st.assign ("nodelist", nodelist);
+      st.assign ("type", type);
+      st.assign ("index", indx);
+
+      /* Return.  */
+      octave_value_list res;
+      res(0) = st;
+
+      return res;
+    }
+  catch (const std::runtime_error& exc)
+    {
+      error (exc.what ());
+      return octave_value_list ();
+    }
+}
diff --git a/src/geomGamma.cpp b/src/geomGamma.cpp
new file mode 100644
index 0000000..032f717
--- /dev/null
+++ b/src/geomGamma.cpp
@@ -0,0 +1,378 @@
+/*
+    GNU Octave level-set package.
+    Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Calculate the gamma chaining information returned by ls_find_geometry.  */
+
+#include "Utils.hpp"
+
+#include <octave/oct.h>
+#include <octave/Cell.h>
+#include <octave/lo-ieee.h>
+#include <octave/ov-struct.h>
+
+#include <cassert>
+#include <stdexcept>
+#include <utility>
+#include <vector>
+
+/* Type used for building up intersection-point chains.  */
+typedef std::vector<unsigned> IndexChain;
+/* Type for indexing a (boundary element, edge) value.  */
+typedef std::pair<unsigned, unsigned> EdgeIndex;
+
+/* ************************************************************************** */
+
+/**
+ * Simple utility class for the information collected with the boundary
+ * element segments.  This class contains all data relevant for one segment,
+ * and can convert it to the Octave struct in the end.
+ */
+class BdryelSegmentEntry
+{
+
+private:
+
+  /* Start point and edge.  Already 1-based.  */
+  unsigned startPt, startEdge;
+  /* End point and edge.  Already 1-based.  */
+  unsigned endPt, endEdge;
+
+  /** The enclosed inner points.  Already 1-based indices.  */
+  IndexChain inners;
+
+public:
+
+  /**
+   * Construct it with set "empty" values.  This is necessary because
+   * we want to use this class later in a vector for collecting the
+   * different segments of a single boundary element.
+   */
+  inline BdryelSegmentEntry ()
+    : startPt(0), startEdge(0), endPt(0), endEdge(0), inners()
+  {}
+
+  // Copying and assignment per default.
+  BdryelSegmentEntry (const BdryelSegmentEntry&) = default;
+  BdryelSegmentEntry& operator= (const BdryelSegmentEntry&) = default;
+
+  // Initialise start and end points (p and e are 0-based).
+
+  inline void
+  setStart (unsigned p, unsigned e)
+  {
+    assert (startPt == 0 && startEdge == 0);
+    assert (e < 4);
+    startPt = p + 1;
+    startEdge = e + 1;
+  }
+
+  inline void
+  setEnd (unsigned p, unsigned e)
+  {
+    assert (endPt == 0 && endEdge == 0);
+    assert (e < 4);
+    endPt = p + 1;
+
+    /* Note that the edge stored for a point is always the one in the
+       boundary element in which the point *starts* an edge.  Thus for
+       the end point, we have to take the opposite edge, since we are
+       interested in the edge relative to the *other* boundary element
+       (where p is the end point).  */
+    endEdge = (e + 2) % 4 + 1;
+  }
+
+  /**
+   * Add a new inner point.
+   */
+  inline void
+  addInner (unsigned p)
+  {
+    inners.push_back (p + 1);
+  }
+
+  /**
+   * Fill in the ispts.onedge structure entries represented by this segment.
+   * @param onedge The onedge array to fill in.
+   * @param bdryel The boundary element index this segment is part of.
+   */
+  void fillOnEdge (NDArray& onedge, unsigned bdryel) const;
+
+  /**
+   * Finalise and convert to Octave structure.
+   * @return Resulting Octave structure value.
+   */
+  octave_scalar_map toStruct () const;
+
+};
+
+/**
+ * Fill in the ispts.onedge structure entries represented by this segment.
+ * @param onedge The onedge array to fill in.
+ * @param bdryel The boundary element index this segment is part of.
+ */
+void
+BdryelSegmentEntry::fillOnEdge (NDArray& onedge, unsigned bdryel) const
+{
+  assert (startPt > 0 && endPt > 0);
+  assert (startEdge >= 1 && startEdge <= 4);
+  assert (endEdge >= 1 && endEdge <= 4);
+  
+#define FILL_CHECK_NA(lhs, rhs) \
+  if (!lo_ieee_is_NA (lhs)) \
+    throw std::runtime_error ("onedge already filled in"); \
+  lhs = (rhs);
+
+  FILL_CHECK_NA (onedge(startPt - 1, 0, 0), bdryel + 1)
+  FILL_CHECK_NA (onedge(startPt - 1, 0, 1), startEdge)
+  FILL_CHECK_NA (onedge(endPt - 1, 1, 0), bdryel + 1)
+  FILL_CHECK_NA (onedge(endPt - 1, 1, 1), endEdge)
+
+#undef FILL_CHECK_NA
+}
+
+/**
+ * Finalise and convert to Octave structure.
+ * @return Resulting Octave structure value.
+ */
+octave_scalar_map
+BdryelSegmentEntry::toStruct () const
+{
+  assert (startPt > 0 && endPt > 0);
+  assert (startEdge >= 1 && startEdge <= 4);
+  assert (endEdge >= 1 && endEdge <= 4);
+  assert (inners.size () >= 1 && inners.size () <= 3);
+
+  octave_scalar_map res;
+  res.assign ("startPt", startPt);
+  res.assign ("startEdge", startEdge);
+  res.assign ("endPt", endPt);
+  res.assign ("endEdge", endEdge);
+  res.assign ("inners", convertToColumnVector (inners));
+
+  return res;
+}
+
+/* ************************************************************************** */
+
+/* C++ implementation.  */
+DEFUN_DLD (__levelset_geomGamma, args, nargout,
+  "  [COMPS, ISPTONEDGE, BDRLELSG]\n"
+  "    = geomGamma (PHI, NODELIST, BDRYIND, EDGES, INOUT)\n\n"
+  "Internal routine to construct the gamma component chains for the\n"
+  "given information.  The arguments should already be calculated by\n"
+  "the other geometry routines.\n\n"
+  "Returned are the components cell array, the intersection points' onedge\n"
+  "array as well as the structure to be kept internally for mesh-building.\n")
+{
+  try
+    {
+      if (args.length () != 5 || nargout > 3)
+        throw std::runtime_error ("invalid argument counts");
+
+      /* Get arguments.  */
+      const Matrix phi = args(0).matrix_value ();
+      const Matrix nodelist = args(1).matrix_value ();
+      const ColumnVector bdryInd = args(2).column_vector_value ();
+      const Matrix edges = args(3).matrix_value ();
+      const Matrix inout = args(4).matrix_value ();
+
+      /* Extract and check the dimensions.  */
+      const unsigned nNodes = phi.nelem ();
+      const unsigned nElem = getDimension (nodelist, -1, 4);
+      const unsigned nBdryEl = getDimension (bdryInd, -1, 1);
+      getDimension (edges, nBdryEl, 4);
+      const unsigned nIspts = getDimension (inout, -1, 2);
+
+      /* Each intersection point should occur in the "edges" array of exactly
+         two boundary elements.  One of these can be used to "continue"
+         following the chain, in order to satisfy our ordering requirement
+         (interior of the domain to the left in coordinate interpretation).
+         
+         For each intersection point, find the corresponding boundary element
+         and edge index for which this is the case and store this information
+         in a map.  It is a map and not a vector, since we will be removing
+         intersection points as we add them to chains.  */
+
+      std::map<unsigned, EdgeIndex> isptStartElem;
+      for (unsigned i = 0; i < nBdryEl; ++i)
+        for (unsigned j = 0; j < 4; ++j)
+          if (!lo_ieee_is_NA (edges(i, j)))
+            {
+              if (edges(i, j) < 1 || edges(i, j) > nIspts)
+                throw std::out_of_range ("edges entry out of bounds");
+              const unsigned ispt = edges(i, j) - 1;
+              assert (ispt < nIspts);
+
+              if (bdryInd(i) < 1 || bdryInd(i) > nElem)
+                throw std::out_of_range ("element index out of bounds");
+              const unsigned elem = bdryInd(i) - 1;
+              assert (elem < nElem);
+
+              /* See if the intersection point's inner node matches the
+                 element's nodelist entry at the correct side of the edge.
+                 For a N edge intersected, we have a valid "starting point"
+                 if the inner node is at NE (in coordinate grid interpretation).
+                 Given the orderings of nodelist and edges, this means
+                 that the "correct" point is at the same index in nodelist
+                 as the edge is in edges.  */
+              const unsigned inpt = inout(ispt, 0);
+              if (inpt == nodelist(elem, j))
+                {
+                  if (isptStartElem.find (ispt) != isptStartElem.end ())
+                    throw std::runtime_error ("multiple starts found for ispt");
+
+                  const EdgeIndex entry(i, j);
+                  isptStartElem.insert (std::make_pair (ispt, entry));
+                }
+            }
+      if (isptStartElem.size () != nIspts)
+        throw std::runtime_error ("wrong number of starting points found");
+
+      /* Now, build up the result.  We start at an arbitrary unused
+         intersection point, and follow the chain until we're back at
+         it.  This is done until no more intersection points are available.  */
+
+      std::vector<IndexChain> chains;
+      std::vector<std::vector<BdryelSegmentEntry>> bdryelSegs(nBdryEl);
+
+      while (!isptStartElem.empty ())
+        {
+          const auto startIter = isptStartElem.begin ();
+          const auto startEntry = *startIter;
+          isptStartElem.erase (startIter);
+
+          IndexChain curChain;
+          std::pair<unsigned, EdgeIndex> curEntry = startEntry;
+          while (true)
+            {
+              /* Add to chain.  Take care of 1-based indices for Octave.  */
+              curChain.push_back (curEntry.first + 1);
+
+              /* Find the next edge with an intersection point.  We get the
+                 correct handling of narrow pairs by trying to make the
+                 enclosed area "as small as possible".  I. e., we turn as much
+                 "left" as possible, until we find an intersection point.  This
+                 means that we have to traverse the edges in *reverse*
+                 direction (since increasing indices are counter-clockwise
+                 in the coordinate grid interpretation).
+
+                 With this choice of ordering, it should also always
+                 be the case that points over which we step are inner
+                 points only.  */
+
+              const unsigned bdryEl = curEntry.second.first;
+              const unsigned fullEl = bdryInd(bdryEl) - 1;
+              const unsigned edge = curEntry.second.second;
+              assert (bdryEl < nBdryEl && edge < 4);
+              assert (edges(bdryEl, edge) == curEntry.first + 1);
+
+              BdryelSegmentEntry seg;
+              seg.setStart (curEntry.first, edge);
+
+              unsigned nextPt = nIspts;
+              for (unsigned i = edge + 3; i != edge; --i)
+                {
+                  const unsigned innerPtInd = (i + 1) % 4;
+                  const unsigned innerPt = nodelist(fullEl, innerPtInd) - 1;
+                  if (innerPt >= nNodes)
+                    throw std::runtime_error ("nodelist entry out-of-bounds");
+                  if (phi(innerPt) >= 0.0)
+                    throw std::runtime_error ("innerPt is not actually inside");
+                  assert (i != edge + 3
+                          || innerPt == inout(curEntry.first) - 1);
+                  seg.addInner (innerPt);
+
+                  if (!lo_ieee_is_NA (edges(bdryEl, i % 4)))
+                    {
+                      nextPt = edges(bdryEl, i % 4) - 1;
+                      break;
+                    }
+                }
+              assert (nextPt <= nIspts);
+              if (nextPt == nIspts)
+                throw std::runtime_error ("no next ispt found");
+
+              /* If we have a closed loop, finish it.  We have to finish
+                 the boundary element segment, too.  */
+              if (nextPt == startEntry.first)
+                {
+                  seg.setEnd (nextPt, startEntry.second.second);
+                  bdryelSegs[bdryEl].push_back (seg);
+                  break;
+                }
+
+              /* Do some book keeping before adding it to the chain at
+                 the beginning of the next iteration.  */
+              const auto nextIter = isptStartElem.find (nextPt);
+              if (nextIter == isptStartElem.end ())
+                throw std::runtime_error ("next point is not available");
+              curEntry = *nextIter;
+              isptStartElem.erase (nextIter);
+
+              /* Finish the boundary element segment.  */
+              seg.setEnd (nextPt, curEntry.second.second);
+              bdryelSegs[bdryEl].push_back (seg);
+            }
+
+          if (curChain.size () < 4)
+            throw std::runtime_error ("too short gamma chain found");
+          chains.push_back (curChain);
+        }
+
+      /* Build up the return value as a cell-array.  */
+      Cell c(chains.size (), 1);
+      for (unsigned i = 0; i < chains.size (); ++i)
+        c(i) = convertToColumnVector (chains[i]);
+
+      /* Build the intersection points' "onedge" array.  */
+      dim_vector dims{static_cast<octave_idx_type> (nIspts), 2, 3};
+      NDArray onedge(dims);
+      onedge.fill (octave_NA);
+      assert (bdryelSegs.size () == nBdryEl);
+      for (unsigned i = 0; i < nBdryEl; ++i)
+        for (const auto& s : bdryelSegs[i])
+          s.fillOnEdge (onedge, i);
+
+      /* Build boundary element segments as cell-array.  */
+      Cell bs(nBdryEl, 1);
+      for (unsigned i = 0; i < nBdryEl; ++i)
+        {
+          const unsigned nSeg = bdryelSegs[i].size ();
+          assert (nSeg == 1 || nSeg == 2);
+
+          Cell e(nSeg, 1);
+          for (unsigned j = 0; j < nSeg; ++j)
+            e(j) = bdryelSegs[i][j].toStruct ();
+
+          bs(i) = e;
+        }
+
+      /* Return.  */
+      octave_value_list res;
+      res(0) = c;
+      res(1) = onedge;
+      res(2) = bs;
+
+      return res;
+    }
+  catch (const std::runtime_error& exc)
+    {
+      error (exc.what ());
+      return octave_value_list ();
+    }
+}
diff --git a/src/heapsort.cpp b/src/heapsort.cpp
new file mode 100644
index 0000000..ebebf19
--- /dev/null
+++ b/src/heapsort.cpp
@@ -0,0 +1,87 @@
+/*
+    GNU Octave level-set package.
+    Copyright (C) 2013-2015  Daniel Kraft <d at domob.eu>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Test program for heap, doing a simple heap sort of random numbers.  */
+
+#include "Heap.hpp"
+
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+
+using namespace fastMarching;
+
+namespace fastMarching
+{
+
+/* Provide printf-based implementation of issueWarning.  */
+void
+issueWarning (const std::string&, const std::string& fmt, ...)
+{
+  va_list args;
+  va_start (args, fmt);
+  vprintf (fmt.c_str (), args);
+  va_end (args);
+}
+
+} // namespace fastMarching
+
+/**
+ * Comparison routine used.  Simply compare doubles.
+ * @param a First value.
+ * @param b Second value.
+ */
+inline bool
+compare (double a, double b)
+{
+  return a < b;
+}
+
+/** Type of heap.  */
+typedef Heap<double, decltype (&compare)> heapT;
+
+/**
+ * Main routine.
+ * @return Exit code 0.
+ */
+int
+main ()
+{
+  heapT heap(&compare);
+
+  for (int i = 0; i < 40000; ++i)
+    {
+      const double cur = static_cast<double> (std::rand ());
+      heap.push (cur / RAND_MAX);
+    }
+
+  double last = **heap.top ();
+  heap.pop ();
+
+  while (!heap.empty ())
+    {
+      const double cur = **heap.top ();
+      heap.pop ();
+
+      if (cur > last)
+        printf ("%f vs %f!\n", cur, last);
+      last = cur;
+    }
+
+  return EXIT_SUCCESS;
+}
diff --git a/src/internal_fastmarching.cpp b/src/internal_fastmarching.cpp
new file mode 100644
index 0000000..543e6dd
--- /dev/null
+++ b/src/internal_fastmarching.cpp
@@ -0,0 +1,131 @@
+/*
+    GNU Octave level-set package.
+    Copyright (C) 2013-2014  Daniel Kraft <d at domob.eu>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Interfacing the C++ fast marching code to Octave.  This is not the actual
+   Octave interface, but the routine performing the work.  The real Octave
+   function is written as .m file and performs some more preparation work
+   on the arguments before calling in here.  */
+
+#include "FastMarching.hpp"
+#include "Utils.hpp"
+
+#include <octave/oct.h>
+
+#include <cassert>
+
+using namespace fastMarching;
+
+/* Octave routine interfacing to fast marching code.  */
+DEFUN_DLD (__levelset_internal_fastmarching, args, nargout,
+  "[U, G, REACHED] = internal_fastmarching (DOMAIN, U0, G0, ALIVE, F)\n\n"
+  "Internal routine for fastmarching.  DOMAIN, U0, ALIVE and F must all be\n"
+  "arrays of the same dimension and size, specifying the grid.  The solution\n"
+  "is returned in the array U, giving the final distance at each grid point.\n"
+  "REACHED is boolean array storing for each point whether it is actually\n"
+  "reached by the method in finite distance from some point that is\n"
+  "initially alive.\n\n"
+  "DOMAIN and ALIVE are boolean arrays defining whether some point belongs\n"
+  "to the domain (holes in the domain are allowed and treated as absolutely\n"
+  "unreachable and untraversable regions), and whether the point belongs to\n"
+  "the initially alive set.  For those points, U0 is assumed to contain\n"
+  "the initialisation distance.\n")
+{
+  try
+    {
+      if (args.length () != 5 || nargout != 3)
+        throw std::invalid_argument ("invalid argument counts");
+
+      const boolNDArray domain = args(0).bool_array_value ();
+      const NDArray u0 = args(1).array_value ();
+      const NDArray g0 = args(2).array_value ();
+      const boolNDArray alive = args(3).bool_array_value ();
+      const NDArray f = args(4).array_value ();
+
+      const dim_vector size = domain.dims ();
+      const dimensionT D = size.length ();
+
+      if (size != u0.dims () || size != g0.dims ()
+          || size != alive.dims () || size != f.dims ())
+        throw std::invalid_argument ("argument dimensions mismatch");
+
+      IndexTuple gridSize(D);
+      for (dimensionT i = 0; i < D; ++i)
+        gridSize[i] = size(i);
+
+      Grid grid(gridSize);
+
+      const auto fillIn
+        = [D, &domain, &u0, &g0, &alive, &f, &grid] (const IndexTuple& c)
+        {
+          const Array<octave_idx_type> idx = getOctaveIdx (c);
+          assert (c.size () == D
+                  && static_cast<dimensionT> (idx.length ()) == D);
+
+          if (domain(idx))
+            {
+              Entry* e;
+              if (alive(idx))
+                e = Entry::newAlive (c, u0(idx), g0(idx));
+              else
+                e = Entry::newFarAway (c, f(idx));
+
+              grid.setInitial (e);
+            }
+        };
+      grid.iterate (fillIn);
+
+      grid.perform ();
+
+      NDArray u(size);
+      NDArray g(size);
+      boolNDArray reached(size);
+      const auto getOut
+        = [D, &grid, &u, &g, &reached] (const IndexTuple& c)
+        {
+          const Array<octave_idx_type> idx = getOctaveIdx (c);
+          assert (c.size () == D
+                  && static_cast<dimensionT> (idx.length ()) == D);
+
+          const Grid& constGrid(grid);
+          const Entry* e = constGrid.get (c);
+
+          if (e)
+            {
+              const realT dist = e->getDistance ();
+              reached(idx) = (dist != LARGE_DIST);
+              u(idx) = dist;
+              g(idx) = e->getFunction ();
+            }
+          else
+            reached(idx) = false;
+        };
+      grid.iterate (getOut);
+
+      octave_value_list res;
+      res(0) = u;
+      res(1) = g;
+      res(2) = reached;
+
+      return res;
+    }
+  catch (const std::exception& exc)
+    {
+      error (exc.what ());
+      return octave_value_list ();
+    }
+}
diff --git a/src/internal_init_narrowband.cpp b/src/internal_init_narrowband.cpp
new file mode 100644
index 0000000..e103e47
--- /dev/null
+++ b/src/internal_init_narrowband.cpp
@@ -0,0 +1,145 @@
+/*
+    GNU Octave level-set package.
+    Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Given a level-set function, initialise the narrow-band distances.  The
+   code here does the internal C++ implementation for better performance,
+   and is used from the "public" .m file including some more error and
+   argument checks as well as test and demo routines.  */
+
+#include "FastMarching.hpp"
+#include "Utils.hpp"
+
+#include <octave/oct.h>
+#include <octave/lo-ieee.h>
+
+#include <cmath>
+
+using namespace fastMarching;
+
+/* Fraction below which we consider a point exactly on the boundary.  This
+   is necessary to prevent underflows from happening for the h's passed
+   to the update equation solver.
+
+   Should this be configurable?  But note that this value is just necessary
+   to prevent underflows.  The fracTol in the geometry routine, on the other
+   hand, influences the resulting geometry and further constructed triangle
+   meshes.  (In particular, how distorted triangles may get.)  Thus it makes
+   sense to allow configuration there.  */
+static const double MIN_FRAC = 1e-16;
+
+/* Octave routine interfacing to .m file code.  */
+DEFUN_DLD (__levelset_internal_init_narrowband, args, nargout,
+  "  D = internal_init_narrowband (PHI, H)\n\n"
+  "Internal routine for ls_init_narrowband.  Given the level-set function\n"
+  "PHI on some array with grid-spacing H, calculate the distances from the\n"
+  "zero level-set of all grid-points next to it ('narrow band') and return\n"
+  "them in D.  All distances are positive, so the sign of PHI is not\n"
+  "considered to differentiate between inside and outside.  Entries not\n"
+  "in the narrow band will be set to NA.\n")
+{
+  try
+    {
+      if (args.length () != 2 || nargout != 1)
+        throw std::invalid_argument ("invalid argument counts");
+
+      const NDArray phi = args(0).array_value ();
+      const double h = args(1).double_value ();
+
+      const dim_vector size = phi.dims ();
+      const dimensionT D = size.length ();
+
+      IndexTuple gridSize(D);
+      for (dimensionT i = 0; i < D; ++i)
+        gridSize[i] = size(i);
+
+      NDArray dists(size);
+      dists.fill (octave_NA);
+
+      /* Now iterate over the grid.  For each cell, check all links to
+         neighbours that are still on the grid.  If phi changes sign
+         along one of those links, we are in the narrow band.  In this
+         case, build up a fast marching update equation from all of those
+         links, and solve it to get our distance.  We assume a linear
+         model for phi in order to find the "exact" point on the
+         boundary along the intersected edge.  */
+
+      Grid grid(gridSize);
+      const auto doIt
+        = [h, &phi, &dists, &grid] (const IndexTuple& coord)
+        {
+          const Array<octave_idx_type> idx = getOctaveIdx (coord);
+          const double myPhi = phi(idx);
+
+          /* Handle the special case of phi exactly zero here.  */
+          if (myPhi == 0.0)
+            {
+              dists(idx) = 0.0;
+              return;
+            }
+
+          UpdateEquation eqn;
+          bool isNarrowBand = false;
+
+          const auto checkLink
+            = [h, myPhi, &eqn, &isNarrowBand, &phi, &dists, &idx]
+              (const IndexTuple& neighbour, dimensionT d, indexT)
+            {
+              const double nPhi = phi(getOctaveIdx (neighbour));
+
+              /* The special case of phi exactly zero at the neighbour
+                 is handled as if there were no intersection at all.  */
+              if (nPhi == 0.0)
+                return;
+
+              if (nPhi * myPhi < 0.0)
+                {
+                  const double frac = getZeroFraction (myPhi, nPhi);
+                  assert (lo_ieee_finite (frac));
+
+                  /* If the neighbour value is infinite leading to a frac
+                     of zero, this point is exactly on the boundary.  Thus
+                     set distance to zero and we're done.  */
+                  if (std::abs (frac) < MIN_FRAC)
+                    {
+                      isNarrowBand = false;
+                      dists(idx) = 0.0;
+                      return;
+                    }
+
+                  isNarrowBand = true;
+                  eqn.add (d, frac * h, 0.0);
+                }
+            };
+          grid.iterateNeighbours (coord, checkLink);
+
+          if (isNarrowBand)
+            dists(idx) = eqn.solve (1.0);
+        };
+      grid.iterate (doIt);
+
+      octave_value_list res;
+      res(0) = dists;
+
+      return res;
+    }
+  catch (const std::exception& exc)
+    {
+      error (exc.what ());
+      return octave_value_list ();
+    }
+}
diff --git a/src/internal_mesh.cpp b/src/internal_mesh.cpp
new file mode 100644
index 0000000..b772615
--- /dev/null
+++ b/src/internal_mesh.cpp
@@ -0,0 +1,525 @@
+/*
+    GNU Octave level-set package.
+    Copyright (C) 2013-2014  Daniel Kraft <d at domob.eu>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Internal C++ implementation for mesh building.  Inside and outside elements
+   are simply split in two triangles, while boundary elements are split
+   depending on how they are intersected by the boundary.  For them, the
+   geom.internal data about "boundary segments" is used.  */
+
+#include <octave/oct.h>
+#include <octave/Cell.h>
+#include <octave/ov-struct.h>
+
+#include <stdexcept>
+#include <vector>
+
+#include "Utils.hpp"
+
+/**
+ * Type used to hold maps of indices.  It needs to allow negative values, too,
+ * since they are used as "indices" of intersection points in the geometry.
+ * Furthermore, -1 is used to denote not-yet-set indices (since NA is not
+ * available as an integer value).
+ */
+typedef std::vector<int> indexArr;
+
+/** Type used for coordinates in space.  */
+typedef double realT;
+
+/* ************************************************************************** */
+/* Containers for the datas of the mesh.  */
+
+/**
+ * Represent a vertex in the list of vertices of a mesh (the mesh.p result).
+ */
+class Vertex
+{
+
+private:
+
+  /** This vertex' x coordinate in space.  */
+  realT x;
+  /** This vertex' y coordinate in space.  */
+  realT y;
+
+public:
+
+  /**
+   * Construct with given coordinates.
+   * @param mx x coordinate.
+   * @param my y coordinate.
+   */
+  inline Vertex (const realT mx, const realT my)
+    : x(mx), y(my)
+  {
+    // Nothing else to do.
+  }
+
+  /* Default copying.  */
+  Vertex () = default;
+  Vertex (const Vertex& o) = default;
+  Vertex& operator= (const Vertex& o) = default;
+
+  /**
+   * Convert to row in the vertex matrix to be returned to Octave.
+   * @param i Index for the row to write in.
+   * @param m Matrix to write to.
+   */
+  inline void
+  toMatrix (const unsigned i, Matrix& m) const
+  {
+    m(0, i) = x;
+    m(1, i) = y;
+  }
+
+};
+
+/**
+ * Represent a triangle in the list of triangles of a mesh (mesh.t field).
+ */
+class Triangle
+{
+
+private:
+
+  /** Indices of the vertices.  */
+  unsigned vertices[3];
+
+public:
+
+  /**
+   * Construct with given vertices.
+   * @param a First vertex.
+   * @param b Second vertex.
+   * @param c Third vertex.
+   */
+  inline Triangle (const unsigned a, const unsigned b, const unsigned c)
+    : vertices{a, b, c}
+  {
+    // Nothing else to do.
+  }
+
+  /* Default copying.  */
+  Triangle () = default;
+  Triangle (const Triangle& o) = default;
+  Triangle& operator= (const Triangle& o) = default;
+
+  /**
+   * Convert to row in the triangle matrix to be returned to Octave.
+   * @param i Index for the row to write in.
+   * @param m Matrix to write to.
+   */
+  inline void
+  toMatrix (const unsigned i, Matrix& m) const
+  {
+    for (unsigned j = 0; j < 3; ++j)
+      m(j, i) = vertices[j];
+  }
+
+};
+
+/* ************************************************************************** */
+/* Mesh class itself.  */
+
+/**
+ * This class represents the mesh that is being built up.  It keeps track of
+ * the vertices and triangles as they are added.  In addition,
+ * in order to do that, it also keeps a map between original vertex indices
+ * and indices into the already set-up list of vertices.
+ */
+class Mesh
+{
+
+private:
+
+  /** The list of vertices.  */
+  std::vector<Vertex> vertices;
+  /** The list of triangles.  */
+  std::vector<Triangle> triangles;
+  
+  /** Map ordinary grid point index to that in list of vertices.  */
+  indexArr innerVertexInd;
+  /** Map intersection point index to that in list of vertices.  */
+  indexArr bdryVertexInd;
+  /**
+   * Map index of vertices on the mesh to the vertex code on the geometry.
+   * I. e., may be positive or negative depending on grid point or ispt.
+   */
+  indexArr meshVertexInd;
+
+public:
+
+  /**
+   * Construct the yet empty mesh.
+   * @param n Number of inner grid points.
+   * @param nBdry Number of intersection points.
+   */
+  Mesh (const unsigned n, const unsigned nBdry);
+
+  /* No copying.  */
+  Mesh () = delete;
+  Mesh (const Mesh& o) = delete;
+  Mesh& operator= (const Mesh& o) = delete;
+
+  /**
+   * Get internal vertex index for a given grid point.  Insert it into
+   * the list of vertices if necessary.
+   * @param ind Grid point index.
+   * @param coord Full coordinate array, so we know the coordinates.
+   * @return Index into internal vertex list.
+   */
+  unsigned getInnerVertex (const unsigned ind, const Matrix& coord);
+
+  /**
+   * Get internal vertex index for a given intersection point.  Insert it into
+   * the list of vertices if necessary.
+   * @param ind Grid point index.
+   * @param coord Array of coordinates of boundary points.
+   * @return Index into internal vertex list.
+   */
+  unsigned getBoundaryVertex (const unsigned ind, const Matrix& coord);
+
+  /**
+   * Add a triangle with the given vertices.
+   * @param a First vertex.
+   * @param b Second vertex.
+   * @param c Third vertex.
+   */
+  inline void
+  addTriangle (const unsigned a, const unsigned b, const unsigned c)
+  {
+    triangles.push_back (Triangle(a, b, c));
+  }
+
+  /**
+   * Wrap the mesh's content up into the struct expected by Octave.
+   * @param res Store the msh structure here.
+   * @param vertexMap Store the vertex map here.
+   */
+  void toStruct (octave_scalar_map& res, octave_scalar_map& vertexMap) const;
+
+};
+
+Mesh::Mesh (const unsigned n, const unsigned nBdry)
+  : vertices(), triangles(),
+    innerVertexInd(n, -1), bdryVertexInd(nBdry, -1), meshVertexInd()
+{
+  // Nothing else to do.
+}
+
+unsigned
+Mesh::getInnerVertex (const unsigned ind, const Matrix& coord)
+{
+  if (innerVertexInd[ind] < 0)
+    {
+      const unsigned next = vertices.size ();
+      assert (next == meshVertexInd.size ());
+
+      vertices.push_back (Vertex(coord(ind, 0), coord(ind, 1)));
+      meshVertexInd.push_back (ind + 1);
+
+      innerVertexInd[ind] = next + 1;
+    }
+
+  assert (innerVertexInd[ind] >= 0);
+  return innerVertexInd[ind];
+}
+
+unsigned
+Mesh::getBoundaryVertex (const unsigned ind, const Matrix& coord)
+{
+  if (bdryVertexInd[ind] < 0)
+    {
+      const unsigned next = vertices.size ();
+      assert (next == meshVertexInd.size ());
+
+      vertices.push_back (Vertex(coord(ind, 0), coord(ind, 1)));
+      /* FIXME: Can get rid of cast?  */
+      meshVertexInd.push_back (-static_cast<int> (ind) - 1);
+
+      bdryVertexInd[ind] = next + 1;
+    }
+
+  assert (bdryVertexInd[ind] >= 0);
+  return bdryVertexInd[ind];
+}
+
+void
+Mesh::toStruct (octave_scalar_map& res, octave_scalar_map& vertexMap) const
+{
+  Matrix vert(2, vertices.size ());
+  Matrix tri(3, triangles.size ());
+
+  for (unsigned i = 0; i < vertices.size (); ++i)
+    vertices[i].toMatrix (i, vert);
+  for (unsigned i = 0; i < triangles.size (); ++i)
+    triangles[i].toMatrix (i, tri);
+
+  vertexMap = octave_scalar_map ();
+  vertexMap.assign ("grid", convertToColumnVector (innerVertexInd));
+  vertexMap.assign ("ispt", convertToColumnVector (bdryVertexInd));
+  vertexMap.assign ("mesh", convertToColumnVector (meshVertexInd));
+
+  res = octave_scalar_map ();
+  res.assign ("p", vert);
+  res.assign ("t", tri);
+  /* e is filled out from geom.bedges in the m-file itself.  */
+}
+
+/* ************************************************************************** */
+
+/**
+ * Extract the relevant information for building up the triangles from
+ * the boundary element segments information provided by ls_find_geometry.
+ * It basically just extracts everything of interest and reorders the points.
+ * @param segs Boundary element segment info from ls_find_geometry.
+ * @param bdryPoints Return boundary points here.
+ * @param innerPts Add inner points here.
+ */
+void
+getInnerSegment (const octave_scalar_map& segs,
+                 unsigned bdryPts[2], indexArr& innerPts)
+{
+  bdryPts[0] = segs.contents ("endPt").int_value () - 1;
+  bdryPts[1] = segs.contents ("startPt").int_value () - 1;
+
+  assert (innerPts.empty ());
+  const ColumnVector inners = segs.contents ("inners").column_vector_value ();
+  const unsigned nInners = inners.nelem ();
+  for (unsigned i = 0; i < nInners; ++i)
+    innerPts.push_back (inners(nInners - i - 1) - 1);
+}
+
+/* Octave routine for mesh building.  */
+DEFUN_DLD (__levelset_internal_mesh, args, nargout,
+  "  [MESH, VMAP] = internal_mesh (L, M, INSIDE, COORDS, ISPTCOORDS,\n"
+  "                                NODELIST, INELEMS, OUTELEMS,\n"
+  "                                BDRYELEMS, BDRYELSEGS, GLOBAL)\n\n"
+  "Internal, fast implementation of mesh-building.  Returned are already\n"
+  "the mesh structure (except the 'e' field) and vertex map returned\n"
+  "from ls_build_mesh, but the arguments are already picked apart by the\n"
+  "wrapper routine.\n\n"
+  "L, M are the size of the grid, COORDS give the coordinates of grid points\n"
+  "in space and INSIDE flags which of those points are inside the domain.\n"
+  "ISPTCOORDS are the coordinates of all intersection points indexed by\n"
+  "intersection point number.  GLOBAL signals that we want a global mesh\n"
+  "instead of a list of meshes for only the inner parts.\n\n"
+  "The remaining arguments contain the relevant information from the\n"
+  "geometry struct picked apart.\n")
+{
+  try
+    {
+      if (args.length () != 11 || nargout > 2)
+        throw std::runtime_error ("invalid argument counts");
+
+      /* Get arguments in.  */
+      const unsigned L = args(0).int_value ();
+      const unsigned M = args(1).int_value ();
+      const boolNDArray inside = args(2).bool_array_value ();
+      const Matrix coords = args(3).matrix_value ();
+      const Matrix isptCoords = args(4).matrix_value ();
+      const Matrix nodelist = args(5).matrix_value ();
+      const ColumnVector inElems = args(6).column_vector_value ();
+      const ColumnVector outElems = args(7).column_vector_value ();
+      const ColumnVector bdryElems = args(8).column_vector_value ();
+      const Cell bdryelSegs = args(9).cell_value ();
+      const bool global = args(10).bool_value ();
+
+      /* Check and extract dimensions.  */
+      getDimension (inside, L * M, 1);
+      getDimension (coords, L * M, 2);
+      const unsigned numIntsec = getDimension (isptCoords, -1, 2);
+      getDimension (nodelist, (L - 1) * (M - 1), 4);
+      const unsigned numInElems = getDimension (inElems, -1, 1);
+      const unsigned numOutElems = getDimension (outElems, -1, 1);
+      const unsigned numBdryElems = getDimension (bdryElems, -1, 1);
+
+      /* Construct empty mesh.  */
+      Mesh mesh(L * M, numIntsec);
+
+      /* Construct triangles for inner elements.  */
+      for (unsigned i = 0; i < numInElems; ++i)
+        {
+          const unsigned cur = inElems(i) - 1;
+
+          unsigned vertInds[4];
+          for (unsigned j = 0; j < 4; ++j)
+            vertInds[j] = mesh.getInnerVertex (nodelist(cur, j) - 1, coords);
+
+          mesh.addTriangle (vertInds[0], vertInds[1], vertInds[2]);
+          mesh.addTriangle (vertInds[0], vertInds[2], vertInds[3]);
+        }
+
+      /* In global mode, do that also for outer elements.  */
+      if (global)
+        for (unsigned i = 0; i < numOutElems; ++i)
+          {
+            const unsigned cur = outElems(i) - 1;
+
+            unsigned vertInds[4];
+            for (unsigned j = 0; j < 4; ++j)
+              vertInds[j] = mesh.getInnerVertex (nodelist(cur, j) - 1, coords);
+
+            mesh.addTriangle (vertInds[0], vertInds[1], vertInds[2]);
+            mesh.addTriangle (vertInds[0], vertInds[2], vertInds[3]);
+          }
+
+      /* Go over boundary elements and handle them.  We use the information
+         provided already by ls_find_geometry internally about the segments
+         of each boundary element that belong to the domain.  These need
+         to be split into triangles.  */
+      for (unsigned i = 0; i < numBdryElems; ++i)
+        {
+          const unsigned cur = bdryElems(i) - 1;
+          const Cell cellSegs = bdryelSegs(i).cell_value ();
+          const unsigned nSegs = cellSegs.nelem ();
+
+          std::vector<octave_scalar_map> segs;
+          indexArr endEdges;
+          for (unsigned j = 0; j < nSegs; ++j)
+            {
+              segs.push_back (cellSegs(j).scalar_map_value ());
+              int edge = segs.back ().contents ("endEdge").int_value () - 1;
+              endEdges.push_back (edge);
+            }
+
+          /* Special case is that of *two* starting points, which
+             indicates that we have a narrow pair.  */
+          if (segs.size () == 2)
+            {
+              assert ((endEdges[0] + 2) % 4 == endEdges[1]);
+
+              unsigned bdryPts[4];
+              indexArr innerPts[2];
+              for (unsigned j = 0; j < 2; ++j)
+                {
+                  getInnerSegment (segs[j], bdryPts + 2 * j, innerPts[j]);
+                  assert (innerPts[j].size () == 1);
+
+                  for (unsigned k = 0; k < 2; ++k)
+                    {
+                      const unsigned ind = 2 * j + k;
+                      bdryPts[ind] = mesh.getBoundaryVertex (bdryPts[ind],
+                                                             isptCoords);
+                    }
+                  for (auto& p : innerPts[j])
+                    p = mesh.getInnerVertex (p, coords);
+
+                  mesh.addTriangle (bdryPts[2 * j], innerPts[j][0],
+                                    bdryPts[2 * j + 1]);
+                }
+
+              if (global)
+                {
+                  int outerPts[] = {endEdges[0], endEdges[1]};
+                  for (auto& p : outerPts)
+                    {
+                      p = nodelist(cur, p) - 1;
+                      assert (!inside (p));
+                      p = mesh.getInnerVertex (p, coords);
+                    }
+
+                  mesh.addTriangle (bdryPts[1], outerPts[1], bdryPts[2]);
+                  mesh.addTriangle (bdryPts[0], bdryPts[1], bdryPts[2]);
+                  mesh.addTriangle (bdryPts[0], bdryPts[2], bdryPts[3]);
+                  mesh.addTriangle (bdryPts[0], bdryPts[3], outerPts[0]);
+                }
+            }
+
+          /* "Ordinary" case.  */
+          else
+            {
+              assert (segs.size () == 1);
+
+              /* Find whole list of points forming the segment.  */
+              unsigned bdryPts[2];
+              indexArr innerPts;
+              getInnerSegment (segs[0], bdryPts, innerPts);
+              assert (!innerPts.empty ());
+
+              indexArr outerPts;
+              for (unsigned p = endEdges[0] % 4; ; p = (p + 3) % 4)
+                {
+                  const unsigned pInd = nodelist (cur, p) - 1;
+                  if (static_cast<int> (pInd) == innerPts.back ())
+                    break;
+                  assert (!inside (pInd));
+                  outerPts.push_back (pInd);
+                }
+              assert (innerPts.size () + outerPts.size () == 4);
+
+              /* Add to list of vertices in the mesh and overwrite the
+                 indices on the way.  */
+              for (auto& p : bdryPts)
+                p = mesh.getBoundaryVertex (p, isptCoords);
+              for (auto& p : innerPts)
+                p = mesh.getInnerVertex (p, coords);
+              if (global)
+                for (auto& p : outerPts)
+                  p = mesh.getInnerVertex (p, coords);
+
+              /* Build the triangles accordingly and also the list of
+                 boundary edges in this case.  */
+              switch (innerPts.size ())
+                {
+                  case 1:
+                    mesh.addTriangle (bdryPts[0], innerPts[0], bdryPts[1]);
+                    if (global)
+                      {
+                        mesh.addTriangle (bdryPts[0], outerPts[1], outerPts[0]);
+                        mesh.addTriangle (bdryPts[0], bdryPts[1], outerPts[1]);
+                        mesh.addTriangle (bdryPts[1], outerPts[2], outerPts[1]);
+                      }
+                    break;
+                  case 2:
+                    mesh.addTriangle (bdryPts[0], innerPts[0], innerPts[1]);
+                    mesh.addTriangle (innerPts[1], bdryPts[1], bdryPts[0]);
+                    if (global)
+                      {
+                        mesh.addTriangle (bdryPts[1], outerPts[1], outerPts[0]);
+                        mesh.addTriangle (outerPts[0], bdryPts[0], bdryPts[1]);
+                      }
+                    break;
+                  case 3:
+                    mesh.addTriangle (bdryPts[0], innerPts[0], innerPts[1]);
+                    mesh.addTriangle (innerPts[1], bdryPts[1], bdryPts[0]);
+                    mesh.addTriangle (innerPts[1], innerPts[2], bdryPts[1]);
+                    if (global)
+                      mesh.addTriangle (bdryPts[1], outerPts[0], bdryPts[0]);
+                    break;
+                  default:
+                    assert (false);
+                }
+            }
+        }
+
+      /* Build return results.  */
+      octave_scalar_map msh, vertexMap;
+      mesh.toStruct (msh, vertexMap);
+      octave_value_list res;
+      res(0) = msh;
+      res(1) = vertexMap;
+
+      return res;
+    }
+  catch (const std::runtime_error& exc)
+    {
+      error (exc.what ());
+      return octave_value_list ();
+    }
+}
diff --git a/src/marching.cpp b/src/marching.cpp
new file mode 100644
index 0000000..b59c0e6
--- /dev/null
+++ b/src/marching.cpp
@@ -0,0 +1,103 @@
+/*
+    GNU Octave level-set package.
+    Copyright (C) 2013-2015  Daniel Kraft <d at domob.eu>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Test program for fast marching.  */
+
+#include "FastMarching.hpp"
+
+#include <cmath>
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+
+using namespace fastMarching;
+
+namespace fastMarching
+{
+
+/* Provide printf-based implementation of issueWarning.  */
+void
+issueWarning (const std::string&, const std::string& fmt, ...)
+{
+  va_list args;
+  va_start (args, fmt);
+  vprintf (fmt.c_str (), args);
+  va_end (args);
+}
+
+} // namespace fastMarching
+
+/**
+ * Main routine.
+ * @return Exit code 0.
+ */
+int
+main ()
+{
+  static const unsigned N = 100;
+
+  IndexTuple size(2);
+  size[0] = N;
+  size[1] = N;
+
+  Grid grid(size);
+  for (unsigned i = 0; i < N; ++i)
+    for (unsigned j = 0; j < N; ++j)
+      {
+        size[0] = i;
+        size[1] = j;
+
+        if (i == N/2 && j == N/2)
+          grid.setInitial (Entry::newAlive (size, 0.0, 0.0));
+        else
+          {
+            realT f = 0.0;
+            f += std::pow (static_cast<double> (i) - N/2, 2);
+            f += std::pow (static_cast<double> (j) - N/2, 2);
+            f = 1.0 - std::sqrt (f) / N * std::sqrt (1.8);
+            f = 1.0 / f;
+            grid.setInitial (Entry::newFarAway (size, f));
+          }
+      }
+
+  grid.perform ();
+
+  printf ("data = [ ...\n"); 
+  for (unsigned i = 0; i < N; ++i)
+    {
+      for (unsigned j = 0; j < N; ++j)
+        {
+          size[0] = i;
+          size[1] = j;
+          const Grid& constGrid(grid);
+          const Entry& cur = *constGrid.get (size);
+
+          printf (" %f", cur.getDistance ());
+          if (j + 1 < N)
+            printf (",");
+        }
+      
+      if (i + 1 < N)
+        printf ("; ...\n");
+      else
+        printf (" ...\n");
+    }
+  printf ("];\n");
+
+  return EXIT_SUCCESS;
+}
diff --git a/src/nbFromGeom.cpp b/src/nbFromGeom.cpp
new file mode 100644
index 0000000..ad28275
--- /dev/null
+++ b/src/nbFromGeom.cpp
@@ -0,0 +1,166 @@
+/*
+    GNU Octave level-set package.
+    Copyright (C) 2014  Daniel Kraft <d at domob.eu>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Internal routine for ls_nb_from_geom.  */
+
+#include "Utils.hpp"
+
+#include <octave/oct.h>
+#include <octave/lo-ieee.h>
+#include <octave/oct-norm.h>
+
+#include <cassert>
+#include <stdexcept>
+
+/* Tolerance for considering distances equal when averaging over multiple
+   g values.  */
+static const double avgTol = 1.0e-9;
+
+/* C++ implementation.  */
+DEFUN_DLD (__levelset_nbFromGeom, args, nargout,
+  "  [D, GSUM, GCNT] = nbFromGeom (BEDGES, PHI, HASG, G0,\n"
+  "                                NODELIST, BDRYINDEX, ONEDGE,\n"
+  "                                NODECOORD, ISPTCOORD)\n\n"
+  "Internal routine to calculate the narrow-band distances from the geometry\n"
+  "information in the respective arguments (corresponding to ls_find_geometry\n"
+  "struct fields).\n")
+{
+  try
+    {
+      if (args.length () != 9 || nargout > 3)
+        throw std::runtime_error ("invalid argument counts");
+
+      /* Get arguments.  */
+      const Matrix bedges = args(0).matrix_value ();
+      const Matrix phi = args(1).matrix_value ();
+      const bool hasG = args(2).bool_value ();
+      const ColumnVector g0 = args(3).column_vector_value ();
+      const Matrix nodelist = args(4).matrix_value ();
+      const ColumnVector bdryIndex = args(5).column_vector_value ();
+      const Matrix onEdge = args(6).matrix_value ();
+      const Matrix nodeCoord = args(7).matrix_value ();
+      const Matrix isptCoord = args(8).matrix_value ();
+
+      /* Extract the dimensions.  */
+
+      const unsigned nEdges = getDimension (bedges, -1, 2);
+      if (hasG)
+        getDimension (g0, nEdges, 1);
+
+      const dim_vector dims = phi.dims ();
+      const unsigned L = dims(0);
+      const unsigned M = dims(1);
+      const unsigned nNodes = L * M;
+      const unsigned nElems = (L - 1) * (M - 1);
+
+      getDimension (nodelist, nElems, 4);
+      const unsigned nBdryEl = getDimension (bdryIndex, -1, 1);
+      const unsigned nIspts = getDimension (onEdge, -1, 2);
+      getDimension (nodeCoord, nNodes, 2);
+      getDimension (isptCoord, nIspts, 2);
+
+      /* Loop over each boundary segment and perform the update to d.  */
+
+      Matrix d(L, M), gSum(L, M), gCnt(L, M);
+      d.fill (octave_NA);
+      if (hasG)
+        {
+          gSum.fill (octave_NA);
+          gCnt.fill (octave_NA);
+        }
+
+      for (unsigned i = 0; i < nEdges; ++i)
+        {
+          const unsigned indA = bedges(i, 0) - 1;
+          const unsigned indB = bedges(i, 1) - 1;
+          const unsigned bdryEl = onEdge(indA, 0) - 1;
+          assert (bdryEl == onEdge(indB, 1) - 1);
+          assert (bdryEl < nBdryEl);
+          const unsigned elem = bdryIndex(bdryEl) - 1;
+
+#define EXTRACT_VERTEX(varname, coordlist, ind) \
+  ColumnVector varname(2); \
+  varname(0) = coordlist((ind), 0); \
+  varname(1) = coordlist((ind), 1);
+
+          /* Get information about the line segment.  */
+
+          EXTRACT_VERTEX (a, isptCoord, indA)
+          EXTRACT_VERTEX (b, isptCoord, indB)
+
+          ColumnVector dir = b - a;
+          const double len = xnorm (dir);
+          dir /= len;
+
+          /* Try to update all boundary element nodes.  */
+          for (unsigned j = 0; j < 4; ++j)
+            {
+              const unsigned node = nodelist(elem, j) - 1;
+              assert (node < nNodes);
+
+              EXTRACT_VERTEX (x, nodeCoord, node)
+
+              /* Project onto the side.  If the projection is negative,
+                 x lies "beyond" the edge on the side of a.  In this case,
+                 its distance is the distance to a.  The same is true (with b)
+                 if the projection is too large.  Otherwise, we
+                 use the distance to the line spanned by a and b as usual.  */
+              const double proj = dir.transpose () * (x - a);
+              double cur;
+              if (proj < 0.0)
+                cur = xnorm (x - a);
+              else if (proj > len)
+                cur = xnorm (x - b);
+              else
+                cur = xnorm (x - (a + proj * dir));
+
+              if (hasG)
+                {
+                  if (lo_ieee_is_NA (d(node)) || d(node) > cur + avgTol)
+                    {
+                      gSum(node) = g0(i);
+                      gCnt(node) = 1;
+                    }
+                  else if (std::abs (d(node) - cur) < avgTol)
+                    {
+                      gSum(node) += g0(i);
+                      ++gCnt(node);
+                    }
+                }
+
+              if (lo_ieee_is_NA (d(node)) || d(node) > cur)
+                d(node) = cur;
+            }
+
+#undef EXTRACT_VERTEX
+        }
+
+      /* Return.  */
+      octave_value_list res;
+      res(0) = d;
+      res(1) = gSum;
+      res(2) = gCnt;
+
+      return res;
+    }
+  catch (const std::runtime_error& exc)
+    {
+      error (exc.what ());
+      return octave_value_list ();
+    }
+}
diff --git a/src/upwindGrad.cpp b/src/upwindGrad.cpp
new file mode 100644
index 0000000..7418dc0
--- /dev/null
+++ b/src/upwindGrad.cpp
@@ -0,0 +1,152 @@
+/*
+    GNU Octave level-set package.
+    Copyright (C) 2015  Daniel Kraft <d at domob.eu>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Implement an approximation to |grad phi| using an appropriate
+   upwind scheme.  The scheme implemented is basically (6.17)
+   from Sethian's book "Level Set Methods and Fast Marching Methods",
+   Cambridge University Press, second edition, 1999.
+
+   The discretisation could probably be discretised also in Octave code
+   with proper vector operations, but this would to harder-to-read code.
+   The direct, point-wise C++ formulation is closer to the formulas
+   that are derived in the book.  */
+
+#include <octave/oct.h>
+#include <octave/lo-ieee.h>
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <stdexcept>
+
+/** Type used to index n-dimensional Octave arrays.  */
+typedef Array<octave_idx_type> GridIndex;
+/** Type used to denote number of dimensions.  */
+typedef size_t dimensionT;
+
+/**
+ * Calculate the gradient norm in a given grid point.
+ * @param idx The current point's index.
+ * @param phi The function whose gradient is requested.
+ * @param F The sign gives the upwind direction.
+ * @param size The total domain size.
+ * @param D The number of dimensions.
+ * @return The approximate gradient norm in the point, not yet divided by h.
+ */
+static double
+gradientNormInPoint (const GridIndex& idx, const NDArray& phi, double F,
+                     const dim_vector& size, dimensionT D)
+{
+  double res = 0.0;
+  for (dimensionT d = 0; d < D; ++d)
+    {
+      GridIndex neighbour(idx);
+      double forward = 0.0;
+      double backward = 0.0;
+
+      if (idx(d) > 0)
+        {
+          neighbour(d) = idx(d) - 1;
+          backward = phi(idx) - phi(neighbour);
+        }
+      if (idx(d) + 1 < size(d))
+        {
+          neighbour(d) = idx(d) + 1;
+          forward = phi(neighbour) - phi(idx);
+        }
+
+      if (F < 0.0)
+        std::swap (forward, backward);
+
+      if (backward > 0.0)
+        res += std::pow (backward, 2);
+      if (forward < 0.0)
+        res += std::pow (forward, 2);
+    }
+
+  return std::sqrt (res);
+}
+
+/**
+ * Recursively (since the number of dimensions and thus required loops
+ * is not known) iterate the whole array, calling gradientNormInPoint
+ * at each grid point.
+ * @param idx The current index.
+ * @param depth The current depth in the recursion.
+ * @param phi The function whose gradient is requested.
+ * @param F The sign defines the upwind direction.
+ * @param size Size of the array.
+ * @param D Number of dimensions.
+ * @param grad Return the gradient norm here.
+ */
+static void
+iterate (GridIndex& idx, dimensionT depth, const NDArray& phi, const NDArray& F,
+         const dim_vector& size, dimensionT D, NDArray& grad)
+{
+  if (depth == D)
+    {
+      grad(idx) = gradientNormInPoint (idx, phi, F(idx), size, D);
+      return;
+    }
+
+  for (idx(depth) = 0; idx(depth) < size(depth); ++idx(depth))
+    iterate (idx, depth + 1, phi, F, size, D, grad);
+}
+
+/* Octave routine interfacing to fast marching code.  */
+DEFUN_DLD (__levelset_upwindGrad, args, nargout,
+  "GNORM = upwindGrad (PHI, F, H)\n\n"
+  "Internal routine to calculate the upwind-discretised gradient norm\n"
+  "of PHI.  F is the speed field, and only the sign of F is used to decide\n"
+  "about the upwind direction.  H is the grid size as usual.\n\n"
+  "Returned is an array GNORM of the same size as PHI, with |grad phi| in\n"
+  "the entries.\n")
+{
+  try
+    {
+      if (args.length () != 3 || nargout != 1)
+        throw std::invalid_argument ("invalid argument counts");
+
+      const NDArray phi = args(0).array_value ();
+      const NDArray F = args(1).array_value ();
+      const double h = args(2).double_value ();
+
+      const dim_vector size = phi.dims ();
+      const dimensionT D = size.length ();
+
+      if (size != F.dims ())
+        throw std::invalid_argument ("argument dimensions mismatch");
+
+      NDArray grad(size);
+      grad.fill (octave_NA);
+
+      GridIndex idx(dim_vector (D, 1));
+      iterate (idx, 0, phi, F, size, D, grad);
+      grad /= h;
+
+      octave_value_list res;
+      res(0) = grad;
+
+      return res;
+    }
+  catch (const std::exception& exc)
+    {
+      error (exc.what ());
+      return octave_value_list ();
+    }
+}

-- 
Alioth's /home/groups/pkg-octave/bin/git-commit-notice on /srv/git.debian.org/git/pkg-octave/octave-level-set.git



More information about the Pkg-octave-commit mailing list